aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js/editor/folding.ts
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-11 01:03:21 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-10-31 19:26:10 +0100
commit299c4d93597b3065e6a1017ebe692cde66fc5e39 (patch)
tree57d634cec30ca37f0af81a37c3011e027589c35c /language-web/src/main/js/editor/folding.ts
parentfeat(web): add CodeMirror 6 editor (diff)
downloadrefinery-299c4d93597b3065e6a1017ebe692cde66fc5e39.tar.gz
refinery-299c4d93597b3065e6a1017ebe692cde66fc5e39.tar.zst
refinery-299c4d93597b3065e6a1017ebe692cde66fc5e39.zip
feat(web): experiment with Lezer parser
Diffstat (limited to 'language-web/src/main/js/editor/folding.ts')
-rw-r--r--language-web/src/main/js/editor/folding.ts97
1 files changed, 97 insertions, 0 deletions
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 @@
1import { EditorState } from '@codemirror/state';
2import type { SyntaxNode } from '@lezer/common';
3
4export type FoldRange = { from: number, to: number };
5
6/**
7 * Folds a block comment between its delimiters.
8 *
9 * @param node the node to fold
10 * @returns the folding range or `null` is there is nothing to fold
11 */
12export function foldBlockComment(node: SyntaxNode): FoldRange {
13 return {
14 from: node.from + 2,
15 to: node.to - 2,
16 };
17}
18
19/**
20 * Folds a declaration after the first element if it appears on the opening line,
21 * otherwise folds after the opening keyword.
22 *
23 * @example
24 * First element on the opening line:
25 * ```
26 * scope Family = 1,
27 * Person += 5..10.
28 * ```
29 * becomes
30 * ```
31 * scope Family = 1,[...].
32 * ```
33 *
34 * @example
35 * First element not on the opening line:
36 * ```
37 * scope Family
38 * = 1,
39 * Person += 5..10.
40 * ```
41 * becomes
42 * ```
43 * scope [...].
44 * ```
45 *
46 * @param node the node to fold
47 * @param state the editor state
48 * @returns the folding range or `null` is there is nothing to fold
49 */
50export function foldDeclaration(node: SyntaxNode, state: EditorState): FoldRange | null {
51 const { firstChild: open, lastChild: close } = node;
52 if (open === null || close === null) {
53 return null;
54 }
55 const { cursor } = open;
56 const lineEnd = state.doc.lineAt(open.from).to;
57 let foldFrom = open.to;
58 while (cursor.next() && cursor.from < lineEnd) {
59 if (cursor.type.name === ',') {
60 foldFrom = cursor.to;
61 break;
62 }
63 }
64 return {
65 from: foldFrom,
66 to: close.from,
67 };
68}
69
70/**
71 * Folds a node only if it has at least one sibling of the same type.
72 *
73 * The folding range will be the entire `node`.
74 *
75 * @param node the node to fold
76 * @returns the folding range or `null` is there is nothing to fold
77 */
78export function foldConjunction(node: SyntaxNode): FoldRange | null {
79 const { parent } = node;
80 if (parent === null) {
81 return null;
82 }
83 const { cursor } = parent;
84 let nConjunctions = 0;
85 while (cursor.next()) {
86 if (cursor.type === node.type) {
87 nConjunctions += 1;
88 }
89 if (nConjunctions >= 2) {
90 return {
91 from: node.from,
92 to: node.to,
93 };
94 }
95 }
96 return null;
97}