Auto-close delimiters and wrap selected text
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user