cm-tarnation
Version:
An alternative parser for CodeMirror 6
134 lines (111 loc) • 3.72 kB
text/typescript
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { NodeSet, Tree, TreeBuffer } from "@lezer/common"
import { CHUNK_ARRAY_INTERVAL } from "../constants"
import type { GrammarState } from "../grammar/state"
import type { ParserAction } from "../types"
export class Chunk {
declare from: number
declare to: number
declare length: number
declare tokens: Int16Array
declare size: number
declare open: ParserAction | null
declare close: ParserAction | null
declare state: GrammarState
declare tree?: Tree
constructor(pos: number, state: GrammarState) {
this.from = pos
this.to = pos
this.length = 0
this.state = state
this.tokens = new Int16Array(CHUNK_ARRAY_INTERVAL)
this.size = 0
this.open = null
this.close = null
}
offset(offset: number) {
this.from += offset
this.to = this.from + this.length
}
add(id: number | null, from: number, to: number) {
this.tree = undefined
if (to > this.to) {
this.to = to
this.length = to - this.from
}
// make token relative to chunk position
from -= this.from
to -= this.from
if (id !== null) {
// resize token array if needed
if (this.size * 3 + 3 > this.tokens.length) {
const old = this.tokens
this.tokens = new Int16Array(this.tokens.length + CHUNK_ARRAY_INTERVAL)
this.tokens.set(old)
}
const idx = this.size * 3
this.tokens[idx] = id
this.tokens[idx + 1] = from
this.tokens[idx + 2] = to
this.size++
}
}
pushOpen(...ids: number[]) {
this.tree = undefined
this.open ??= []
this.open.push(...ids)
}
pushClose(...ids: number[]) {
this.tree = undefined
this.close ??= []
this.close.push(...ids)
}
tryForTree(nodeSet: NodeSet) {
if (this.tree) return this.tree
if (this.size <= 1) return null
if (this.open || this.close) {
if (!(this.open && this.close)) return null
if (this.open.length !== this.close.length) return null
const open = this.open.slice().reverse()
const close = this.close
for (let i = 0; i < open.length; i++) {
if (open[i] !== close[i]) return null
}
}
// if we're here, we can make a tree buffer out of this chunk
const buffer: number[] = []
const total = this.size * 4 + (this.open?.length ?? 0) * 4
if (this.open) {
for (let i = this.open.length - 1; i >= 0; i--) {
buffer.push(this.open[i], 0, this.length, total)
}
}
for (let i = 0; i < this.size; i++) {
buffer.push(
this.tokens[i * 3],
this.tokens[i * 3 + 1],
this.tokens[i * 3 + 2],
buffer.length + 4
)
}
const tree = new TreeBuffer(new Uint16Array(buffer), this.length, nodeSet)
this.tree = tree as unknown as Tree
return tree
}
/**
* Determines if a grammar's state (and parse position) is compatible
* with reusing this node. This is only a safe determination if it is
* made _after_ the changed range of the document.
*
* @param state - The state to compare against.
* @param pos - The position to compare against.
* @param offset - The edit offset, to correct for chunk position differences.
*/
isReusable(state: GrammarState, pos: number, offset = 0) {
if (this.from + offset !== pos) return false
if (!state.equals(this.state)) return false
return true
}
}