Hey Marijn, great project you have here. I am trying to create a button that will fold all code levels. The core of my function is here:
function foldAllRecursive(view: EditorView) {
// Traverse the syntax tree and collect all foldable ranges
const foldRanges: { from: number, to: number }[] = [];
syntaxTree(view.state).iterate({
enter(node) {
const from = node.from;
const to = node.to;
if (foldable(view.state, from, to)) {
foldRanges.push({ from, to });
}
}
});
view.dispatch({
effects: foldRanges.map(range => foldEffect.of({ from: range.from, to: range.to }))
});
}
When I run this, it finds a bunch of lines that it can fold, it changes the gutter from \/ to >, and it adds ellipses to the page, but the content of the page is not folded.
If I use foldAll(view)
then it does modify the page content as expected, but this seems to only fold the top-level code. I want to fold everything and then unfold it as necessary.
The full code for a basic example is below. I’m still fairly new to webdev, I tried following this example and I feel like I’m really close but perhaps missing something basic?
import { python } from '@codemirror/lang-python';
import { EditorState } from '@codemirror/state';
import { useEffect, useRef } from 'react';
import {
EditorView,
lineNumbers,
} from '@codemirror/view';
import {
codeFolding,
foldGutter,
unfoldAll,
syntaxTree,
foldable,
foldEffect,
foldAll,
} from '@codemirror/language';
export const useCodeMirror = (userOptions: UserOptions) => {
const mainViewRef = useRef<EditorView | null>(null);
const mainStateRef = useRef<EditorState | null>(null);
// Create main state
useEffect(() => {
if (!mainStateRef.current) {
mainStateRef.current = EditorState.create({
doc: userOptions.workbenchCode,
extensions: [
python(),
codeFolding(),
lineNumbers(),
foldGutter(),
],
});
}
}, [userOptions]);
// Create main view
useEffect(() => {
if (!mainViewRef.current && mainStateRef.current) {
mainViewRef.current = new EditorView({
state: mainStateRef.current,
parent: document.getElementById('custom-editor') as HTMLElement,
});
}
}, []);
return mainViewRef;
};
export const handleFoldClick = (
setIsFolded: React.Dispatch<React.SetStateAction<boolean>>,
editorViewRef: React.MutableRefObject<EditorView | null>,
) => {
setIsFolded(prevValue => {
if (editorViewRef.current) {
if (prevValue) {
unfoldAll(editorViewRef.current);
} else {
// foldAll(editorViewRef.current)
foldAllRecursive(editorViewRef.current);
}
} else {
console.error('CodeMirror editor not found');
}
return !prevValue;
});
};
// Function to fold all levels of code
function foldAllRecursive(view: EditorView) {
// Traverse the syntax tree and collect all foldable ranges
const foldRanges: { from: number, to: number }[] = [];
syntaxTree(view.state).iterate({
enter(node) {
const from = node.from;
const to = node.to;
if (foldable(view.state, from, to)) {
foldRanges.push({ from, to });
}
}
});
view.dispatch({
effects: foldRanges.map(range => foldEffect.of({ from: range.from, to: range.to }))
});
}