I'm writing an upcast converter that needs to consume all children of the element being converted to ensure that they are skipped.
I could use for( 'upcast' ).elementToElement() converter with a callback for the model property, but I don't have the access to the conversion API there. This detail forces me to write an event-based converter which is a pity.
There may be more things that the higher-level converters could know about.
If you'd like to see this improvement implemented, add a 👍 reaction to this post.
cc @scofalik @jodator
I was pretty sure this is available in the callback. Check if it is available in downcasting.
AFAIR most of the one-way converters has a minimal set of information in the callback. For instance attribute converters do not have access to the model element on which this attribute is set. This might be similar story in element to element converters.
ps.: maybe a simplified use-case for your converter could be helpful to check what we can do there?
I'm writing an upcast converter that needs to consume all children of the element being converted to ensure that they are skipped.
Actually, I was wrong – there's no method for consuming all children. So the solution needs to be a bit different – not calling convertChildren() will have a similar effect. But then it's a bit different issue: #7336.
Still, this issue could be valid because I might've wanted to call convertChildren().
Conclusion: Make conversionApi accessible from model/view callbacks of elementToElement() and other converters for upcast and downcast pipelines.
Looks it is a way to do this: https://github.com/ckeditor/ckeditor5/issues/7336#issuecomment-654704526.
Current element-to-element upcast converter lacks simple, but powerful addition that would allow for a simpler upcast converters (check the example of RawHTML POC simplification).
editor.conversion.for( 'upcast' ).elementToElement( {
view: 'div',
model: ( viewElement, writer ) => {
return writer.createElement( 'div' );
}
} );
So we need to add whole conversionApi to the elementToElement()'s model() callback.
Option 1: Backwards compatible
editor.conversion.for( 'upcast' ).elementToElement( {
view: 'div',
model: ( viewElement, writer, conversionApi ) => {
// API usage
conversionApi.consumable.consume( viewElement );
// Ambigous writer usage:
return writer.createElement( 'div' ); // or
return conversionApi.writer.createElement( 'div' );
}
} );
Option 2: Backwards in-compatible
// Using object destructuring:
editor.conversion.for( 'upcast' ).elementToElement( {
view: 'div',
model: ( viewElement, { writer, consumabe } ) => {
consumable.consume( viewElement );
return writer.createElement( 'div' );
}
} );
// Old-fashined objects:
editor.conversion.for( 'upcast' ).elementToElement( {
view: 'div',
model: ( viewElement, conversionApi ) => {
conversionApi.consumable.consume( viewElement );
return conversionApi.writer.createElement( 'div' );
}
} );
Option 1 (Backwards compatible):
writer, a conversionApi is an "advanced"/less common use-case.writer as parameter and in conversionAPI.Option 2 (Backwards in-compatible):
model: ( viewEl, writer ) => {} -> model: ( viewEl, { writer } ) => {}.I'd see option 2 to be implemented, but backwards incompatibility and some additional work is need.
I also like 2 the most. It's simple and future-proof (if more tools/steps arrive to the conversion pipeline).
What I'm worried about, though, is the backwards compatibility issue. Upgrading to this editor version will certainly require some work if an integration brings its own editing plugins. That's a huge cost (although the migration path is simple).
What do other callbacks (`view/model`) in other converters accept? If we change it here, we'll need to change it in other places as well. Does it make sense in all those examples?
@Reinmar - as for now I see that we could unify upcast/downcast for element conversion:
elementToElement( element, writer ) => {}attributeToElement( attributeValue, writer ) => {}writer.createAttributeElement() anyway... dunno for now :man_shrugging: attributeToAttribute( attributeValue ) => ( {} )elementToElement( element, writer ) => {}attributeToElement( element ) => {}attributeToAttribute( element ) => {}In the topic: #7774.
I also vote for the option 2 for all of both upcast and downcast helpers. If we have a green light I can handle it.
How about having elementValue, writer, conversionApi in all those calls. I understand that the writer is duplicated but I can live with it.
elementToElement( element, writer, conversionApi ) => {}attributeToElement( attributeValue, writer, conversionApi ) => {}attributeToAttribute( attributeValue, writer, conversionApi ) => ( {} )elementToElement( element, writer, conversionApi ) => {}attributeToElement( element, writer, conversionApi ) => {}attributeToAttribute( element, writer, conversionApi ) => {}OTOH, I don't see anything wrong with breaking change either. The change is cosmetic, people should be able to change their custom code easily.
Hm.
Now, as I look at it, I actually like having a breaking change more :D.
BTW. Don't forget about markers conversion. We might want to have conversionApi there too, for example, to consume the range in order to halt ("disable") conversion (to be researched if it is possible to "disable conversion" right now, and how).
To sum up:
callback( elementOrAttribute, conversionApi ).BTW. Don't forget about markers conversion. We might want to have
conversionApithere too, for example, to consume the range in order to halt ("disable") conversion (to be researched if it is possible to "disable conversion" right now, and how).
AFAICS, markers callbacks are similar, so adding conversionApi there could potentially make sense. As I didn't use marker converters much, I'm relaying on you in this.
The simplest upgrade path is to use Object destructuring for existing callbacks:
Before:
editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'attribute',
view: ( attributeValue, writer ) => {
return writer.createAttributeElement( 'span', { style: 'color:blue' } );
}
} );
After:
editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'attribute',
view: ( attributeValue, { writer } ) => {
return writer.createAttributeElement( 'span', { style: 'color:blue' } );
}
} );
Or using variable name:
editor.conversion.for( 'downcast' ).attributeToElement( {
model: 'attribute',
view: ( attributeValue, { writer: viewWriter } ) => {
return viewWriter.createAttributeElement( 'span', { style: 'color:blue' } );
}
} );
Most helpful comment
The simplest upgrade path is to use Object destructuring for existing callbacks:
Before:
After:
Or using variable name: