Conversation
|
dmsnell to the rescue! I think allowing nested blocks from the parser will be a good step forward! Awesome work so far! Slightly off topic, but definitely tied to this, although they are more next step questions: How do you think we will handle block normalization, or if we even want to do normalization? How do we parse this correctly?: {
"blockName": "core/inside",
"attrs": null,
"innerBlocks": [
{
"blockName": "core/mark",
"attrs": null,
"innerBlocks": [],
"rawContent": "jump here"
}
],
"rawContent": "\nAnother in the list\n"
}"jump here" is supposed to live in between the words "the" and "list", how do we denote that location in our block data? Do we not allow other blocks within text to avoid the need for cursor data or do we need to figure out a data model that can accommodate nested blocks better, or do we create some sort of template syntax to abstract our render tree? I don't know if this is already a solved problem, but I think the above example, in my opinion, shows that we really want to create a data spec for blocks first, and then build our API around that. Maybe with the news of changing view libraries, this could be a good time to reflect on the current data model for blocks state. |
There are two aspects involved in determining how a block is rendered. The parsing step handled by the grammar breaks up the ingredients, but doesn't know how they are combined—that is determined by the block |
|
thanks for jumping in @BE-Webdesign (sorry I should have added you to this one).
much obliged 🙇 for starters I can say sorry for pushing out a PR without much description, explanation of motivation, or context. I've been talking in person with @mtias and some others and wanted to make sure that I could record in code some of the ideas we've spoken about. will add more of the written part later as we continue to think through the implications here.
mostly I'll just defer to the response that @mtias gave, but we had lots of discussion on this very point. this PR is the realization of the ideas we landed at. |
That sounds like a cool approach matias. What I am trying to figure out is how our block state will give those components enough information to render in the correct order. Maybe parsing each text node as a block would be a good solution? |
No need to apologize. I don't expect to be added to anything. I am only a casual contributor. Handling the parsing/serialization and inbetween for nested blocks will be a challenge, and hopefully I can constructively aid that discussion and path forward. |
bcb4c07 to
8a47e27
Compare
|
Thanks @belcherj - was from the work @aduth did with the
Best to defer until this merges - the
Thanks! when I recreated the fixtures before they didn't all pass the tests and I didn't have the chance to dig in to find out why. Are they passing tests in your branch? |
21c08a3 to
395bb69
Compare
|
Thanks @belcherj! (oops look like tests are still failing) Is anyone opposed to merging this PR? The ability to nest blocks doesn't force blocks to nest and as-is, this parser should be compliant with all existing posts. Would anyone be interested in helping out by testing this against several different existing posts? |
|
@dmsnell still a couple tests failing. Could not quickly determine how to fix. |
blocks/api/parser.js
Outdated
| export function parseWithGrammar( content ) { | ||
| return grammarParse( content ).reduce( ( memo, blockNode ) => { | ||
| const { blockName, rawContent, attrs } = blockNode; | ||
| // eslint-disable-next-line no-unused-vars |
There was a problem hiding this comment.
If we're truly not using innerBlocks, what's the intent with disabling the rule?
Noting your comment at #2743 (comment) , I could go either way on whether it's sensible to leave as a placeholder. It should be fine.
blocks/api/parser.js
Outdated
| return grammarParse( content ).reduce( ( memo, blockNode ) => { | ||
| const { blockName, rawContent, attrs } = blockNode; | ||
| // eslint-disable-next-line no-unused-vars | ||
| const { blockName, innerBlocks, innerHtml: rawContent, attrs } = blockNode; |
There was a problem hiding this comment.
Should we replace all references to rawContent as innerHTML ? Here or in a separate pull changeset?
There was a problem hiding this comment.
or now I thought a second PR would be good so that this can be introduced in as minimal of a way as possible - I'm open to input
blocks/api/post.pegjs
Outdated
| / WP_Block_Html | ||
| return [ | ||
| freeform( pre ), | ||
| ...ts.reduce( ( out, [ t, h ] ) => [ ...out, t, freeform( h ) ], [] ), |
There was a problem hiding this comment.
I recall we had issues previously because we don't run Babel on the .pegjs JavaScript, so I have a feeling this spread syntax won't work in IE11 and we'd either need to port to ES5 equivalent or investigate transpiling the result of the file.
blocks/api/post.pegjs
Outdated
| ts:(t:Token html:$((!Token .)*) { /** <?php return $t; ?> **/ return [ t, html ] })* | ||
| post:$(.*) | ||
| { /** <?php | ||
| $blocks = []; |
There was a problem hiding this comment.
The square bracket array syntax was added in PHP 5.4. We need to use the more-verbose array() for support back to PHP 5.2.4
blocks/api/post.pegjs
Outdated
| function freeform( s ) { | ||
| return s.length && { | ||
| blockName: 'core/freeform', | ||
| innerHtml: s |
There was a problem hiding this comment.
We might want to consider the capitalization here. I created #2511 a while back to open discussion on a convention. I've started leaning more toward capitalizing abbreviations (innerHTML) but really just care to enforce some consistency.
There was a problem hiding this comment.
personally I don't care. I'll use HTML
blocks/api/post.pegjs
Outdated
| = s:Block_Start children:(Token / $(!Block_End .))+ e:Block_End | ||
| { | ||
| /** <?php | ||
| $innerBlocks = array_filter( $children, function( $a ) { |
There was a problem hiding this comment.
Anonymous functions are only available in PHP 5.3+. To support PHP 5.2.4+, we'd need this to be a named function, passing the string callable reference to array_filter.
blocks/api/post.pegjs
Outdated
| } ); | ||
|
|
||
| $innerHtml = array_filter( $children, function( $a ) { | ||
| return is_string( $a ); |
There was a problem hiding this comment.
Or here we could probably do array_filter( $children, 'is_string' );
There was a problem hiding this comment.
good catch. I actually intended on maybe creating an array_partition function and maybe will still do that. I always get confused which core functions are functions and which are operators (like empty()) and cannot be used as Callables
42e3c23 to
4e41437
Compare
|
All, here are some big updates since the last time I addressed this: more complete PHP support, some big comments, and a few functions extracting out specific logic for return value shapes. I've been inspired by some alternative parser generators in how they separate actions on a rule from the grammar itself, allowing the host language to define them separately. For example, Canopy lets you call these actions on a rule by adding something like const actions = {
combine_blocks: function( pre, tokens, post ) {
…
}
}Ohm also has this notion and they consider it separating the syntax from the semantics of the language being parsed. Anyway, something to think about as we start introducing more code. I'd love to have a grammar completely free from any code. |
460e57f to
74a2fcb
Compare
aduth
left a comment
There was a problem hiding this comment.
Looks like fixtures need to be regenerated per innerHtml -> innerHTML change:
npm run fixtures:regenerate
Resolves some, but not all PHPUnit errors.
It looks like we no longer guarantee that attrs will exist on the returned parsed block object, so there are PHP errors occurring here where we assume to be able to access the property:
Line 77 in 6e984b0
1) Dynamic_Blocks_Render_Test::test_dynamic_block_rendering
Undefined index: attrs
blocks/api/post.pegjs
Outdated
|
|
||
| function freeform( s ) { | ||
| return s.length && { | ||
| blockName: 'core/freeform', |
There was a problem hiding this comment.
Elsewhere we have made assumptions that freeform is the absence of a block name. Specifically, this test case now fails for a few reasons:
- In block registration we allow unknown type handler to be assigned to any block name, not assuming
core/freeform - The content of the freeform block now includes the block delimiters
<!-- wp:freeform -->which was not the case previously and arguably not desirable (see also Blocks: Preserve unknown block, remove freeform block comment delimiters #2513)
| var truthy = []; | ||
| var falsey = []; | ||
|
|
||
| // nod to performance over a simpler reduce |
There was a problem hiding this comment.
❤️ A good place for micro-optimizations.
| predicate( item ) | ||
| ? truthy.push( item ) | ||
| : falsey.push( item ); | ||
| }; |
There was a problem hiding this comment.
Unnecessary semi-colon. Guessing it wouldn't be very easy for us to apply linting to this file, would it?
There was a problem hiding this comment.
is it? I thought I would be following style guidelines by including it.
|
Thanks @aduth - I have updated with your feedback! |
Codecov Report
@@ Coverage Diff @@
## master #2743 +/- ##
=======================================
Coverage 32.05% 32.05%
=======================================
Files 249 249
Lines 6885 6885
Branches 1249 1249
=======================================
Hits 2207 2207
Misses 3934 3934
Partials 744 744
Continue to review full report at Codecov.
|
blocks/api/parser.js
Outdated
| return grammarParse( content ).reduce( ( memo, blockNode ) => { | ||
| const { blockName, rawContent, attrs } = blockNode; | ||
| // eslint-disable-next-line no-unused-vars | ||
| const { blockName, innerBlocks, innerHTML: rawContent, attrs } = blockNode; |
There was a problem hiding this comment.
We probably want to normalize this toward innerHTML and drop rawContent, for consistency, and also because objects of innerBlocks will keep their innerHTML property. Should be fine to do separately.
aduth
left a comment
There was a problem hiding this comment.
Oops, looks like there's still an issue: Support for passing strings as callables in PHP was only added in PHP 5.4, which does not meet our support requirements of 5.2.4:
http://php.net/manual/en/language.types.callable.php
https://wordpress.org/about/requirements/
|
@aduth what’s the point of removing the type hint? |
|
oh roger @aduth - I thought that the act itself of passing a string callable was the issue, not adding the type hint |
In this patch we're opening up a new avenue for allowing nested blocks in the data structure. For each block: - Nested blocks appear as `innerBlocks` as a sequential list - The contained HTML _without_ the nested blocks appear as a string property `innerHtml` which replaces `rawContent` Also: - Remove `WP_` prefix on grammar terms - not needed - was there from the earliest iterations where different parts were prefixed by which spec they implemented, such as HTML_, URL_, etc... Regenerates fixtures based on updated parser Disable eslint for line so tests will run Fix rebase issue Update based on PR feedback I'm breaking my own rules here by introducing more code into the parser but I'm also not sure how we can escape this without placing higher demands on some post-processing after the parse. Changes: - Do away with non-supported language features - Abstract `joinBlocks( pre, tokens, post )` into a function basically just need to join non-empty items into a flat list Tiny fix and big header comment Added comment to start of rules Actually return blocks from peg_join_blocks() Minor adjustments to parser to preserve existing behavior Update parser and fix bug in updates When the `Balanced_Block` was rebuilt to be defined as a starting block, some number of tokens and non-closing HTML, finished by a closing block, I used a `+` to indicate that we needed at least _some_ content inside of the block to be valid. In some regards this is true because empty blocks should be void blocks. On the other hand, it's very likely that we'll receive empty non-void blocks in practice and the parser should not invalidate one because it has chosen the wrong syntax. This update replaces the `+` with a `*` such that we can have empty blocks and they will be treated as normal. Remove unnecessary semicolon Blocks: Access block content by innerHTML Parser: Drop callable type hint Type hint supported only in PHP 5.4+, above support level. Only use is in call_user_func, supportable with expected string callable input prior to 5.4
c344da6 to
4e0b589
Compare
|
Thanks for all your help on this one @aduth! |
|
There are no test cases for this major and foundational change to the parser, even though there is a framework in place for creating them. What is the plan for adding these test cases? |
In this patch we're opening up a new avenue for allowing nested blocks
in the data structure.
For each block:
innerBlocksas a sequential listproperty
innerHtmlwhich replacesrawContent(update it doesn't replace it yet)Testing
Smoke test in the browser.
Load the grammar into the explorer (you can use the fetcher URL in the explorer for the grammar link) then load in some test content and play with nesting. You should be able to see how
innerBlocksgrows and when blocks do nest, that the snippet which represents them disappears frominnerHtml.Input
Output
Notes