UNPKG

cm-tarnation

Version:

An alternative parser for CodeMirror 6

160 lines 5.48 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 { CompletionContext } from "@codemirror/autocomplete"; import { NodeTypeProp } from "../grammar/node"; /** * An extended form of CodeMirror's `CompletionContext` provided to * Tarnation autocomplete handlers. */ export class TarnationCompletionContext extends CompletionContext { /** * Mutates a CodeMirror `CompletionContext` into a * {@link TarnationCompletionContext}. * * @param context - The `CompletionContext` to mutate. * @param type - The {@link Node} type. * @param tree - The syntax tree of the editor. * @param node - The node at the current position. * @param traversed - Set to `true` if the completion handler should be * called via the parent chain. */ static mutate(context, type, tree, node, traversed) { if (Object.getPrototypeOf(context) === this.prototype) { return context; } const mutated = Object.setPrototypeOf(context, this.prototype); mutated.type = type; mutated.tree = tree; mutated.node = node; mutated.traversed = traversed; return mutated; } /** The `SyntaxNode` surrounding the current position. */ get around() { if (this._around !== undefined) return this._around; const node = this.tree.resolve(this.pos); if (!node) return null; this._around = node; return node; } /** The {@link Node} type of the {@link around} node. */ get aroundType() { return this.grammarType(this.around); } /** The starting position of the current node. */ get from() { return this.node.from; } /** The ending positon of the current node. */ get to() { return this.node.to; } /** The string of text covered by the current node. */ get text() { if (this._text !== undefined) return this._text; const result = this.state.sliceDoc(this.node.from, this.node.to); this._text = result; return result; } /** * Gets the {@link Node} type of a `SyntaxNode`. * * @param node - The `SyntaxNode` to get the type from. */ grammarType(node) { const type = node?.type.prop(NodeTypeProp); return type ?? null; } /** * Gets the string of text covered by a `SyntaxNode`. * * @param node - The `SyntaxNode` to get the text from. */ textOf(node) { if (!node) return null; return this.state.sliceDoc(node.from, node.to); } /** * Gets the parents of a `SyntaxNode`. * * @param node - The `SyntaxNode` to get the parents from. * @param max - The maximum number of parents to traverse upwards for. */ parentsOf(node, max) { if (!node) return []; const parents = []; let dist = 0; for (let cur = node.parent; cur; cur = cur.parent) { dist++; parents.push(cur); if (max && max > 0 && dist >= max) break; } return parents; } /** * Finds the first parent of a `SyntaxNode` that matches the given name(s). * * @param node - The `SyntaxNode` to find a parent for. * @param parent - The name(s) of the parent to find. * @param max - The maximum number of parents to traverse upwards for. */ findParentOf(node, parent, max) { if (!node) return null; if (typeof parent === "string") parent = [parent]; let dist = 0; for (let cur = node.parent; cur; cur = cur.parent) { dist++; if (parent.includes(cur.name)) return cur; if (max && max > 0 && dist >= max) break; } return null; } /** * Shorthand for using {@link findParentsOf} on the current node. * * @param parent - The name(s) of the parent to find. * @param max - The maximum number of parents to traverse upwards for. */ parent(parent, max) { return this.findParentOf(this.node, parent, max); } /** * Shorthand for running {@link parentsOf} on the current node. * * @param level - The maximum number of parents to get. */ parents(level = 1) { if (level <= 0) return null; const parents = this.parentsOf(this.node, level); return parents[parents.length - 1] ?? null; } /** * Checks if the current position is inbetween the two given nodes. * * @param left - The left node to check for. * @param right - The right node to check for. Defaults to the same as * the `left` node. */ isInbetween(left, right = left) { const leftNode = this.tree.resolve(this.pos, 1); const rightNode = this.tree.resolve(this.pos, -1); if (!leftNode || !rightNode) return false; if (leftNode.name !== left || rightNode.name !== right) return false; return true; } } //# sourceMappingURL=context.js.map