mirror of
https://github.com/heyman/heynote.git
synced 2024-11-26 01:44:05 +01:00
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 { IterMode } from "@lezer/common";
|
||||||
import { INITIAL_DATA } from "./annotation.js";
|
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 {
|
class NoteBlockStart extends WidgetType {
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
@ -47,20 +90,18 @@ const noteBlockWidget = () => {
|
|||||||
const decorate = (state) => {
|
const decorate = (state) => {
|
||||||
const widgets = [];
|
const widgets = [];
|
||||||
|
|
||||||
syntaxTree(state).iterate({
|
state.facet(blockState).forEach(block => {
|
||||||
enter: (type) => {
|
let delimiter = block.delimiter
|
||||||
if (type.name === "NoteDelimiter") {
|
|
||||||
//console.log("found!", type.name, type.from, type.to)
|
|
||||||
let deco = Decoration.replace({
|
let deco = Decoration.replace({
|
||||||
widget: type.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
widget: delimiter.from === 0 ? new FirstNoteBlockStart() : new NoteBlockStart(),
|
||||||
inclusive: true,
|
inclusive: true,
|
||||||
block: type.from === 0 ? false : true,
|
block: delimiter.from === 0 ? false : true,
|
||||||
side: 0,
|
side: 0,
|
||||||
});
|
});
|
||||||
widgets.push(deco.range(type.from === 0 ? type.from : type.from+1, type.from === 0 ? type.to : type.to-1));
|
widgets.push(deco.range(
|
||||||
}
|
delimiter.from === 0 ? delimiter.from : delimiter.from+1,
|
||||||
},
|
delimiter.from === 0 ? delimiter.to : delimiter.to-1,
|
||||||
mode: IterMode.IgnoreMounts,
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none;
|
return widgets.length > 0 ? RangeSet.of(widgets) : Decoration.none;
|
||||||
@ -88,19 +129,14 @@ const noteBlockWidget = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function atomicRanges(view) {
|
function atomicRanges(view) {
|
||||||
let builder = new RangeSetBuilder()
|
let builder = new RangeSetBuilder()
|
||||||
syntaxTree(view.state).iterate({
|
view.state.facet(blockState).forEach(block => {
|
||||||
enter: (type) => {
|
builder.add(block.delimiter.from, block.delimiter.to, {})
|
||||||
if (type.type.id === NoteDelimiter) {
|
})
|
||||||
builder.add(type.from, type.to, {})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mode: IterMode.IgnoreMounts,
|
|
||||||
});
|
|
||||||
return builder.finish()
|
return builder.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
const atomicNoteBlock = ViewPlugin.fromClass(
|
const atomicNoteBlock = ViewPlugin.fromClass(
|
||||||
class {
|
class {
|
||||||
constructor(view) {
|
constructor(view) {
|
||||||
@ -115,7 +151,7 @@ const atomicNoteBlock = ViewPlugin.fromClass(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: plugin => EditorView.atomicRanges.of(view => {
|
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) {
|
markers(view) {
|
||||||
const markers = []
|
const markers = []
|
||||||
let idx = 0
|
let idx = 0
|
||||||
syntaxTree(view.state).iterate({
|
view.state.facet(blockState).forEach(block => {
|
||||||
enter: (type) => {
|
const fromCoords = view.coordsAtPos(block.content.from)
|
||||||
//console.log("type", type.name, type.type.id, Document)
|
const toCoords = view.coordsAtPos(block.content.to)
|
||||||
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(
|
markers.push(new RectangleMarker(
|
||||||
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
idx++ % 2 == 0 ? "block-even" : "block-odd",
|
||||||
0,
|
0,
|
||||||
fromCoords.top - (view.documentTop - view.documentPadding.top),
|
fromCoords.top - (view.documentTop - view.documentPadding.top) - 1,
|
||||||
editorWidth,
|
editorWidth,
|
||||||
(toCoords.bottom - fromCoords.top),
|
(toCoords.bottom - fromCoords.top) + 2,
|
||||||
))
|
))
|
||||||
return false;
|
})
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
mode: IterMode.IgnoreMounts,
|
|
||||||
});
|
|
||||||
return markers
|
return markers
|
||||||
|
|
||||||
},
|
},
|
||||||
@ -182,7 +203,7 @@ const blockLayer = () => {
|
|||||||
|
|
||||||
const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr) => {
|
const preventFirstBlockFromBeingDeleted = EditorState.changeFilter.of((tr) => {
|
||||||
if (!tr.annotations.some(a => a.value === INITIAL_DATA)) {
|
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)
|
//console.log("transaction:", tr)
|
||||||
tr?.selection?.ranges.forEach(range => {
|
tr?.selection?.ranges.forEach(range => {
|
||||||
// change the selection to after the first block if the transaction sets the selection before the first block
|
// change the selection to after the first block if the transaction sets the selection before the first block
|
||||||
if (range && range.from < 10) {
|
const markerSize = 11
|
||||||
range.from = 10
|
if (range && range.from < markerSize) {
|
||||||
|
range.from = markerSize
|
||||||
}
|
}
|
||||||
if (range && range.to < 10) {
|
if (range && range.to < markerSize) {
|
||||||
range.to = 10
|
range.to = markerSize
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return tr
|
return tr
|
||||||
@ -206,6 +228,8 @@ const preventSelectionBeforeFirstBlock = EditorState.transactionFilter.of((tr) =
|
|||||||
|
|
||||||
export const noteBlockExtension = () => {
|
export const noteBlockExtension = () => {
|
||||||
return [
|
return [
|
||||||
|
blockState,
|
||||||
|
|
||||||
noteBlockWidget(),
|
noteBlockWidget(),
|
||||||
atomicNoteBlock,
|
atomicNoteBlock,
|
||||||
blockLayer(),
|
blockLayer(),
|
||||||
|
Loading…
Reference in New Issue
Block a user