Make the Lezer parser for Heynote documents use overlay parsers for the individual blocks

This fixes the issue with some blocks not being foldable (since the NoteContent nodes were getting replaced by the tree produced by the sub-parser).

Update the todo-checkbox plugin to use regular expression to find the todo list syntax, since it was previously relying on the parsed syntax tree, and the markdown syntax tree is no longer available in the main document's syntax tree.
This commit is contained in:
Jonatan Heyman 2025-06-09 14:13:59 +02:00
parent 82200da28d
commit 0e8a5dfccc
2 changed files with 13 additions and 28 deletions

View File

@ -34,6 +34,7 @@ export function configureNesting() {
//console.log("found parser for language:", langName)
return {
parser:languageMapping[langName],
overlay: [{from:node.from, to:node.to}],
}
}
}

View File

@ -1,8 +1,8 @@
import { EditorView, Decoration } from "@codemirror/view"
import { syntaxTree, ensureSyntaxTree } from "@codemirror/language"
import { WidgetType } from "@codemirror/view"
import { ViewUpdate, ViewPlugin, DecorationSet } from "@codemirror/view"
import { getNoteBlockFromPos } from "./block/block"
import { isMonospaceFont } from "./theme/font-theme"
import { transactionsHasAnnotation, SET_FONT } from "./annotation"
@ -48,38 +48,22 @@ class CheckboxWidget extends WidgetType {
ignoreEvent() { return false }
}
const checkboxRegex = /^([\t\f\v ]*-[\t\f\v ]*)\[( |x|X)\]/gm
function checkboxes(view: EditorView) {
let widgets: any = []
for (let { from, to } of view.visibleRanges) {
syntaxTree(view.state).iterate({
from, to,
enter: (nodeRef) => {
// make sure we only enter markdown nodes
if (nodeRef.name == "Note") {
let langNode = nodeRef.node.firstChild?.firstChild
if (langNode) {
const language = view.state.doc.sliceString(langNode.from, langNode.to)
if (!language.startsWith("markdown")) {
return false
}
}
}
if (nodeRef.name == "TaskMarker") {
// the Markdown parser creates a TaskMarker for "- [x]", but we don't want to replace it with a
// checkbox widget, unless its followed by a space
if (view.state.doc.sliceString(nodeRef.to, nodeRef.to+1) === " ") {
let isChecked = view.state.doc.sliceString(nodeRef.from, nodeRef.to).toLowerCase() === "[x]"
let deco = Decoration.replace({
widget: new CheckboxWidget(isChecked, view.state.facet(isMonospaceFont)),
inclusive: false,
})
widgets.push(deco.range(nodeRef.from, nodeRef.to))
}
}
let range = view.state.sliceDoc(from, to)
let match
while (match = checkboxRegex.exec(range)) {
if (getNoteBlockFromPos(view.state, from + match.index)?.language?.name === "markdown") {
let deco = Decoration.replace({
widget: new CheckboxWidget(match[2] === "x" || match[2] === "X", view.state.facet(isMonospaceFont)),
inclusive: false,
})
widgets.push(deco.range(from + match.index + match[1].length, from + match.index + match[0].length))
}
})
}
}
return Decoration.set(widgets)
}