From 8d18823608a79b12bd202565fa54a88256c15e84 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 11 May 2025 17:59:44 +0300 Subject: [PATCH] chore(code): support multiple selections in tab --- .../codemirror/src/extensions/custom_tab.ts | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/codemirror/src/extensions/custom_tab.ts b/packages/codemirror/src/extensions/custom_tab.ts index ae99e90a6..9ba602010 100644 --- a/packages/codemirror/src/extensions/custom_tab.ts +++ b/packages/codemirror/src/extensions/custom_tab.ts @@ -1,38 +1,45 @@ import { indentLess, indentMore } from "@codemirror/commands"; +import { EditorSelection, type ChangeSpec } from "@codemirror/state"; import type { KeyBinding } from "@codemirror/view"; const smartIndentWithTab: KeyBinding[] = [ { key: "Tab", - run({ state, dispatch}) { + run({ state, dispatch }) { const { selection } = state; - for (const range of selection.ranges) { - if (!range.empty) { - // Allow default behaviour. - return false; - } + // Handle selection indenting normally + if (selection.ranges.some(range => !range.empty)) { + return indentMore({ state, dispatch }); + } + const changes = []; + const newSelections = []; + + for (let range of selection.ranges) { const line = state.doc.lineAt(range.head); const beforeCursor = state.doc.sliceString(line.from, range.head); if (/^\s*$/.test(beforeCursor)) { - // Only whitespace before cursor: indent line - return indentMore({state, dispatch}); + // Only whitespace before cursor → indent line + return indentMore({ state, dispatch }); } else { - // Insert a tab character - const cursor = range.head; - dispatch(state.update({ - changes: { - from: cursor, - insert: "\t" - }, - selection: { anchor: cursor + 1 }, + // Insert a tab character at cursor + changes.push({ from: range.head, to: range.head, insert: "\t" }); + newSelections.push(EditorSelection.cursor(range.head + 1)); + } + } + + if (changes.length) { + dispatch( + state.update({ + changes, + selection: EditorSelection.create(newSelections), scrollIntoView: true, userEvent: "input" - })); - return true; - } + }) + ); + return true; } return false;