Hi!
Sorry for my amateur questions again. I want to insert a widget above codeblocks, and when this widget is clicked then it should collapse the codelines below it. If clicked again, then it should uncollapse the lines. I got it working (there is probably a much easier way to do this), but the problem is, that when the code is edited, and more code is added then the end position is increased. When I created my widget, I set the start and end parameters of the codelines, which should be collapsed.
So, my question is:
- How can I update these start and end position if the code was edited, so it matches the current content?
- When enabling the plugin, how can I immediately collapse specific blocks?
- Is it possible to combine CodeblockHeader and markField?
Thanks in advance!
Here is my code:
export const CodeblockHeader = StateField.define<DecorationSet>({
create(state): DecorationSet {
return Decoration.none;
},
update(oldState: DecorationSet, transaction: Transaction): DecorationSet {
const builder = new RangeSetBuilder<Decoration>();
let CollapseStart = null;
let CollapseEnd = null;
let WidgetStart;
syntaxTree(transaction.state).iterate({
enter(node) {
const lineText = transaction.state.doc.slice(node.from, node.to).toString();
if (node.type.id === 3 ) {
CollapseStart = node.from
WidgetStart = node.from;
}
if (node.type.id === 6 ) {
CollapseEnd = node.to;
if (CollapseStart != null && CollapseEnd!= null ){
builder.add(WidgetStart, WidgetStart, Decoration.widget({
widget: new TextAboveCodeblockWidget("Testing", CollapseStart, CollapseEnd),
block: true
})
);// builder
CollapseStart = null;
CollapseEnd = null;
}
}
},// enter
});// syntaxTree
return builder.finish();
},// update
provide(field: StateField<DecorationSet>): Extension {
return EditorView.decorations.from(field);
},// provide
});// CodeblockHeader
const Collapse = StateEffect.define(), UnCollapse = StateEffect.define()
const markField = StateField.define({
create() { return Decoration.none },
update(value, tr) {
value = value.map(tr.changes)
for (let effect of tr.effects) {
if (effect.is(Collapse)) value = value.update({add: effect.value, sort: true})
else if (effect.is(UnCollapse)) value = value.update({filter: effect.value})
}
return value
},
provide: f => EditorView.decorations.from(f)
})
const doFold = Decoration.replace({block: true})
class TextAboveCodeblockWidget extends WidgetType {
constructor(private text: string,private CollapseStart: number, private CollapseEnd: number, private isCollapsed: boolean) {
super();
this.folded = isCollapsed;
}
eq(other: TextAboveCodeblockWidget) { return other.folded == this.folded }
toDOM(view: EditorView): HTMLElement {
const container = document.createElement("div");
container.classList.add("codeblock-header-container");
container.addEventListener('click', function() {
if (this.folded) {
console.log("UnCollapse");
view.dispatch({
effects: UnCollapse.of((from, to) => to <= this.CollapseStart || from >= this.CollapseEnd)
})
} else {
console.log("Collapse");
view.dispatch({
effects: Collapse.of([doFold.range(this.CollapseStart, this.CollapseEnd)])
})
}
this.folded = !this.folded;
}.bind(this));
//});
const span = document.createElement("div");
span.innerText = this.text;
span.classList.add("codeblock-header-text");
const line = document.createElement("hr");
line.classList.add("codeblock-header-line");
line.style.border = '1px solid #46cced';
container.appendChild(span);
container.appendChild(line);
return container;
}
ignoreEvent() { return false }
}// TextAboveCodeblockWidget
export default class LineNumberingPlugin extends Plugin {
async onload() {
this.registerEditorExtension(CodeblockHeader);
this.registerEditorExtension(markField);
console.log("Plugin is registered");
}
}