Skip to content

Instantly share code, notes, and snippets.

@marcuswestin
Created September 20, 2021 16:21
Show Gist options
  • Save marcuswestin/89e51bdf95ada35d4ad1b6532ee5f0ef to your computer and use it in GitHub Desktop.
Save marcuswestin/89e51bdf95ada35d4ad1b6532ee5f0ef to your computer and use it in GitHub Desktop.
Example of content-based code-folding that works
import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
import { StateField } from "@codemirror/state";
import { Line } from "@codemirror/text";
import { Decoration, DecorationSet, WidgetType } from "@codemirror/view";
let doc = `
Test list 1
- Test item
- Test item
[FOLD] - Test list 2
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test item
- Test list 3
- Test item
`
function makeDecarationSet(state: EditorState) {
let doc = state.doc
let lineNumber = 1
let decorations = []
while (lineNumber <= doc.lines) {
let line = doc.line(lineNumber)
if (line.text.startsWith('[FOLD]')) {
let foldRangeStart = line.to
while (lineNumber <= doc.lines) {
line = doc.line(lineNumber)
if (isWhitespaceOnly(line)) {
let foldRangeStop = line.to
console.log(foldRangeStart, foldRangeStop)
decorations.push(hideDecoration.range(foldRangeStart, foldRangeStop))
break
}
lineNumber += 1
}
}
lineNumber += 1
}
return Decoration.set(decorations)
}
function isWhitespaceOnly(line: Line) {
return line.text.match(/^\w*$/)
}
const decorateFoldedContentStateField = StateField.define<DecorationSet>({
create(state) {
return makeDecarationSet(state)
},
update(field, tr) {
return makeDecarationSet(tr.state)
},
provide: f => EditorView.decorations.from(f),
})
let hideDecoration = Decoration.replace({
widget: new class extends WidgetType {
toDOM(view: EditorView) {
let element = document.createElement('span')
element.textContent = '…'
element.setAttribute('aria-label', state.phrase('folded code'))
element.title = state.phrase('unfold')
element.className = 'cm-foldPlaceholder'
return element
}
}
})
let state = EditorState.create({doc, extensions: [
decorateFoldedContentStateField,
basicSetup,
]})
let view = new EditorView({state, parent: document.querySelector("#editor")!})
;(window as any).view = view
view.focus()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment