From 299c4d93597b3065e6a1017ebe692cde66fc5e39 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Mon, 11 Oct 2021 01:03:21 +0200 Subject: feat(web): experiment with Lezer parser --- language-web/src/main/js/editor/folding.ts | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 language-web/src/main/js/editor/folding.ts (limited to 'language-web/src/main/js/editor/folding.ts') diff --git a/language-web/src/main/js/editor/folding.ts b/language-web/src/main/js/editor/folding.ts new file mode 100644 index 00000000..54c7294d --- /dev/null +++ b/language-web/src/main/js/editor/folding.ts @@ -0,0 +1,97 @@ +import { EditorState } from '@codemirror/state'; +import type { SyntaxNode } from '@lezer/common'; + +export type FoldRange = { from: number, to: number }; + +/** + * Folds a block comment between its delimiters. + * + * @param node the node to fold + * @returns the folding range or `null` is there is nothing to fold + */ +export function foldBlockComment(node: SyntaxNode): FoldRange { + return { + from: node.from + 2, + to: node.to - 2, + }; +} + +/** + * Folds a declaration after the first element if it appears on the opening line, + * otherwise folds after the opening keyword. + * + * @example + * First element on the opening line: + * ``` + * scope Family = 1, + * Person += 5..10. + * ``` + * becomes + * ``` + * scope Family = 1,[...]. + * ``` + * + * @example + * First element not on the opening line: + * ``` + * scope Family + * = 1, + * Person += 5..10. + * ``` + * becomes + * ``` + * scope [...]. + * ``` + * + * @param node the node to fold + * @param state the editor state + * @returns the folding range or `null` is there is nothing to fold + */ +export function foldDeclaration(node: SyntaxNode, state: EditorState): FoldRange | null { + const { firstChild: open, lastChild: close } = node; + if (open === null || close === null) { + return null; + } + const { cursor } = open; + const lineEnd = state.doc.lineAt(open.from).to; + let foldFrom = open.to; + while (cursor.next() && cursor.from < lineEnd) { + if (cursor.type.name === ',') { + foldFrom = cursor.to; + break; + } + } + return { + from: foldFrom, + to: close.from, + }; +} + +/** + * Folds a node only if it has at least one sibling of the same type. + * + * The folding range will be the entire `node`. + * + * @param node the node to fold + * @returns the folding range or `null` is there is nothing to fold + */ +export function foldConjunction(node: SyntaxNode): FoldRange | null { + const { parent } = node; + if (parent === null) { + return null; + } + const { cursor } = parent; + let nConjunctions = 0; + while (cursor.next()) { + if (cursor.type === node.type) { + nConjunctions += 1; + } + if (nConjunctions >= 2) { + return { + from: node.from, + to: node.to, + }; + } + } + return null; +} -- cgit v1.2.3-70-g09d2