-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tag Processor: Add bookmark invalidation logic #47559
Conversation
Flaky tests detected in 83f274c. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4231726230
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this something you consider necessary for 6.1?
is this necessary at all? It appears to change the behavior of bookmarks where if we seek to a bookmark we previously set, but that bookmark is gone, instead of returning false
, with WP_DEBUG
set it will crash saying "you did it wrong."
is the safety we're after something that belongs in seek
instead so that we can still distinguish between "failed to seek to the bookmark you previously set" and "you tried to seek to a non-existent bookmark"? that is, "something failed because of normal situations" vs. "you tried to do something that is guaranteed to fail and should never be done"
You mean 6.2 🙂 No, this isn't needed for the pending WP release.
I kinda think it is 🤔 Since a bookmark is just a Take this unit test for example. We start with the following HTML, and set a bookmark to the <div>outside</div><section><div><img>inside</div></section>
^ ^ Now we replace the contents between the third and fourth <div>outside</div><section><div>This is the new section content.</section>
^ ^ The bookmark isn't even pointing to a tag anymore. I don't think we want the user to proceed assuming that all is well with that bookmark 😬 It's easy enough for us to add logic (like in this PR) to determine which bookmarks (if any) have been invalidated; it seems like it'd be quite cumbersome for the user to do so (without access to class internals).
Yeah, that's a fair point.
I could see that 🤔 The downside is that we'll need to keep the bookmark around -- probably setting it to Since the user would have to check the return value of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great @ockham 🚢
Is the safety we're after something that belongs in seek instead
I'm not sure if we need to distinguish between these two situations. If the bookmark is gone, it's gone – the entire code branch relying upon that bookmark will no longer work and needs to perform careful checks. I'm not sure how to exactly use the ability to distinguish between the two failure modes.
|
Thank you for reviewing and approving, Adam! (Apologies for not replying any sooner -- I was AFK from Friday until yesterday!)
No, IMO, that should be all that's needed! 😄 Does that work for you @dmsnell? |
6895965
to
b334e23
Compare
One thing we need to be mindful of here is that this patch will change the code of the tag processor with regard to what we merged into Core 🤔 While the user is never supposed to encounter any practical consequences from this (since it's currently impossible to make any changes that would actually invalidate bookmakrs), we might want to retain the code in the 6.2 compat layer identical to what is in Core. So maybe this should go into a different file and/or folder 😬 |
b334e23
to
ce6c644
Compare
ce6c644
to
6d223e5
Compare
Rebased on #47933 and modified to add the bookmark invalidation logic to the 6.3 compat layer version of this class. |
dc3e4ce
to
50f4305
Compare
I've added |
lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php
Outdated
Show resolved
Hide resolved
$p->set_bookmark( 'my-bookmark' ); | ||
$p->release_bookmark( 'my-bookmark' ); | ||
$this->assertFalse( $p->has_bookmark( 'my-bookmark' ) ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not we can verify already in a test if a bookmark exists or not, because $p->seek( 'place' )
will return false
if it's unavailable.
$this->assertFalse( $p->seek( 'nonexistent' ) );
$this->assertTrue( $p->seek( 'existent' ) );
// wipe out bookmark with some HTML modifications
$this->assertFalse( $p->seek( 'existent' ) );
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting to add a separate test to cover that behavior of seek
? (Happy to do so 😊 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I missed the // wipe out bookmark with some HTML modifications
part. We don't have any API to do that yet though, do we? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you suggesting to add a separate test to cover that behavior of seek?
No; that's a valid thing to talk about, but I was only responding to what I now realize could be a misreading? I thought you had said that it wasn't possible to test before this patch if we have a bookmark or not, and I was suggesting that we do, it's just a method that also impacts the processor state.
We don't have any API to do that yet though, do we? 🤔
That's correct. It's intentionally possible to mess this up though, hopefully in quiet dark corners with blatant warnings to look away. So yes, we could actually write some tests of this behavior before it's necessary, for the purpose of writing tests.
A simpler alternative is simply to assert that a seek to a nonexistent bookmark doesn't exist. I was trying to provide more context in that snippet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No; that's a valid thing to talk about, but I was only responding to what I now realize could be a misreading? I thought you had said that it wasn't possible to test before this patch if we have a bookmark or not, and I was suggesting that we do, it's just a method that also impacts the processor state.
Right, thanks for clarifying!
FWIW, part of the reason for adding has_bookmark
was to give the user a way to find out without triggering a PHP notice when WP_DEBUG
is set (as you'd pointed out).
I'll add a test for seek( 'nonexistent' )
though!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a test for
seek( 'nonexistent' )
though!
Hmm, this is a bit more involved. Since the PHP notice is being thrown in unit tests, I need the test to "expect" it.
I just haven't found the right exception type yet 🤔
$this->expectException( 'PHPUnit_Framework_Error_Notice' ); // Nope, not that one.
Looks very good, and thanks for your patience. I left some questions about the naming, which at first seemed helpful, and then confusing. Since this is targeting the compat PR can we make sure not to merge it until that one merges? I would rather avoid mixing this preventative fix with that code shuffle. |
4204f21
to
f960d1e
Compare
lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php
Outdated
Show resolved
Hide resolved
lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php
Show resolved
Hide resolved
lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php
Outdated
Show resolved
Hide resolved
Co-authored-by: Dennis Snell <[email protected]>
6d8d626
to
83f274c
Compare
Do we have a WordPress Trac ticket we could reference in the PR? I think it will become an official recommendation soon for PHP changes that we know require backporting to WordPress core. It would also allow us to include |
$update_tail = $bookmark->end >= $diff->start; | ||
|
||
if ( ! $update_head && ! $update_tail ) { | ||
if ( $bookmark->start < $diff->start && $bookmark->end < $diff->start ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think this would read better as:
if ( $bookmark->start < $diff->start && $bookmark->end < $diff->start ) { | |
if ( $bookmark->end < $diff->start ) { |
If $bookmark->start
is ever after $bookmark->end
, I'd rather make it fail in some way rather than try to salvage it with a defensive condition.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While we did inline $update_head
and $update_tail
here, we didn't otherwise change the condition -- it existed like that prior to this PR. For that reason, I'd like to keep it as is 😬 I know it shouldn't make a difference outside of any pathological cases, but since this PR has been sitting for a while, I'd like to de-risk it as much as possible.
Aside: I kinda like that it's symmetrical to the condition right below:
if ( $bookmark->start >= $diff->start && $bookmark->end < $diff->end ) {
I feel it helps me visualize better that the bookmark is either fully past the diff, or enclosed by it.
I'm open to changing this in a follow-up though 😊
lib/compat/wordpress-6.3/html-api/class-gutenberg-html-tag-processor-6-3.php
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a nit but it looks great overall. I like how simple it turned!
We don't have a Trac ticket for this specific change. However, since the general plan is to move development of the HTML (Tag) Processor to That ticket might be a bit broader in scope than just this PR, since there's strictly speaking no need for bookmark invalidation as long as there isn't any API in
That's a good point; but maybe we can do that after the fact? 😊 Especially since the idea is to port changes from |
Yes, one Trac ticket to use as a reference for all work planned in WordPress 6.3 cycle would be perfect. It can cover non-attribute lexical changes for now, but we can extend the scope if that makes sense later.
I'm not sure I understand the reasoning here. If the ticket blocks merging the PR now, then definitely ignore my comment. We will have to include that annotation in WordPress core anyway, but it's the least important thing today. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is definitely a good example of why I think developing in Core is a good idea. can we prioritize getting this merged into Core and then remove this code ASAP. right now we have introduced divergent codebases and will need to continue to resolve discrepancies introduced on both sides (something we already did once during the merge of #47933)
that is, remove the unit tests by merging them into Core's suite, then the compat file probably won't need updating (or maybe it must since we'll need to reference a Trac ticket), but may anyway, depending on whether the class in Core received updates as well.
thanks for the work on this.
I'll file a PR against |
|
While `WP_HTML_Tag_Processor` currently only supports changing a given tag's attributes, the plan is to provide methods to make broader changes (possibly through a subclass of `WP_HTML_Tag_Processor`). The API will have the potential of replacing a tag that a bookmark points to. To prepare, this changeset makes sure that all bookmarks affected by a HTML replacement are invalidated (i.e. released). Changes: * Extends the existing loop in `WP_HTML_Tag_Processor::apply_attributes_updates()` that adjusts bookmarks' start and end positions upon HTML changes to check if the entire bookmark is within a portion of the HTML that has been replaced. * Adds `WP_HTML_Tag_Processor::has_bookmark() to check whether the given bookmark name exists. References: * [WordPress/gutenberg#47559 Gutenberg PR 47559] * [https://github.com/WordPress/gutenberg/releases/tag/v15.3.0 Released in Gutenberg 15.3.0] Follow-up to [55203]. Props bernhard-reiter, dmsnell, zieladam. Fixes #57788. git-svn-id: https://develop.svn.wordpress.org/trunk@55555 602fd350-edb4-49c9-b593-d223f7449a82
While `WP_HTML_Tag_Processor` currently only supports changing a given tag's attributes, the plan is to provide methods to make broader changes (possibly through a subclass of `WP_HTML_Tag_Processor`). The API will have the potential of replacing a tag that a bookmark points to. To prepare, this changeset makes sure that all bookmarks affected by a HTML replacement are invalidated (i.e. released). Changes: * Extends the existing loop in `WP_HTML_Tag_Processor::apply_attributes_updates()` that adjusts bookmarks' start and end positions upon HTML changes to check if the entire bookmark is within a portion of the HTML that has been replaced. * Adds `WP_HTML_Tag_Processor::has_bookmark() to check whether the given bookmark name exists. References: * [WordPress/gutenberg#47559 Gutenberg PR 47559] * [https://github.com/WordPress/gutenberg/releases/tag/v15.3.0 Released in Gutenberg 15.3.0] Follow-up to [55203]. Props bernhard-reiter, dmsnell, zieladam. Fixes #57788. Built from https://develop.svn.wordpress.org/trunk@55555 git-svn-id: http://core.svn.wordpress.org/trunk@55067 1a063a9b-81f0-0310-95a4-ce76da25c4cd
While `WP_HTML_Tag_Processor` currently only supports changing a given tag's attributes, the plan is to provide methods to make broader changes (possibly through a subclass of `WP_HTML_Tag_Processor`). The API will have the potential of replacing a tag that a bookmark points to. To prepare, this changeset makes sure that all bookmarks affected by a HTML replacement are invalidated (i.e. released). Changes: * Extends the existing loop in `WP_HTML_Tag_Processor::apply_attributes_updates()` that adjusts bookmarks' start and end positions upon HTML changes to check if the entire bookmark is within a portion of the HTML that has been replaced. * Adds `WP_HTML_Tag_Processor::has_bookmark() to check whether the given bookmark name exists. References: * [WordPress/gutenberg#47559 Gutenberg PR 47559] * [https://github.com/WordPress/gutenberg/releases/tag/v15.3.0 Released in Gutenberg 15.3.0] Follow-up to [55203]. Props bernhard-reiter, dmsnell, zieladam. Fixes #57788. Built from https://develop.svn.wordpress.org/trunk@55555 git-svn-id: https://core.svn.wordpress.org/trunk@55067 1a063a9b-81f0-0310-95a4-ce76da25c4cd
What?
Part of #44410.
Invalidate bookmarks in a
WP_HTML_Tag_Processor
instance if the tags that they're pointing to have been replaced.Why?
While
WP_HTML_Tag_Processor
currently only supports changing a given tag's attributes, we plan to also provide methods to make broader changes (e.g.set_content_inside_balanced_tags
) (probably through a subclass ofWP_HTML_Tag_Processor
).An API like that will have the potential of replacing a tag that a bookmark points to. As a preparation, we thus need to make sure that all bookmarks affected by a HTML replacement are invalidated (i.e. released).
How?
By extending the existing loop in
apply_attributes_updates
that adjusts bookmarks' start and end positions upon HTML changes to check if the entire bookmark is within a portion of the HTML that has been replaced.Note that this PR is extracted from #47068, per this suggestion.
Testing Instructions
As stated above, there aren't currently any methods that actually replace existing bookmarks, so the relevant codepath should never be hit. Furthermore,
apply_attributes_updates
is aprivate
method, so we cannot directly test it.However, we have extensive unit test coverage of
WP_HTML_Tag_Processor
's public methods which guarantee that their desired behavior is still warranted.Finally, #47036 contains the code from this PR, and has unit test coverage for bookmark invalidation.