11import { 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" ;
33import 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]
9451export 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