Skip to content

Commit 3fb16f5

Browse files
author
Zhengbo Li
committed
Merge pull request microsoft#8364 from zhengbli/i7503
Fix indentation for array items
2 parents 5433553 + b85b004 commit 3fb16f5

2 files changed

Lines changed: 79 additions & 30 deletions

File tree

src/services/formatting/formatting.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ namespace ts.formatting {
2020
Unknown = -1
2121
}
2222

23-
/*
23+
/*
2424
* Indentation for the scope that can be dynamically recomputed.
25-
* i.e
25+
* i.e
2626
* while(true)
2727
* { let x;
2828
* }
29-
* Normally indentation is applied only to the first token in line so at glance 'var' should not be touched.
29+
* Normally indentation is applied only to the first token in line so at glance 'var' should not be touched.
3030
* However if some format rule adds new line between '}' and 'var' 'var' will become
3131
* the first token in line so it should be indented
3232
*/
@@ -48,15 +48,15 @@ namespace ts.formatting {
4848
* foo(bar({
4949
* $
5050
* }))
51-
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
51+
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
5252
* foo: { indentation: 0, delta: 4 }
5353
* bar: { indentation: foo.indentation + foo.delta = 4, delta: 4} however 'foo' and 'bar' are on the same line
5454
* so bar inherits indentation from foo and bar.delta will be 4
55-
*
55+
*
5656
*/
5757
getDelta(child: TextRangeWithKind): number;
5858
/**
59-
* Formatter calls this function when rule adds or deletes new lines from the text
59+
* Formatter calls this function when rule adds or deletes new lines from the text
6060
* so indentation scope can adjust values of indentation and delta.
6161
*/
6262
recomputeIndentation(lineAddedByFormatting: boolean): void;
@@ -130,9 +130,9 @@ namespace ts.formatting {
130130
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
131131
let precedingToken = findPrecedingToken(position, sourceFile);
132132

133-
// when it is claimed that trigger character was typed at given position
133+
// when it is claimed that trigger character was typed at given position
134134
// we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
135-
// If this condition is not hold - then trigger character was typed in some other context,
135+
// If this condition is not hold - then trigger character was typed in some other context,
136136
// i.e.in comment and thus should not trigger autoformatting
137137
if (!precedingToken ||
138138
precedingToken.kind !== expectedTokenKind ||
@@ -142,12 +142,12 @@ namespace ts.formatting {
142142

143143
// walk up and search for the parent node that ends at the same position with precedingToken.
144144
// for cases like this
145-
//
145+
//
146146
// let x = 1;
147147
// while (true) {
148-
// }
148+
// }
149149
// after typing close curly in while statement we want to reformat just the while statement.
150-
// However if we just walk upwards searching for the parent that has the same end value -
150+
// However if we just walk upwards searching for the parent that has the same end value -
151151
// we'll end up with the whole source file. isListElement allows to stop on the list element level
152152
let current = precedingToken;
153153
while (current &&
@@ -223,7 +223,7 @@ namespace ts.formatting {
223223
// 'index' tracks the index of the most recent error that was checked.
224224
while (true) {
225225
if (index >= sorted.length) {
226-
// all errors in the range were already checked -> no error in specified range
226+
// all errors in the range were already checked -> no error in specified range
227227
return false;
228228
}
229229

@@ -249,7 +249,7 @@ namespace ts.formatting {
249249

250250
/**
251251
* Start of the original range might fall inside the comment - scanner will not yield appropriate results
252-
* This function will look for token that is located before the start of target range
252+
* This function will look for token that is located before the start of target range
253253
* and return its end as start position for the scanner.
254254
*/
255255
function getScanStartPosition(enclosingNode: Node, originalRange: TextRange, sourceFile: SourceFile): number {
@@ -274,7 +274,7 @@ namespace ts.formatting {
274274
}
275275

276276
/*
277-
* For cases like
277+
* For cases like
278278
* if (a ||
279279
* b ||$
280280
* c) {...}
@@ -284,8 +284,8 @@ namespace ts.formatting {
284284
* Initial indentation for this node will be 0.
285285
* Binary expressions don't introduce new indentation scopes, however it is possible
286286
* that some parent node on the same line does - like if statement in this case.
287-
* Note that we are considering parents only from the same line with initial node -
288-
* if parent is on the different line - its delta was already contributed
287+
* Note that we are considering parents only from the same line with initial node -
288+
* if parent is on the different line - its delta was already contributed
289289
* to the initial indentation.
290290
*/
291291
function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number {
@@ -364,10 +364,10 @@ namespace ts.formatting {
364364
// local functions
365365

366366
/** Tries to compute the indentation for a list element.
367-
* If list element is not in range then
368-
* function will pick its actual indentation
367+
* If list element is not in range then
368+
* function will pick its actual indentation
369369
* so it can be pushed downstream as inherited indentation.
370-
* If list element is in the range - its indentation will be equal
370+
* If list element is in the range - its indentation will be equal
371371
* to inherited indentation from its predecessors.
372372
*/
373373
function tryComputeIndentationForListItem(startPos: number,
@@ -378,7 +378,7 @@ namespace ts.formatting {
378378

379379
if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
380380
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
381-
381+
382382
if (inheritedIndentation !== Constants.Unknown) {
383383
return inheritedIndentation;
384384
}
@@ -529,12 +529,12 @@ namespace ts.formatting {
529529
// a useful observations when tracking context node
530530
// /
531531
// [a]
532-
// / | \
532+
// / | \
533533
// [b] [c] [d]
534-
// node 'a' is a context node for nodes 'b', 'c', 'd'
534+
// node 'a' is a context node for nodes 'b', 'c', 'd'
535535
// except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a'
536536
// this rule can be applied recursively to child nodes of 'a'.
537-
//
537+
//
538538
// context node is set to parent node value after processing every child node
539539
// context node is set to parent of the token after processing every token
540540

@@ -567,7 +567,8 @@ namespace ts.formatting {
567567
parentDynamicIndentation: DynamicIndentation,
568568
parentStartLine: number,
569569
undecoratedParentStartLine: number,
570-
isListItem: boolean): number {
570+
isListItem: boolean,
571+
isFirstListItem?: boolean): number {
571572

572573
let childStartPos = child.getStart(sourceFile);
573574

@@ -626,6 +627,10 @@ namespace ts.formatting {
626627

627628
childContextNode = node;
628629

630+
if (isFirstListItem && parent.kind === SyntaxKind.ArrayLiteralExpression && inheritedIndentation === Constants.Unknown) {
631+
inheritedIndentation = childIndentation.indentation;
632+
}
633+
629634
return inheritedIndentation;
630635
}
631636

@@ -665,16 +670,17 @@ namespace ts.formatting {
665670
}
666671

667672
let inheritedIndentation = Constants.Unknown;
668-
for (let child of nodes) {
669-
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListElement*/ true)
673+
for (let i = 0; i < nodes.length; i++) {
674+
const child = nodes[i];
675+
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListElement*/ true, /*isFirstListItem*/ i === 0);
670676
}
671677

672678
if (listEndToken !== SyntaxKind.Unknown) {
673679
if (formattingScanner.isOnToken()) {
674680
let tokenInfo = formattingScanner.readTokenInfo(parent);
675681
// consume the list end token only if it is still belong to the parent
676682
// there might be the case when current token matches end token but does not considered as one
677-
// function (x: function) <--
683+
// function (x: function) <--
678684
// without this check close paren will be interpreted as list end token for function expression which is wrong
679685
if (tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) {
680686
// consume list end token
@@ -733,7 +739,7 @@ namespace ts.formatting {
733739
let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
734740

735741
for (let triviaItem of currentTokenInfo.leadingTrivia) {
736-
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
742+
const triviaInRange = rangeContainsRange(originalRange, triviaItem);
737743
switch (triviaItem.kind) {
738744
case SyntaxKind.MultiLineCommentTrivia:
739745
if (triviaInRange) {
@@ -826,15 +832,15 @@ namespace ts.formatting {
826832

827833
if (rule.Operation.Action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
828834
lineAdded = false;
829-
// Handle the case where the next line is moved to be the end of this line.
835+
// Handle the case where the next line is moved to be the end of this line.
830836
// In this case we don't indent the next line in the next pass.
831837
if (currentParent.getStart(sourceFile) === currentItem.pos) {
832838
dynamicIndentation.recomputeIndentation(/*lineAdded*/ false);
833839
}
834840
}
835841
else if (rule.Operation.Action & RuleAction.NewLine && currentStartLine === previousStartLine) {
836842
lineAdded = true;
837-
// Handle the case where token2 is moved to the new line.
843+
// Handle the case where token2 is moved to the new line.
838844
// In this case we indent token2 in the next pass but we set
839845
// sameLineIndent flag to notify the indenter that the indentation is within the line.
840846
if (currentParent.getStart(sourceFile) === currentItem.pos) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
///<reference path='fourslash.ts' />
2+
3+
////export let Things = [{
4+
//// Hat: 'hat', /*1*/
5+
//// Glove: 'glove',
6+
//// Umbrella: 'umbrella'
7+
////},{/*2*/
8+
//// Salad: 'salad', /*3*/
9+
//// Burrito: 'burrito',
10+
//// Pie: 'pie'
11+
//// }];/*4*/
12+
////
13+
////export let Things2 = [
14+
////{
15+
//// Hat: 'hat', /*5*/
16+
//// Glove: 'glove',
17+
//// Umbrella: 'umbrella'
18+
////}/*6*/,
19+
//// {
20+
//// Salad: 'salad', /*7*/
21+
//// Burrito: 'burrito',
22+
//// Pie: 'pie'
23+
//// }];/*8*/
24+
25+
format.document();
26+
27+
goTo.marker("1");
28+
verify.currentLineContentIs(" Hat: 'hat',");
29+
goTo.marker("2");
30+
verify.currentLineContentIs("}, {");
31+
goTo.marker("3");
32+
verify.currentLineContentIs(" Salad: 'salad',");
33+
goTo.marker("4");
34+
verify.currentLineContentIs("}];");
35+
36+
goTo.marker("5");
37+
verify.currentLineContentIs(" Hat: 'hat',");
38+
goTo.marker("6");
39+
verify.currentLineContentIs(" },");
40+
goTo.marker("7");
41+
verify.currentLineContentIs(" Salad: 'salad',");
42+
goTo.marker("8");
43+
verify.currentLineContentIs(" }];");

0 commit comments

Comments
 (0)