Skip to content

Commit 70747b8

Browse files
committed
Merge pull request #5197 from Microsoft/supportIndentStyle
Support different indentation styles
2 parents 89ab0ef + ab8e93c commit 70747b8

File tree

8 files changed

+426
-12
lines changed

8 files changed

+426
-12
lines changed

src/harness/fourslash.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ module FourSlash {
9999
end: number;
100100
}
101101

102+
export import IndentStyle = ts.IndentStyle;
103+
102104
let entityMap: ts.Map<string> = {
103105
"&": "&amp;",
104106
"\"": "&quot;",
@@ -307,6 +309,7 @@ module FourSlash {
307309
TabSize: 4,
308310
NewLineCharacter: Harness.IO.newLine(),
309311
ConvertTabsToSpaces: true,
312+
IndentStyle: ts.IndentStyle.Smart,
310313
InsertSpaceAfterCommaDelimiter: true,
311314
InsertSpaceAfterSemicolonInForStatements: true,
312315
InsertSpaceBeforeAndAfterBinaryOperators: true,
@@ -1678,24 +1681,28 @@ module FourSlash {
16781681
}
16791682
}
16801683

1681-
private getIndentation(fileName: string, position: number): number {
1682-
return this.languageService.getIndentationAtPosition(fileName, position, this.formatCodeOptions);
1684+
private getIndentation(fileName: string, position: number, indentStyle: ts.IndentStyle): number {
1685+
1686+
let formatOptions = ts.clone(this.formatCodeOptions);
1687+
formatOptions.IndentStyle = indentStyle;
1688+
1689+
return this.languageService.getIndentationAtPosition(fileName, position, formatOptions);
16831690
}
16841691

1685-
public verifyIndentationAtCurrentPosition(numberOfSpaces: number) {
1692+
public verifyIndentationAtCurrentPosition(numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
16861693
this.taoInvalidReason = "verifyIndentationAtCurrentPosition NYI";
16871694

1688-
let actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition);
1695+
let actual = this.getIndentation(this.activeFile.fileName, this.currentCaretPosition, indentStyle);
16891696
let lineCol = this.getLineColStringAtPosition(this.currentCaretPosition);
16901697
if (actual !== numberOfSpaces) {
16911698
this.raiseError(`verifyIndentationAtCurrentPosition failed at ${lineCol} - expected: ${numberOfSpaces}, actual: ${actual}`);
16921699
}
16931700
}
16941701

1695-
public verifyIndentationAtPosition(fileName: string, position: number, numberOfSpaces: number) {
1702+
public verifyIndentationAtPosition(fileName: string, position: number, numberOfSpaces: number, indentStyle: ts.IndentStyle = ts.IndentStyle.Smart) {
16961703
this.taoInvalidReason = "verifyIndentationAtPosition NYI";
16971704

1698-
let actual = this.getIndentation(fileName, position);
1705+
let actual = this.getIndentation(fileName, position, indentStyle);
16991706
let lineCol = this.getLineColStringAtPosition(position);
17001707
if (actual !== numberOfSpaces) {
17011708
this.raiseError(`verifyIndentationAtPosition failed at ${lineCol} - expected: ${numberOfSpaces}, actual: ${actual}`);

src/server/editorServices.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ namespace ts.server {
11771177
TabSize: 4,
11781178
NewLineCharacter: ts.sys ? ts.sys.newLine : '\n',
11791179
ConvertTabsToSpaces: true,
1180+
IndentStyle: ts.IndentStyle.Smart,
11801181
InsertSpaceAfterCommaDelimiter: true,
11811182
InsertSpaceAfterSemicolonInForStatements: true,
11821183
InsertSpaceBeforeAndAfterBinaryOperators: true,
@@ -1187,7 +1188,6 @@ namespace ts.server {
11871188
PlaceOpenBraceOnNewLineForFunctions: false,
11881189
PlaceOpenBraceOnNewLineForControlBlocks: false,
11891190
}
1190-
11911191
}
11921192

11931193
export interface LineCollection {

src/server/session.ts

+1
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ namespace ts.server {
606606
TabSize: formatOptions.TabSize,
607607
NewLineCharacter: "\n",
608608
ConvertTabsToSpaces: formatOptions.ConvertTabsToSpaces,
609+
IndentStyle: ts.IndentStyle.Smart,
609610
};
610611
var indentPosition =
611612
compilerService.languageService.getIndentationAtPosition(file, position, editorOptions);

src/services/formatting/smartIndenter.ts

+29-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ namespace ts.formatting {
1313
return 0; // past EOF
1414
}
1515

16+
// no indentation when the indent style is set to none,
17+
// so we can return fast
18+
if (options.IndentStyle === IndentStyle.None) {
19+
return 0;
20+
}
21+
1622
let precedingToken = findPrecedingToken(position, sourceFile);
1723
if (!precedingToken) {
1824
return 0;
@@ -32,6 +38,26 @@ namespace ts.formatting {
3238

3339
let lineAtPosition = sourceFile.getLineAndCharacterOfPosition(position).line;
3440

41+
// indentation is first non-whitespace character in a previous line
42+
// for block indentation, we should look for a line which contains something that's not
43+
// whitespace.
44+
if (options.IndentStyle === IndentStyle.Block) {
45+
46+
// move backwards until we find a line with a non-whitespace character,
47+
// then find the first non-whitespace character for that line.
48+
let current = position;
49+
while (current > 0){
50+
let char = sourceFile.text.charCodeAt(current);
51+
if (!isWhiteSpace(char) && !isLineBreak(char)) {
52+
break;
53+
}
54+
current--;
55+
}
56+
57+
let lineStart = ts.getLineStartPositionForPosition(current, sourceFile);
58+
return SmartIndenter.findFirstNonWhitespaceColumn(lineStart, current, sourceFile, options);
59+
}
60+
3561
if (precedingToken.kind === SyntaxKind.CommaToken && precedingToken.parent.kind !== SyntaxKind.BinaryExpression) {
3662
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
3763
let actualIndentation = getActualIndentationForListItemBeforeComma(precedingToken, sourceFile, options);
@@ -224,7 +250,7 @@ namespace ts.formatting {
224250
function getStartLineAndCharacterForNode(n: Node, sourceFile: SourceFile): LineAndCharacter {
225251
return sourceFile.getLineAndCharacterOfPosition(n.getStart(sourceFile));
226252
}
227-
253+
228254
export function childStartsOnTheSameLineWithElseInIfStatement(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFile): boolean {
229255
if (parent.kind === SyntaxKind.IfStatement && (<IfStatement>parent).elseStatement === child) {
230256
let elseKeyword = findChildOfKind(parent, SyntaxKind.ElseKeyword, sourceFile);
@@ -325,7 +351,7 @@ namespace ts.formatting {
325351
}
326352

327353
return Value.Unknown;
328-
354+
329355
function getStartingExpression(node: PropertyAccessExpression | CallExpression | ElementAccessExpression) {
330356
while (true) {
331357
switch (node.kind) {
@@ -470,4 +496,4 @@ namespace ts.formatting {
470496
}
471497
}
472498
}
473-
}
499+
}

src/services/services.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,13 @@ namespace ts {
11891189
TabSize: number;
11901190
NewLineCharacter: string;
11911191
ConvertTabsToSpaces: boolean;
1192+
IndentStyle: IndentStyle;
1193+
}
1194+
1195+
export enum IndentStyle {
1196+
None = 0,
1197+
Block = 1,
1198+
Smart = 2,
11921199
}
11931200

11941201
export interface FormatCodeOptions extends EditorOptions {

tests/cases/fourslash/fourslash.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ enum EmitReturnStatus {
6868
EmitErrorsEncountered = 4 // Emitter errors occurred during emitting process
6969
}
7070

71+
// This is a duplicate of the indentstyle in services.ts to expose it to testcases in fourslash
72+
enum IndentStyle {
73+
None,
74+
Block,
75+
Smart,
76+
}
77+
7178
module FourSlashInterface {
7279

7380
export interface Marker {
@@ -278,8 +285,8 @@ module FourSlashInterface {
278285
FourSlash.currentTestState.verifyIndentationAtCurrentPosition(numberOfSpaces);
279286
}
280287

281-
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number) {
282-
FourSlash.currentTestState.verifyIndentationAtPosition(fileName, position, numberOfSpaces);
288+
public indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle = IndentStyle.Smart) {
289+
FourSlash.currentTestState.verifyIndentationAtPosition(fileName, position, numberOfSpaces, indentStyle);
283290
}
284291

285292
public textAtCaretIs(text: string) {
+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
////
4+
////module classes {
5+
////{| "indent": 0 |}
6+
//// class Bar {
7+
////{| "indent": 4 |}
8+
////
9+
//// constructor() {
10+
////{| "indent": 8 |}
11+
//// }
12+
////
13+
//// private foo: string = "";
14+
////{| "indent": 8 |}
15+
////
16+
//// private f() {
17+
//// var a: any[] = [[1, 2], [3, 4], 5];
18+
////{| "indent": 12 |}
19+
//// return ((1 + 1));
20+
//// }
21+
////
22+
////{| "indent": 8 |}
23+
//// private f2() {
24+
//// if (true) { } { };
25+
//// }
26+
//// }
27+
////}
28+
////
29+
////
30+
////module interfaces {
31+
////{| "indent": 0 |}
32+
//// interface Foo {
33+
////{| "indent": 4 |}
34+
////
35+
//// x: number;
36+
////{| "indent": 8 |}
37+
////
38+
//// foo(): number;
39+
////{| "indent": 8 |}
40+
//// }
41+
////}
42+
////
43+
////
44+
////module nestedModules {
45+
//// module Foo2 {
46+
////{| "indent": 4 |}
47+
//// function f() {
48+
//// }
49+
////{| "indent": 8 |}
50+
//// var x: number;
51+
////{| "indent": 8 |}
52+
//// }
53+
////}
54+
////
55+
////
56+
////module Enums {
57+
//// enum Foo3 {
58+
////{| "indent": 4 |}
59+
//// val1,
60+
////{| "indent": 8 |}
61+
//// val2,
62+
////{| "indent": 8 |}
63+
//// }
64+
////{| "indent": 4 |}
65+
////}
66+
////
67+
////
68+
////function controlStatements() {
69+
//// for (var i = 0; i < 10; i++) {
70+
////{| "indent": 4 |}
71+
//// }
72+
////
73+
//// for (var e in foo.bar) {
74+
////{| "indent": 4 |}
75+
//// }
76+
////
77+
//// with (foo.bar) {
78+
////{| "indent": 4 |}
79+
//// }
80+
////
81+
//// while (false) {
82+
////{| "indent": 4 |}
83+
//// }
84+
////
85+
//// do {
86+
////{| "indent": 4 |}
87+
//// } while (false);
88+
////
89+
//// switch (foo.bar) {
90+
////{| "indent": 4 |}
91+
//// }
92+
////
93+
//// switch (foo.bar) {
94+
////{| "indent": 4 |}
95+
//// case 1:
96+
////{| "indent": 8 |}
97+
//// break;
98+
//// default:
99+
////{| "indent": 8 |}
100+
//// break;
101+
//// }
102+
////}
103+
////
104+
////
105+
////function tryCatch() {
106+
////{| "indent": 0 |}
107+
//// try {
108+
////{| "indent": 4 |}
109+
//// }
110+
////{| "indent": 4 |}
111+
//// catch (err) {
112+
////{| "indent": 4 |}
113+
//// }
114+
////{| "indent": 4 |}
115+
////}
116+
////
117+
////
118+
////function tryFinally() {
119+
////{| "indent": 0 |}
120+
//// try {
121+
////{| "indent": 4 |}
122+
//// }
123+
////{| "indent": 4 |}
124+
//// finally {
125+
////{| "indent": 4 |}
126+
//// }
127+
////{| "indent": 4 |}
128+
////}
129+
////
130+
////
131+
////function tryCatchFinally() {
132+
////{| "indent": 0 |}
133+
//// try {
134+
////{| "indent": 4 |}
135+
//// }
136+
////{| "indent": 4 |}
137+
//// catch (err) {
138+
////{| "indent": 4 |}
139+
//// }
140+
////{| "indent": 4 |}
141+
//// finally {
142+
////{| "indent": 4 |}
143+
//// }
144+
////{| "indent": 4 |}
145+
////}
146+
////
147+
////
148+
////class indentBeforeCurly
149+
////{| "indent": 0 |}
150+
////{| "indent": 0 |}{
151+
////{| "indent": 0 |}
152+
////}
153+
////
154+
////
155+
////function argumentsListIndentation(bar,
156+
//// blah,
157+
//// {| "indent": 13 |}
158+
////);
159+
////
160+
////
161+
////function blockIndentAfterIndentedParameter1(bar,
162+
//// blah) {
163+
////{| "indent": 13 |}
164+
////}
165+
////
166+
////
167+
////function blockIndentAfterIndentedParameter2(bar,
168+
//// blah) {
169+
//// if (foo) {
170+
////{| "indent": 4 |}
171+
//// }
172+
////}
173+
////
174+
////
175+
////// Note: Do not add more tests at the end of this file, as
176+
////// the purpose of this test is to verity smart indent
177+
////// works for unterminated function arguments at the end of a file.
178+
////function unterminatedListIndentation(a,
179+
////{| "indent": 0 |}
180+
181+
test.markers().forEach(marker => {
182+
verify.indentationAtPositionIs(marker.fileName, marker.position, marker.data.indent, IndentStyle.Block);
183+
});

0 commit comments

Comments
 (0)