Skip to content

Commit f7c9ab4

Browse files
committed
add folding
1 parent 53318b4 commit f7c9ab4

File tree

2 files changed

+76
-3
lines changed

2 files changed

+76
-3
lines changed

packages/codemirror-lang/index.js

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,89 @@
1+
/** @typedef {import('@lezer/common').SyntaxNode} SyntaxNode */
2+
13
import {
4+
defineLanguageFacet,
5+
foldNodeProp,
6+
foldService,
27
Language,
38
LanguageSupport,
4-
defineLanguageFacet,
9+
syntaxTree,
510
} from '@codemirror/language'
6-
import { parser, tags as t } from '@orgajs/lezer'
11+
import { NodeProp } from '@lezer/common'
12+
import { parser as baseParser, tags as t } from '@orgajs/lezer'
713

814
export const tags = t
915

1016
const data = defineLanguageFacet({})
1117

18+
// --- folding ---
19+
/** @type {NodeProp<number>} */
20+
const headlineProp = new NodeProp()
21+
22+
/**
23+
* @param {import('@lezer/common').NodeType} type
24+
*/
25+
function isHeadline(type) {
26+
let match = /^headline(\d)$/.exec(type.name)
27+
return match ? +match[1] : undefined
28+
}
29+
30+
/**
31+
* @param {SyntaxNode} headerNode
32+
* @param {number} level
33+
*/
34+
function findSectionEnd(headerNode, level) {
35+
let last = headerNode
36+
for (;;) {
37+
let next = last.nextSibling
38+
if (!next) break
39+
const headline = isHeadline(next.type)
40+
if (headline != null) {
41+
if (headline <= level) {
42+
return next.from - 1 // escape the newline. TODO: is this safe?
43+
}
44+
}
45+
last = next
46+
}
47+
return last.to
48+
}
49+
50+
const headerIndent = foldService.of((state, start, end) => {
51+
for (
52+
let /** @type {SyntaxNode | null} */ node = syntaxTree(state).resolveInner(
53+
end,
54+
-1
55+
);
56+
node;
57+
node = node.parent
58+
) {
59+
if (node.from < start) break
60+
let headline = node.type.prop(headlineProp)
61+
if (headline == null) continue
62+
let upto = findSectionEnd(node, headline)
63+
if (upto > end) return { from: end, to: upto }
64+
}
65+
return null
66+
})
67+
68+
const parser = baseParser.configure({
69+
props: [
70+
foldNodeProp.add((type) => {
71+
const m = isHeadline(type)
72+
if (!m) return undefined
73+
return (tree, state) => ({
74+
from: state.doc.lineAt(tree.from).to,
75+
to: state.doc.lineAt(tree.from).to,
76+
})
77+
}),
78+
headlineProp.add(isHeadline),
79+
],
80+
})
81+
1282
/**
1383
* @param {any} parser
1484
*/
1585
function mkLang(parser) {
16-
return new Language(data, parser, [], 'org')
86+
return new Language(data, parser, [headerIndent], 'org')
1787
}
1888

1989
export function org() {

packages/codemirror-lang/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@
3030
"dependencies": {
3131
"@codemirror/language": "^6.9.0",
3232
"@orgajs/lezer": "workspace:^"
33+
},
34+
"devDependencies": {
35+
"@lezer/common": "^1.1.0"
3336
}
3437
}

0 commit comments

Comments
 (0)