Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit 56b7965

Browse files
committed
refactor(codemirror): split custom tab functionality into more methods
1 parent 9e3909a commit 56b7965

File tree

1 file changed

+58
-46
lines changed

1 file changed

+58
-46
lines changed
Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { indentLess, indentMore } from "@codemirror/commands";
2-
import { EditorSelection, EditorState, SelectionRange, type ChangeSpec } from "@codemirror/state";
2+
import { EditorSelection, EditorState, SelectionRange, type Transaction, type ChangeSpec } from "@codemirror/state";
33
import type { KeyBinding } from "@codemirror/view";
44

55
/**
@@ -19,8 +19,6 @@ const smartIndentWithTab: KeyBinding[] = [
1919
}
2020

2121
const { selection } = state;
22-
const changes: ChangeSpec[] = [];
23-
const newSelections: SelectionRange[] = [];
2422

2523
// Step 1: Handle non-empty selections → replace with tab
2624
if (selection.ranges.some(range => !range.empty)) {
@@ -40,55 +38,69 @@ const smartIndentWithTab: KeyBinding[] = [
4038
// Multiple lines are selected, indent each line.
4139
return indentMore({ state, dispatch });
4240
} else {
43-
// Single line selection, replace with tab.
44-
for (let range of selection.ranges) {
45-
changes.push({ from: range.from, to: range.to, insert: "\t" });
46-
newSelections.push(EditorSelection.cursor(range.from + 1));
47-
}
48-
49-
dispatch(
50-
state.update({
51-
changes,
52-
selection: EditorSelection.create(newSelections),
53-
scrollIntoView: true,
54-
userEvent: "input"
55-
})
56-
);
41+
return handleSingleLineSelection(state, dispatch);
5742
}
58-
59-
return true;
6043
}
6144

6245
// Step 2: Handle empty selections
63-
for (let range of selection.ranges) {
64-
const line = state.doc.lineAt(range.head);
65-
const beforeCursor = state.doc.sliceString(line.from, range.head);
66-
67-
if (/^\s*$/.test(beforeCursor)) {
68-
// Only whitespace before cursor → indent line
69-
return indentMore({ state, dispatch });
70-
} else {
71-
// Insert tab character at cursor
72-
changes.push({ from: range.head, to: range.head, insert: "\t" });
73-
newSelections.push(EditorSelection.cursor(range.head + 1));
74-
}
75-
}
76-
77-
if (changes.length) {
78-
dispatch(
79-
state.update({
80-
changes,
81-
selection: EditorSelection.create(newSelections),
82-
scrollIntoView: true,
83-
userEvent: "input"
84-
})
85-
);
86-
return true;
87-
}
88-
89-
return false;
46+
return handleEmptySelections(state, dispatch);
9047
},
9148
shift: indentLess
9249
},
9350
]
9451
export default smartIndentWithTab;
52+
53+
function handleSingleLineSelection(state: EditorState, dispatch: (transaction: Transaction) => void) {
54+
const changes: ChangeSpec[] = [];
55+
const newSelections: SelectionRange[] = [];
56+
57+
// Single line selection, replace with tab.
58+
for (let range of state.selection.ranges) {
59+
changes.push({ from: range.from, to: range.to, insert: "\t" });
60+
newSelections.push(EditorSelection.cursor(range.from + 1));
61+
}
62+
63+
dispatch(
64+
state.update({
65+
changes,
66+
selection: EditorSelection.create(newSelections),
67+
scrollIntoView: true,
68+
userEvent: "input"
69+
})
70+
);
71+
72+
return true;
73+
}
74+
75+
function handleEmptySelections(state: EditorState, dispatch: (transaction: Transaction) => void) {
76+
const changes: ChangeSpec[] = [];
77+
const newSelections: SelectionRange[] = [];
78+
79+
for (let range of state.selection.ranges) {
80+
const line = state.doc.lineAt(range.head);
81+
const beforeCursor = state.doc.sliceString(line.from, range.head);
82+
83+
if (/^\s*$/.test(beforeCursor)) {
84+
// Only whitespace before cursor → indent line
85+
return indentMore({ state, dispatch });
86+
} else {
87+
// Insert tab character at cursor
88+
changes.push({ from: range.head, to: range.head, insert: "\t" });
89+
newSelections.push(EditorSelection.cursor(range.head + 1));
90+
}
91+
}
92+
93+
if (changes.length) {
94+
dispatch(
95+
state.update({
96+
changes,
97+
selection: EditorSelection.create(newSelections),
98+
scrollIntoView: true,
99+
userEvent: "input"
100+
})
101+
);
102+
return true;
103+
}
104+
105+
return false;
106+
}

0 commit comments

Comments
 (0)