mirror of https://github.com/heyman/heynote.git
Only iterate through the syntax tree in one place to build up a state and store in a StateField, which is then used by the other extensions
This commit is contained in:
parent
551089d613
commit
693390cac4
|
@ -7,6 +7,49 @@ import { Note, Document, NoteDelimiter } from "./lang-heynote/parser.terms.js"
|
|||
import { IterMode } from "@lezer/common";
|
||||
import { INITIAL_DATA } from "./annotation.js";
|
||||
|
||||
|
||||
function getBlocks(state) {
|
||||
const blocks = [];
|
||||
syntaxTree(state).iterate({
|
||||
enter: (type) => {
|
||||
if (type.type.id == Document || type.type.id == Note) {
|
||||
return true
|
||||
} else if (type.type.id === NoteDelimiter) {
|
||||
const contentNode = type.node.nextSibling
|
||||
blocks.push({
|
||||
content: {
|
||||
from: contentNode.from,
|
||||
to: contentNode.to,
|
||||
},
|
||||
delimiter: {
|
||||
from: type.from,
|
||||
to: type.to,
|
||||
},
|
||||
})
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
return blocks
|
||||
}
|
||||
|
||||
const blockState = StateField.define({
|
||||
create(state) {
|
||||
return getBlocks(state);
|
||||
},
|
||||
update(blocks, transaction) {
|
||||
//console.log("blocks", blocks)
|
||||
if (transaction.docChanged) {
|
||||
return getBlocks(transaction.state);
|
||||
}
|
||||
//return widgets.map(transaction.changes);
|
||||
return blocks
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
class NoteBlockStart extends WidgetType {
|
||||
constructor() {
|
||||
super()
|
||||
|
@ -46,21 +89,19 @@ class FirstNoteBlockStart extends WidgetType {
|
|||
const noteBlockWidget = () => {
|
||||
const decorate = (state) => {
|
||||
const widgets = [];
|
||||
|
||||
syntaxTree(state).iterate({
|
||||
enter: (type) => {
|
||||
if (type.name === "NoteDelimiter") {
|
||||
//console.log("found!", type.name, type.from, type.to)
|
||||
let deco = Decoration.replace({
|
||||
widget: type.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
||||
inclusive: true,
|
||||
block: type.from === 0 ? false : true,
|
||||
side: 0,
|
||||
});
|
||||
widgets.push(deco.range(type.from === 0 ? type.from : type.from+1, type.from === 0 ? type.to : type.to-1));
|
||||
}
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
|
||||
state.facet(blockState).forEach(block => {
|
||||
let delimiter = block.delimiter
|
||||
let deco = Decoration.replace({
|
||||
widget: delimiter.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
||||
inclusive: true,
|
||||
block: delimiter.from === 0 ? false : true,
|
||||
side: 0,
|
||||
});
|
||||
widgets.push(deco.range(
|
||||
delimiter.from === 0 ? delimiter.from : delimiter.from+1,
|
||||
delimiter.from === 0 ? delimiter.to : delimiter.to-1,
|
||||
));
|
||||
});
|
||||
|
||||
return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none;
|
||||
|
@ -88,19 +129,14 @@ const noteBlockWidget = () => {
|
|||
|
||||
|
||||
|
||||
|
||||
function atomicRanges(view) {
|
||||
let builder = new RangeSetBuilder()
|
||||
syntaxTree(view.state).iterate({
|
||||
enter: (type) => {
|
||||
if (type.type.id === NoteDelimiter) {
|
||||
builder.add(type.from, type.to, {})
|
||||
}
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
view.state.facet(blockState).forEach(block => {
|
||||
builder.add(block.delimiter.from, block.delimiter.to, {})
|
||||
})
|
||||
return builder.finish()
|
||||
}
|
||||
|
||||
const atomicNoteBlock = ViewPlugin.fromClass(
|
||||
class {
|
||||
constructor(view) {
|
||||
|
@ -115,7 +151,7 @@ const atomicNoteBlock = ViewPlugin.fromClass(
|
|||
},
|
||||
{
|
||||
provide: plugin => EditorView.atomicRanges.of(view => {
|
||||
return view.plugin(plugin)?.atomicRanges || Decoration.none
|
||||
return view.plugin(plugin)?.atomicRanges || []
|
||||
})
|
||||
}
|
||||
)
|
||||
|
@ -139,32 +175,17 @@ const blockLayer = () => {
|
|||
markers(view) {
|
||||
const markers = []
|
||||
let idx = 0
|
||||
syntaxTree(view.state).iterate({
|
||||
enter: (type) => {
|
||||
//console.log("type", type.name, type.type.id, Document)
|
||||
if (type.type.id == Document || type.type.id == Note) {
|
||||
return true
|
||||
} else if (type.type.id === NoteDelimiter) {
|
||||
const contentNode = type.node.nextSibling
|
||||
//console.log("adding marker", type.node.nextSibling.name)
|
||||
//let line = view.state.doc.lineAt(type.from)
|
||||
const fromCoords = view.coordsAtPos(contentNode.from)
|
||||
const toCoords = view.coordsAtPos(contentNode.to)
|
||||
//console.log("line", fromCoords.top, toCoords.bottom)
|
||||
//console.log("documentTop", view.documentTop)
|
||||
markers.push(new RectangleMarker(
|
||||
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
||||
0,
|
||||
fromCoords.top - (view.documentTop - view.documentPadding.top),
|
||||
editorWidth,
|
||||
(toCoords.bottom - fromCoords.top),
|
||||
))
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
mode: IterMode.IgnoreMounts,
|
||||
});
|
||||
view.state.facet(blockState).forEach(block => {
|
||||
const fromCoords = view.coordsAtPos(block.content.from)
|
||||
const toCoords = view.coordsAtPos(block.content.to)
|
||||
markers.push(new RectangleMarker(
|
||||
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
||||
0,
|
||||
fromCoords.top - (view.documentTop - view.documentPadding.top) - 1,
|
||||
editorWidth,
|
||||
(toCoords.bottom - fromCoords.top) + 2,
|
||||
))
|
||||
})
|
||||
return markers
|
||||
|
||||
},
|
||||
|
@ -182,7 +203,7 @@ const blockLayer = () => {
|
|||
|
||||
const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr) => {
|
||||
if (!tr.annotations.some(a => a.value === INITIAL_DATA)) {
|
||||
return [-1,10]
|
||||
return [-1,11]
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -193,11 +214,12 @@ const preventSelectionBeforeFirstBlock = EditorState.transactionFilter.of((tr) =
|
|||
//console.log("transaction:", tr)
|
||||
tr?.selection?.ranges.forEach(range => {
|
||||
// change the selection to after the first block if the transaction sets the selection before the first block
|
||||
if (range && range.from < 10) {
|
||||
range.from = 10
|
||||
const markerSize = 11
|
||||
if (range && range.from < markerSize) {
|
||||
range.from = markerSize
|
||||
}
|
||||
if (range && range.to < 10) {
|
||||
range.to = 10
|
||||
if (range && range.to < markerSize) {
|
||||
range.to = markerSize
|
||||
}
|
||||
})
|
||||
return tr
|
||||
|
@ -206,6 +228,8 @@ const preventSelectionBeforeFirstBlock = EditorState.transactionFilter.of((tr) =
|
|||
|
||||
export const noteBlockExtension = () => {
|
||||
return [
|
||||
blockState,
|
||||
|
||||
noteBlockWidget(),
|
||||
atomicNoteBlock,
|
||||
blockLayer(),
|
||||
|
|
Loading…
Reference in New Issue