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) //console.log("found parser for language:", langName)
return { return {
parser:languageMapping[langName], parser:languageMapping[langName],
overlay: [{from:node.from, to:node.to}],
} }
} }
} }

View File

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