Hello, what would be the best way for cursor to skip ranges hidden through Decoration.replace?
Should I use transactionFilter(tr) that would update tr.selection if it gets into RangeSet that stores all Decoration.replace?
Hello, what would be the best way for cursor to skip ranges hidden through Decoration.replace?
Should I use transactionFilter(tr) that would update tr.selection if it gets into RangeSet that stores all Decoration.replace?
You may be looking for atomicRanges.
Thanks a lot—this is exactly what’s needed!
Out of curiosity, if atomicRanges are included as core part of View why not to add them as additional parameter of Decoration.replace or Decoration.mark?
I made a quick plugin that links View.Plugin.atomicRanges with state.Facet(EditorView.decorations), but think it could be useful for others too.
import { Decoration, ViewPlugin, PluginField, EditorView } from '@codemirror/view'
import { RangeSet, Range } from '@codemirror/rangeset'
/** Decoration that hides the content and skips on selection move */
const rangeHide = Decoration.replace({})
/** Decoration that displays the content but skips on selection move */
const rangeSkip = Decoration.mark({})
/** ViewPlugin that enables the functionality
* to hide a range provide RangeSet with either `rangeHide` or `rangeSkip` to ViewState facet
*/
const atomicRanges = ViewPlugin.define(
(view) => ({ view }),
{
provide: PluginField.atomicRanges.from((val) => {
const decorations = val.view.state.facet(EditorView.decorations)
let ranges = []
for (let iter = RangeSet.iter(decorations); iter.value !== null; iter.next())
if (iter.value === rangeSkip || iter.value === rangeHide)
ranges.push(new Range(iter.from, iter.to, iter.value))
return RangeSet.of(ranges, true)
})
}
)
export { rangeHide, rangeSkip, atomicRanges }
Thank you @ilyakochik - this was very helpful to me.
I’ve been following the boolean toggle widget example but building a replace decoration instead of a widget and I want to make them atomic.
How would I apply your Facet in my case?
e.g.
const MyPluginClass = class {
constructor(view){
this.decorations = createsMyReplacementWidgets(update.view)
}
update(update){
if(update.docChanged || update.viewportChanged){
this.decorations = createsMyReplacementWidgets(update.view)
}
}
}
const myExtension = [
ViewPlugin.fromClass(
MyPluginClass,
{
decorations: v => v.decorations
}
)
]
I think this could work:
const MyPluginClass = class {
constructor(view) {
this.decorations = createsMyReplacementWidgets(view)
}
update(update) {
if (update.docChanged || update.viewportChanged) {
this.decorations = createsMyReplacementWidgets(update.view)
}
}
}
const myExtension = [
ViewPlugin.fromClass(
MyPluginClass,
{
decorations: v => v.decorations,
// provide PluginField
provide: PluginField.atomicRanges.from(val => val.decorations)
}
)
]
@ilyakochik yes this works perfectly - thank you very much