Auto-close delimiters and wrap selected text

This commit is contained in:
2026-09-05 22:07:19 -04:00
parent 7ab71fc843
commit 9d3966772b
2 changed files with 58 additions and 1 deletions
+3 -1
View File
@@ -14,6 +14,7 @@
import { page } from '$app/stores'; import { page } from '$app/stores';
import { LSPClient, languageServerExtensions } from "@codemirror/lsp-client"; import { LSPClient, languageServerExtensions } from "@codemirror/lsp-client";
import { setDiagnostics, lintGutter } from '@codemirror/lint'; import { setDiagnostics, lintGutter } from '@codemirror/lint';
import { bracketExtensions, typstBracketSettings } from '../ts/editor-brackets';
let { let {
ytext = undefined, ytext = undefined,
@@ -168,7 +169,7 @@
); );
const isToml = (filePath ?? '').toLowerCase().endsWith('.toml'); const isToml = (filePath ?? '').toLowerCase().endsWith('.toml');
const languageExtension = isToml ? StreamLanguage.define(toml) : myLang; const languageExtension = isToml ? StreamLanguage.define(toml) : [myLang, typstBracketSettings(myLang)];
const completionExtensions = isToml ? [] : [autocompletion({ override: [typstCompletions] })]; const completionExtensions = isToml ? [] : [autocompletion({ override: [typstCompletions] })];
state = EditorState.create({ state = EditorState.create({
@@ -177,6 +178,7 @@
lineNumbers(), lineNumbers(),
lintGutter(), lintGutter(),
history(), history(),
...bracketExtensions,
keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab] as any), keymap.of([...defaultKeymap, ...historyKeymap, indentWithTab] as any),
languageExtension, languageExtension,
yCollab(activeText, activeProvider.awareness), yCollab(activeText, activeProvider.awareness),
+55
View File
@@ -0,0 +1,55 @@
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
import { EditorView, keymap } from '@codemirror/view';
import { EditorSelection, Prec } from '@codemirror/state';
import type { Language } from '@codemirror/language';
const autoClosedDelimiters = ['(', '[', '{', '"', '$'];
const charactersAllowedAfterOpening = ')]}:;>,.$';
const markupDelimiters: Record<string, string> = {
'*': '*',
_: '_',
'`': '`',
'<': '>'
};
export function typstBracketSettings(language: Language) {
return language.data.of({
closeBrackets: {
brackets: autoClosedDelimiters,
before: charactersAllowedAfterOpening
}
});
}
function surroundSelection(view: EditorView, typedText: string) {
const closingText = markupDelimiters[typedText];
if (!closingText) return false;
const state = view.state;
if (state.readOnly || state.selection.ranges.every((range) => range.empty)) return false;
const changes = state.changeByRange((range) => {
if (range.empty) return { range };
const shift = typedText.length;
return {
changes: [
{ from: range.from, insert: typedText },
{ from: range.to, insert: closingText }
],
range: EditorSelection.range(range.anchor + shift, range.head + shift)
};
});
view.dispatch(state.update(changes, { scrollIntoView: true, userEvent: 'input.type' }));
return true;
}
export const bracketExtensions = [
closeBrackets(),
Prec.high(
EditorView.inputHandler.of((view, _from, _to, typedText) => surroundSelection(view, typedText))
),
keymap.of(closeBracketsKeymap)
];