Hey there!
I’m trying to add logic to decorate an empty line but am running into some problems. My initial approach involved using a widget decorator as suggested in this issue: Adding placeholder on active line if active line is empty - #3 by crossvalidator since that’s what view.placeholder
does under the hood: view/src/placeholder.ts at main · codemirror/view · GitHub.
For context, my code ended up looking something like this:
function addIndentationMarkers(view: EditorView) {
const builder = new RangeSetBuilder<Decoration>();
for (const { from, to } of view.visibleRanges) {
let pos = from;
while (pos <= to) {
const line = view.state.doc.lineAt(pos);
const { text } = line;
// Decorate empty line
if (text.trim().length === 0) {
const indentationWidget = Decoration.widget({
widget: new IndentationWidget(),
});
builder.add(line.from, line.from, indentationWidget);
}
// Move on to next line
pos = line.to + 1;
}
}
return builder.finish();
}
function createIndentMarkerPlugin() {
return ViewPlugin.define(
(view) => ({
decorations: addIndentationMarkers(view),
update(update) {
if (update.docChanged || update.viewportChanged) {
this.decorations = addIndentationMarkers(update.view);
}
},
}),
{
decorations: (v) => v.decorations,
},
);
}
However, after implementing this and playing around with it a bit, I discovered that my widget would conflict with the mark decoration that gets produced by the lint plugin, which we use to display lint errors. Specifically, the cm-lintRange
mark, rendered by the lint package, wraps my custom widget which ruins the styling and leads to some pretty strange bugs. Here’s what it looks like in the DOM:
Notice my custom widget (parent of cm-indentation-marker
) is wrapped by the cm-lintRange
element whereas before the lintRange
element wouldn’t be rendered at all since this line is empty.
This leads to some pretty strange bugs such as lines seemingly disappearing (being rendered with a zero length height):
It’s unclear to me whether this is a bug or intentional behavior since the docs only specify precedence between a mark component and another mark component:
Nesting order is determined by precedence of the facet or (below the facet-provided decorations) view plugin.
And between multiple widgets:
When multiple widgets sit at the same position, their
side
values will determine their ordering—those with a lower value come first.
but not between a conflicting widget and mark decoration.
In any case, this made me feel like I was approaching this the wrong way because I don’t want my overlayed content to interfere with the doc’s content like it is here which suggests that they shouldn’t live in the same place in the DOM.
My other attempts at this involved using tooltips (which was slow and interfered with the doc’s content) and mark decorators (which wouldn’t render at all for ranges with zero length). Line decorations don’t work either because the overlayed content depends on adding new elements to the DOM rather than just changing the styling of the line.
Let me know if there’s a way around this or what you would suggest doing to overlay content above an empty line in a way that doesn’t conflict with the doc’s content or decorations produced by other extensions. Thanks so much for your help!