Hi my usecase is that I have two function which will be exposed which are addHighlight
and clearHighlights
what addHighlight
does is takes from
and to
and highlights that part and also I need to make highlighted part atomic , for which i am using atomicExtension
, and clearHighlights
will remove the highlight once the highlighted text is deleted
Below is my implementation , which is working fine ( except for after highligting when I press space the highlight colour is extented ,and probably on next render it is going back to word)
I wanted to ask is this the correct implementation or there are any other good approach to achieve this? And how can we fix the above mentioned bug
const addHighlightEffect = StateEffect.define<{
from: number;
to: number;
className: string;
atomic: boolean;
}>();
const removeHighlightEffect = StateEffect.define<{
className: string;
}>();
const highlightExtension = StateField.define<DecorationSet>({
create() {
return Decoration.none;
},
update(decorations, transaction) {
decorations = decorations.map(transaction.changes);
for (const effect of transaction.effects) {
if (effect.is(addHighlightEffect)) {
const { from, to, className, atomic } = effect.value;
const decoration = Decoration.mark({
class: className,
inclusive: true,
atomic,
}).range(from, to);
decorations = decorations.update({ add: [decoration] });
} else if (effect.is(removeHighlightEffect)) {
const { className } = effect.value;
decorations = decorations.update({
filter: (from, to, value) => {
return !value.spec.class.includes(className);
},
});
}
}
return decorations;
},
provide: (field) => EditorView.decorations.from(field),
});
const atomicExtension = EditorView.atomicRanges.of((view: EditorView) => {
const decorations = view.state.field(highlightExtension, false);
if (!decorations) {
return RangeSet.empty;
}
const ranges: { from: number; to: number; value: Decoration }[] = [];
decorations.between(0, view.state.doc.length, (from, to, value) => {
if (value.spec?.class && value.spec?.atomic) {
ranges.push({ from, to, value });
}
});
return RangeSet.of(
ranges.map(({ from, to, value }) => ({ from, to, value })),
false,
);
});
And this is how I am using the above mentioned function
addHighlight: (
type,
{ columnStart, columnEnd, lineStart, lineEnd },
) => {
const state = view.state;
const from = state.doc.line(lineStart + 1).from + columnStart;
const to = state.doc.line(lineEnd + 1).from + columnEnd;
view.dispatch({
effects: addHighlightEffect.of({
from,
to,
className: classes[type],
atomic: type === 'parameter',
}),
});
view.dispatch({
selection: EditorSelection.cursor(to),
scrollIntoView: true,
});
},
clearHighlights: (type) => {
view.dispatch({
effects: removeHighlightEffect.of({
className: classes[type],
}),
});
},