Skip to content

Commit 9aa994a

Browse files
committed
Fixes microsoft#1620: Indent commands respect editor.useTabStops
1 parent dfa8bf9 commit 9aa994a

3 files changed

Lines changed: 456 additions & 188 deletions

File tree

src/vs/editor/common/commands/shiftCommand.ts

Lines changed: 109 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface IShiftCommandOpts {
1616
isUnshift: boolean;
1717
tabSize: number;
1818
oneIndent: string;
19+
useTabStops: boolean;
1920
}
2021

2122
export 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);

src/vs/editor/common/controller/cursorTypeOperations.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ export class TypeOperations {
2424
new ShiftCommand(cursor.selection, {
2525
isUnshift: false,
2626
tabSize: config.tabSize,
27-
oneIndent: config.oneIndent
27+
oneIndent: config.oneIndent,
28+
useTabStops: config.useTabStops
2829
}), {
2930
shouldPushStackElementBefore: true,
3031
shouldPushStackElementAfter: true
@@ -37,7 +38,8 @@ export class TypeOperations {
3738
new ShiftCommand(cursor.selection, {
3839
isUnshift: true,
3940
tabSize: config.tabSize,
40-
oneIndent: config.oneIndent
41+
oneIndent: config.oneIndent,
42+
useTabStops: config.useTabStops
4143
}), {
4244
shouldPushStackElementBefore: true,
4345
shouldPushStackElementAfter: true

0 commit comments

Comments
 (0)