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
Schema addChildCheck/addAttributeCheck filtering #15959
base: master
Are you sure you want to change the base?
Conversation
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 added some comments.
One decision to make here is whether we want to stick to the original plan (my advice to keep things simple) or if we want to extend the original plan a bit, following what @map-r did in this PR.
In short (read my comments to understand more):
- The original was to have
addChildCheck( callback, elementName )
andaddAttributeCheck( callback, attributeName )
so that these callbacks are fired only for given element name (checkChild( context, elementName )
) or attribute (checkAttribute( context, attributeName )
). - However they could be extended to cover more cases.
- It would be something like
addChildCheck( callback, elementOrParentName )
andaddAttributeCheck( callback, attributeOrElementName )
. - With these extensions, it seems we would cover most (all?) of the cases we have right now. Callback added by both methods would be additionally fired if
context.last.name
matches the parameter. - I'd advise closing the other PR (introducing disallowing in schema) first, and then rewrite some of the callbacks to static declarations. And then see how many more callbacks we need to cover.
- In general, I'd err on keeping this simple. If there's one or two global callbacks, then we are fine. We can always extend later. OTOH, note, that if you go with extensions how Adam proposed, you need to make extra checks in the callbacks (which I commented on in this review -- I thought they are not needed but they actually are). Extending later would be a breaking change
@DawidKossowski @map-r @niegowski I leave this to you.
if ( childDefinition.name === '$marker' ) { | ||
return true; | ||
} |
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.
If now we fire this callback just for $marker
checks, can't it be just () => true
? 🤔.
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.
we run it for both $marker as item in context and as a child element
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.
thus, it might fire for wrong thing (when $marker is in context and something else is child)
if ( typeof retValue === 'boolean' ) { | ||
evt.stop(); | ||
evt.return = retValue; |
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.
Can't we move it into the if
above (in the loop)?
Ah, okay, I get it. This should get a comment why we are doing it exactly like that. Add a comment here (above this `if`) and also above `if` from line `114` (comment there that if any of the callbacks disallowed the attribute then it is disallowed and it cannot be overwritten, hence we can stop processing).
One thing that is related to the other PR is that it would be good to document that the custom callbacks will get precedence over the rules declared in schema.register()
. After we introduce disabling things in schema.register()
it may be confusing, that, for example, I disabled something through schema.register()
but it is still enabled (because another custom callback allowed for it). I think we should mention it in checkChild()
, checkAttribute()
, addChildCheck()
, and addAttributeCheck()
.
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.
The above comment as of today is still to be resolved, but probably in sync with that other PR Szymon mentioned
public addAttributeCheck( callback: SchemaAttributeCheckCallback ): void { | ||
this.on<SchemaCheckAttributeEvent>( 'checkAttribute', ( evt, [ ctx, attributeName ] ) => { | ||
const retValue = callback( ctx, attributeName ); | ||
public addAttributeCheck( callback: SchemaAttributeCheckCallback, forNode?: string ): void { |
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.
Misunderstanding here on your part. It should be declared for attribute, not for node. From the issue:
First, we should add additional parameter to
addChildCheck()
andaddAttributeCheck()
that will specify for which element/attribute this callback is declared.
We want to call this callbacks only when "bold" or "linkHref" are checked, not when given elements are checked.
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.
Although I think I understand why you went this way. We have two types of callbacks when it comes to current addAttributeCheck
.
First -- we have custom callbacks for attribute X. They would be solved if you implemented it accordingly to my idea (set callback for attribute name, not element name).
But then, we have attribute-related callbacks for element Y. For example, disallow all attributes in $text
inside title-content
. But in this case, such callback should be actually called for all attributes, so it makes sense that it would stay as a generic callback. It's the same as adding one callback for all attributes.
OTOH, if a callback stays generic it will be also called for non-related items. Let's take example rule:
schema.addAttributeCheck( ( context, attributeName ) => {
if ( context.endsWith( 'codeBlock $text' ) ) {
return false;
}
} );
Now it would be fired for, e.g. schema.checkAttribute( [ 'image' ], 'imageSrc' )
, which doesn't make much sense. If we specify $text
here, and only get callbacks for context.last.name
and attributeName
, then we could limit the number of callbacks even more.
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.
again, the solution is to either do the attr check only and disregard specific nodes, making some checks generic, or to make option to use either context or attribute to filter the checks.
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 guess making two properties to filter in both childCheck and attributeCheck seems reasonable. I can leave it like this to be improved, or try to improve it by this two-type filtering, tbd.
|
||
const generalChecks = this._customChildChecks.get( this._genericCheckSymbol ) || []; | ||
const checksForChild = this._customChildChecks.get( childDef.name ) || []; | ||
const checksForContext = this._customChildChecks.get( ctx.last.name ) || []; |
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 am very hesitant about this. It expands how this mechanism work in a way that I haven thought about, so I am not sure if this is correct.
The point of the issue was to turn this situation:
- We call
schema.childCheck( context, 'elementName' );
- As a result, we call 15 custom callbacks
- Then we call declarative check (if needed).
Into this situation:
- We call
schema.childCheck( context, 'elementName' );
- As a result, we call 2 custom generic callbacks.
- Then we call 1 specific callback for
'elementName'
(if needed). - Then we call declarative check (if needed).
With your solution, we call a custom callback both when we want to check if 'elementName'
can be inserted somewhere and where something is being inserted into 'elementName'
.
It may turn one or two global callbacks into specific but works in a bit unexpected way. Unless you have convincing arguments, for now, I'd be against it (because I don't want to spend too much time thinking about this topic) but we can keep it in mind as a possible improvement.
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.
everything we would need at this point is to make a filter object, which either has context
or a child
as a property, with the name of element being the value. Then we will only check what we want only once, as calling this twice is what you say you have problem with.
@DawidKossowski Outro for you and of course for @scofalik: I've fixed and resolved some of the comments. The major one was to move the callback triggering into an actual The things left out for you to pick up are:
|
Suggested merge commit message (convention)
Internal (engine): Added option for custom check callbacks to run only for a specified context node or children. Closes #15834.
Additional information