UNPKG

cm-tarnation

Version:

An alternative parser for CodeMirror 6

134 lines (111 loc) 3.72 kB
/* 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 } }