@@ -16,6 +16,7 @@ export interface IShiftCommandOpts {
1616 isUnshift : boolean ;
1717 tabSize : number ;
1818 oneIndent : string ;
19+ useTabStops : boolean ;
1920}
2021
2122export class ShiftCommand implements ICommand {
@@ -52,20 +53,16 @@ export class ShiftCommand implements ICommand {
5253 }
5354
5455 public getEditOperations ( model : ITokenizedModel , builder : IEditOperationBuilder ) : void {
55- let startLine = this . _selection . startLineNumber ;
56- let endLine = this . _selection . endLineNumber ;
56+ const startLine = this . _selection . startLineNumber ;
5757
58+ let endLine = this . _selection . endLineNumber ;
5859 if ( this . _selection . endColumn === 1 && startLine !== endLine ) {
5960 endLine = endLine - 1 ;
6061 }
6162
62- let lineNumber : number ,
63- tabSize = this . _opts . tabSize ,
64- oneIndent = this . _opts . oneIndent ,
65- shouldIndentEmptyLines = ( startLine === endLine ) ;
66-
67- // indents[i] represents i * oneIndent
68- let indents : string [ ] = [ '' , oneIndent ] ;
63+ const tabSize = this . _opts . tabSize ;
64+ const oneIndent = this . _opts . oneIndent ;
65+ const shouldIndentEmptyLines = ( startLine === endLine ) ;
6966
7067 // if indenting or outdenting on a whitespace only line
7168 if ( this . _selection . isEmpty ( ) ) {
@@ -74,79 +71,126 @@ export class ShiftCommand implements ICommand {
7471 }
7572 }
7673
77- // keep track of previous line's "miss-alignment"
78- let previousLineExtraSpaces = 0 , extraSpaces = 0 ;
79- for ( lineNumber = startLine ; lineNumber <= endLine ; lineNumber ++ , previousLineExtraSpaces = extraSpaces ) {
80- extraSpaces = 0 ;
81- let lineText = model . getLineContent ( lineNumber ) ;
82- let indentationEndIndex = strings . firstNonWhitespaceIndex ( lineText ) ;
74+ if ( this . _opts . useTabStops ) {
75+ // indents[i] represents i * oneIndent
76+ let indents : string [ ] = [ '' , oneIndent ] ;
8377
84- if ( this . _opts . isUnshift && ( lineText . length === 0 || indentationEndIndex === 0 ) ) {
85- // empty line or line with no leading whitespace => nothing to do
86- continue ;
87- }
78+ // keep track of previous line's "miss-alignment"
79+ let previousLineExtraSpaces = 0 , extraSpaces = 0 ;
80+ for ( let lineNumber = startLine ; lineNumber <= endLine ; lineNumber ++ , previousLineExtraSpaces = extraSpaces ) {
81+ extraSpaces = 0 ;
82+ let lineText = model . getLineContent ( lineNumber ) ;
83+ let indentationEndIndex = strings . firstNonWhitespaceIndex ( lineText ) ;
8884
89- if ( ! shouldIndentEmptyLines && ! this . _opts . isUnshift && lineText . length === 0 ) {
90- // do not indent empty lines => nothing to do
91- continue ;
92- }
85+ if ( this . _opts . isUnshift && ( lineText . length === 0 || indentationEndIndex === 0 ) ) {
86+ // empty line or line with no leading whitespace => nothing to do
87+ continue ;
88+ }
9389
94- if ( indentationEndIndex === - 1 ) {
95- // the entire line is whitespace
96- indentationEndIndex = lineText . length ;
97- }
90+ if ( ! shouldIndentEmptyLines && ! this . _opts . isUnshift && lineText . length === 0 ) {
91+ // do not indent empty lines => nothing to do
92+ continue ;
93+ }
9894
99- if ( lineNumber > 1 ) {
100- let contentStartVisibleColumn = CursorColumns . visibleColumnFromColumn ( lineText , indentationEndIndex + 1 , tabSize ) ;
101- if ( contentStartVisibleColumn % tabSize !== 0 ) {
102- // The current line is "miss-aligned", so let's see if this is expected...
103- // This can only happen when it has trailing commas in the indent
104- let enterAction = LanguageConfigurationRegistry . getRawEnterActionAtPosition ( model , lineNumber - 1 , model . getLineMaxColumn ( lineNumber - 1 ) ) ;
105- if ( enterAction ) {
106- extraSpaces = previousLineExtraSpaces ;
107- if ( enterAction . appendText ) {
108- for ( let j = 0 , lenJ = enterAction . appendText . length ; j < lenJ && extraSpaces < tabSize ; j ++ ) {
109- if ( enterAction . appendText . charCodeAt ( j ) === CharCode . Space ) {
110- extraSpaces ++ ;
111- } else {
112- break ;
95+ if ( indentationEndIndex === - 1 ) {
96+ // the entire line is whitespace
97+ indentationEndIndex = lineText . length ;
98+ }
99+
100+ if ( lineNumber > 1 ) {
101+ let contentStartVisibleColumn = CursorColumns . visibleColumnFromColumn ( lineText , indentationEndIndex + 1 , tabSize ) ;
102+ if ( contentStartVisibleColumn % tabSize !== 0 ) {
103+ // The current line is "miss-aligned", so let's see if this is expected...
104+ // This can only happen when it has trailing commas in the indent
105+ let enterAction = LanguageConfigurationRegistry . getRawEnterActionAtPosition ( model , lineNumber - 1 , model . getLineMaxColumn ( lineNumber - 1 ) ) ;
106+ if ( enterAction ) {
107+ extraSpaces = previousLineExtraSpaces ;
108+ if ( enterAction . appendText ) {
109+ for ( let j = 0 , lenJ = enterAction . appendText . length ; j < lenJ && extraSpaces < tabSize ; j ++ ) {
110+ if ( enterAction . appendText . charCodeAt ( j ) === CharCode . Space ) {
111+ extraSpaces ++ ;
112+ } else {
113+ break ;
114+ }
113115 }
114116 }
115- }
116- if ( enterAction . removeText ) {
117- extraSpaces = Math . max ( 0 , extraSpaces - enterAction . removeText ) ;
118- }
117+ if ( enterAction . removeText ) {
118+ extraSpaces = Math . max ( 0 , extraSpaces - enterAction . removeText ) ;
119+ }
119120
120- // Act as if `prefixSpaces` is not part of the indentation
121- for ( let j = 0 ; j < extraSpaces ; j ++ ) {
122- if ( indentationEndIndex === 0 || lineText . charCodeAt ( indentationEndIndex - 1 ) !== CharCode . Space ) {
123- break ;
121+ // Act as if `prefixSpaces` is not part of the indentation
122+ for ( let j = 0 ; j < extraSpaces ; j ++ ) {
123+ if ( indentationEndIndex === 0 || lineText . charCodeAt ( indentationEndIndex - 1 ) !== CharCode . Space ) {
124+ break ;
125+ }
126+ indentationEndIndex -- ;
124127 }
125- indentationEndIndex -- ;
126128 }
127129 }
128130 }
129- }
130131
131132
132- if ( this . _opts . isUnshift && indentationEndIndex === 0 ) {
133- // line with no leading whitespace => nothing to do
134- continue ;
135- }
133+ if ( this . _opts . isUnshift && indentationEndIndex === 0 ) {
134+ // line with no leading whitespace => nothing to do
135+ continue ;
136+ }
136137
137- let desiredIndentCount : number ;
138- if ( this . _opts . isUnshift ) {
139- desiredIndentCount = ShiftCommand . unshiftIndentCount ( lineText , indentationEndIndex + 1 , tabSize ) ;
140- } else {
141- desiredIndentCount = ShiftCommand . shiftIndentCount ( lineText , indentationEndIndex + 1 , tabSize ) ;
142- }
138+ let desiredIndentCount : number ;
139+ if ( this . _opts . isUnshift ) {
140+ desiredIndentCount = ShiftCommand . unshiftIndentCount ( lineText , indentationEndIndex + 1 , tabSize ) ;
141+ } else {
142+ desiredIndentCount = ShiftCommand . shiftIndentCount ( lineText , indentationEndIndex + 1 , tabSize ) ;
143+ }
143144
144- // Fill `indents`, as needed
145- for ( let j = indents . length ; j <= desiredIndentCount ; j ++ ) {
146- indents [ j ] = indents [ j - 1 ] + oneIndent ;
145+ // Fill `indents`, as needed
146+ for ( let j = indents . length ; j <= desiredIndentCount ; j ++ ) {
147+ indents [ j ] = indents [ j - 1 ] + oneIndent ;
148+ }
149+
150+ builder . addEditOperation ( new Range ( lineNumber , 1 , lineNumber , indentationEndIndex + 1 ) , indents [ desiredIndentCount ] ) ;
147151 }
152+ } else {
153+
154+ for ( let lineNumber = startLine ; lineNumber <= endLine ; lineNumber ++ ) {
155+ const lineText = model . getLineContent ( lineNumber ) ;
156+ let indentationEndIndex = strings . firstNonWhitespaceIndex ( lineText ) ;
148157
149- builder . addEditOperation ( new Range ( lineNumber , 1 , lineNumber , indentationEndIndex + 1 ) , indents [ desiredIndentCount ] ) ;
158+ if ( this . _opts . isUnshift && ( lineText . length === 0 || indentationEndIndex === 0 ) ) {
159+ // empty line or line with no leading whitespace => nothing to do
160+ continue ;
161+ }
162+
163+ if ( ! shouldIndentEmptyLines && ! this . _opts . isUnshift && lineText . length === 0 ) {
164+ // do not indent empty lines => nothing to do
165+ continue ;
166+ }
167+
168+ if ( indentationEndIndex === - 1 ) {
169+ // the entire line is whitespace
170+ indentationEndIndex = lineText . length ;
171+ }
172+
173+ if ( this . _opts . isUnshift && indentationEndIndex === 0 ) {
174+ // line with no leading whitespace => nothing to do
175+ continue ;
176+ }
177+
178+ if ( this . _opts . isUnshift ) {
179+
180+ indentationEndIndex = Math . min ( indentationEndIndex , tabSize ) ;
181+ for ( let i = 0 ; i < indentationEndIndex ; i ++ ) {
182+ const chr = lineText . charCodeAt ( i ) ;
183+ if ( chr === CharCode . Tab ) {
184+ indentationEndIndex = i + 1 ;
185+ break ;
186+ }
187+ }
188+
189+ builder . addEditOperation ( new Range ( lineNumber , 1 , lineNumber , indentationEndIndex + 1 ) , '' ) ;
190+ } else {
191+ builder . addEditOperation ( new Range ( lineNumber , 1 , lineNumber , 1 ) , oneIndent ) ;
192+ }
193+ }
150194 }
151195
152196 this . _selectionId = builder . trackSelection ( this . _selection ) ;
0 commit comments