For calculating active states of menu buttons (like checking for h1, h2, etc.) I’m iterating over nodes within the current text selection on every keystroke.
Basically something like this:
const { from, to } = state.selection
state.doc.nodesBetween(from, to, (node, pos) => {
// ...
})
This works great for “normal” sized documents. But for large documents this is pretty slow. In my test for a document with 5000 nodes this takes about 250ms.
I’m experimenting with a custom nodesBetween
method which is searching within a sliced array of doc.content.content
.
function fastNodesBetween(
doc: ProseMirrorNode,
from: number,
to: number,
fn: (
node: ProseMirrorNode,
pos: number,
parent: ProseMirrorNode,
index: number,
) => boolean | void | null | undefined,
): void {
const $from = doc.resolve(from)
const $to = doc.resolve(to)
const startPos = $from.start(1)
const fromOffset = from - startPos
const toOffset = fromOffset + to - from
const fromIndex = $from.index(0)
const toIndex = $to.index(0)
// `doc.content.content` is not public
// @ts-ignore
const nodes = (doc.content.content as ProseMirrorNode[]).slice(fromIndex, toIndex + 1)
Fragment
.fromArray(nodes)
.nodesBetween(fromOffset, toOffset, (node, pos, parent, index) => {
pos = $from.before(1) + pos
index = parent
? index
: fromIndex + index
parent = parent || doc
return fn(node, pos, parent, index)
})
}
In my tests this runs in about 1ms instead of 250ms which is great.
Searching within doc.slice
or state.doc.content.cut
didn’t help for performance. That’s why I’m searching within the private doc.content.content
which isn’t optimal I think.
But maybe there is another way I missed to efficiently search nodes within the current selection?