Merge pull request #8364 from zhengbli/i7503

Fix indentation for array items
This commit is contained in:
Zhengbo Li 2016-05-16 21:53:27 -07:00
commit 3fb16f5930
2 changed files with 79 additions and 30 deletions

View file

@ -20,13 +20,13 @@ namespace ts.formatting {
Unknown = -1 Unknown = -1
} }
/* /*
* Indentation for the scope that can be dynamically recomputed. * Indentation for the scope that can be dynamically recomputed.
* i.e * i.e
* while(true) * while(true)
* { let x; * { let x;
* } * }
* Normally indentation is applied only to the first token in line so at glance 'var' should not be touched. * Normally indentation is applied only to the first token in line so at glance 'var' should not be touched.
* However if some format rule adds new line between '}' and 'var' 'var' will become * However if some format rule adds new line between '}' and 'var' 'var' will become
* the first token in line so it should be indented * the first token in line so it should be indented
*/ */
@ -48,15 +48,15 @@ namespace ts.formatting {
* foo(bar({ * foo(bar({
* $ * $
* })) * }))
* Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8. * Both 'foo', 'bar' introduce new indentation with delta = 4, but total indentation in $ is not 8.
* foo: { indentation: 0, delta: 4 } * foo: { indentation: 0, delta: 4 }
* bar: { indentation: foo.indentation + foo.delta = 4, delta: 4} however 'foo' and 'bar' are on the same line * bar: { indentation: foo.indentation + foo.delta = 4, delta: 4} however 'foo' and 'bar' are on the same line
* so bar inherits indentation from foo and bar.delta will be 4 * so bar inherits indentation from foo and bar.delta will be 4
* *
*/ */
getDelta(child: TextRangeWithKind): number; getDelta(child: TextRangeWithKind): number;
/** /**
* Formatter calls this function when rule adds or deletes new lines from the text * Formatter calls this function when rule adds or deletes new lines from the text
* so indentation scope can adjust values of indentation and delta. * so indentation scope can adjust values of indentation and delta.
*/ */
recomputeIndentation(lineAddedByFormatting: boolean): void; recomputeIndentation(lineAddedByFormatting: boolean): void;
@ -130,9 +130,9 @@ namespace ts.formatting {
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node { function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
let precedingToken = findPrecedingToken(position, sourceFile); let precedingToken = findPrecedingToken(position, sourceFile);
// when it is claimed that trigger character was typed at given position // when it is claimed that trigger character was typed at given position
// we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed). // we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
// If this condition is not hold - then trigger character was typed in some other context, // If this condition is not hold - then trigger character was typed in some other context,
// i.e.in comment and thus should not trigger autoformatting // i.e.in comment and thus should not trigger autoformatting
if (!precedingToken || if (!precedingToken ||
precedingToken.kind !== expectedTokenKind || precedingToken.kind !== expectedTokenKind ||
@ -142,12 +142,12 @@ namespace ts.formatting {
// walk up and search for the parent node that ends at the same position with precedingToken. // walk up and search for the parent node that ends at the same position with precedingToken.
// for cases like this // for cases like this
// //
// let x = 1; // let x = 1;
// while (true) { // while (true) {
// } // }
// after typing close curly in while statement we want to reformat just the while statement. // after typing close curly in while statement we want to reformat just the while statement.
// However if we just walk upwards searching for the parent that has the same end value - // However if we just walk upwards searching for the parent that has the same end value -
// we'll end up with the whole source file. isListElement allows to stop on the list element level // we'll end up with the whole source file. isListElement allows to stop on the list element level
let current = precedingToken; let current = precedingToken;
while (current && while (current &&
@ -223,7 +223,7 @@ namespace ts.formatting {
// 'index' tracks the index of the most recent error that was checked. // 'index' tracks the index of the most recent error that was checked.
while (true) { while (true) {
if (index >= sorted.length) { if (index >= sorted.length) {
// all errors in the range were already checked -> no error in specified range // all errors in the range were already checked -> no error in specified range
return false; return false;
} }
@ -249,7 +249,7 @@ namespace ts.formatting {
/** /**
* Start of the original range might fall inside the comment - scanner will not yield appropriate results * Start of the original range might fall inside the comment - scanner will not yield appropriate results
* This function will look for token that is located before the start of target range * This function will look for token that is located before the start of target range
* and return its end as start position for the scanner. * and return its end as start position for the scanner.
*/ */
function getScanStartPosition(enclosingNode: Node, originalRange: TextRange, sourceFile: SourceFile): number { function getScanStartPosition(enclosingNode: Node, originalRange: TextRange, sourceFile: SourceFile): number {
@ -274,7 +274,7 @@ namespace ts.formatting {
} }
/* /*
* For cases like * For cases like
* if (a || * if (a ||
* b ||$ * b ||$
* c) {...} * c) {...}
@ -284,8 +284,8 @@ namespace ts.formatting {
* Initial indentation for this node will be 0. * Initial indentation for this node will be 0.
* Binary expressions don't introduce new indentation scopes, however it is possible * Binary expressions don't introduce new indentation scopes, however it is possible
* that some parent node on the same line does - like if statement in this case. * that some parent node on the same line does - like if statement in this case.
* Note that we are considering parents only from the same line with initial node - * Note that we are considering parents only from the same line with initial node -
* if parent is on the different line - its delta was already contributed * if parent is on the different line - its delta was already contributed
* to the initial indentation. * to the initial indentation.
*/ */
function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number { function getOwnOrInheritedDelta(n: Node, options: FormatCodeOptions, sourceFile: SourceFile): number {
@ -364,10 +364,10 @@ namespace ts.formatting {
// local functions // local functions
/** Tries to compute the indentation for a list element. /** Tries to compute the indentation for a list element.
* If list element is not in range then * If list element is not in range then
* function will pick its actual indentation * function will pick its actual indentation
* so it can be pushed downstream as inherited indentation. * so it can be pushed downstream as inherited indentation.
* If list element is in the range - its indentation will be equal * If list element is in the range - its indentation will be equal
* to inherited indentation from its predecessors. * to inherited indentation from its predecessors.
*/ */
function tryComputeIndentationForListItem(startPos: number, function tryComputeIndentationForListItem(startPos: number,
@ -378,7 +378,7 @@ namespace ts.formatting {
if (rangeOverlapsWithStartEnd(range, startPos, endPos) || if (rangeOverlapsWithStartEnd(range, startPos, endPos) ||
rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) { rangeContainsStartEnd(range, startPos, endPos) /* Not to miss zero-range nodes e.g. JsxText */) {
if (inheritedIndentation !== Constants.Unknown) { if (inheritedIndentation !== Constants.Unknown) {
return inheritedIndentation; return inheritedIndentation;
} }
@ -529,12 +529,12 @@ namespace ts.formatting {
// a useful observations when tracking context node // a useful observations when tracking context node
// / // /
// [a] // [a]
// / | \ // / | \
// [b] [c] [d] // [b] [c] [d]
// node 'a' is a context node for nodes 'b', 'c', 'd' // node 'a' is a context node for nodes 'b', 'c', 'd'
// except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a' // except for the leftmost leaf token in [b] - in this case context node ('e') is located somewhere above 'a'
// this rule can be applied recursively to child nodes of 'a'. // this rule can be applied recursively to child nodes of 'a'.
// //
// context node is set to parent node value after processing every child node // context node is set to parent node value after processing every child node
// context node is set to parent of the token after processing every token // context node is set to parent of the token after processing every token
@ -567,7 +567,8 @@ namespace ts.formatting {
parentDynamicIndentation: DynamicIndentation, parentDynamicIndentation: DynamicIndentation,
parentStartLine: number, parentStartLine: number,
undecoratedParentStartLine: number, undecoratedParentStartLine: number,
isListItem: boolean): number { isListItem: boolean,
isFirstListItem?: boolean): number {
let childStartPos = child.getStart(sourceFile); let childStartPos = child.getStart(sourceFile);
@ -626,6 +627,10 @@ namespace ts.formatting {
childContextNode = node; childContextNode = node;
if (isFirstListItem && parent.kind === SyntaxKind.ArrayLiteralExpression && inheritedIndentation === Constants.Unknown) {
inheritedIndentation = childIndentation.indentation;
}
return inheritedIndentation; return inheritedIndentation;
} }
@ -665,8 +670,9 @@ namespace ts.formatting {
} }
let inheritedIndentation = Constants.Unknown; let inheritedIndentation = Constants.Unknown;
for (let child of nodes) { for (let i = 0; i < nodes.length; i++) {
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListElement*/ true) const child = nodes[i];
inheritedIndentation = processChildNode(child, inheritedIndentation, node, listDynamicIndentation, startLine, startLine, /*isListElement*/ true, /*isFirstListItem*/ i === 0);
} }
if (listEndToken !== SyntaxKind.Unknown) { if (listEndToken !== SyntaxKind.Unknown) {
@ -674,7 +680,7 @@ namespace ts.formatting {
let tokenInfo = formattingScanner.readTokenInfo(parent); let tokenInfo = formattingScanner.readTokenInfo(parent);
// consume the list end token only if it is still belong to the parent // consume the list end token only if it is still belong to the parent
// there might be the case when current token matches end token but does not considered as one // there might be the case when current token matches end token but does not considered as one
// function (x: function) <-- // function (x: function) <--
// without this check close paren will be interpreted as list end token for function expression which is wrong // without this check close paren will be interpreted as list end token for function expression which is wrong
if (tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) { if (tokenInfo.token.kind === listEndToken && rangeContainsRange(parent, tokenInfo.token)) {
// consume list end token // consume list end token
@ -733,7 +739,7 @@ namespace ts.formatting {
let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container); let commentIndentation = dynamicIndentation.getIndentationForComment(currentTokenInfo.token.kind, tokenIndentation, container);
for (let triviaItem of currentTokenInfo.leadingTrivia) { for (let triviaItem of currentTokenInfo.leadingTrivia) {
const triviaInRange = rangeContainsRange(originalRange, triviaItem); const triviaInRange = rangeContainsRange(originalRange, triviaItem);
switch (triviaItem.kind) { switch (triviaItem.kind) {
case SyntaxKind.MultiLineCommentTrivia: case SyntaxKind.MultiLineCommentTrivia:
if (triviaInRange) { if (triviaInRange) {
@ -826,7 +832,7 @@ namespace ts.formatting {
if (rule.Operation.Action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) { if (rule.Operation.Action & (RuleAction.Space | RuleAction.Delete) && currentStartLine !== previousStartLine) {
lineAdded = false; lineAdded = false;
// Handle the case where the next line is moved to be the end of this line. // Handle the case where the next line is moved to be the end of this line.
// In this case we don't indent the next line in the next pass. // In this case we don't indent the next line in the next pass.
if (currentParent.getStart(sourceFile) === currentItem.pos) { if (currentParent.getStart(sourceFile) === currentItem.pos) {
dynamicIndentation.recomputeIndentation(/*lineAdded*/ false); dynamicIndentation.recomputeIndentation(/*lineAdded*/ false);
@ -834,7 +840,7 @@ namespace ts.formatting {
} }
else if (rule.Operation.Action & RuleAction.NewLine && currentStartLine === previousStartLine) { else if (rule.Operation.Action & RuleAction.NewLine && currentStartLine === previousStartLine) {
lineAdded = true; lineAdded = true;
// Handle the case where token2 is moved to the new line. // Handle the case where token2 is moved to the new line.
// In this case we indent token2 in the next pass but we set // In this case we indent token2 in the next pass but we set
// sameLineIndent flag to notify the indenter that the indentation is within the line. // sameLineIndent flag to notify the indenter that the indentation is within the line.
if (currentParent.getStart(sourceFile) === currentItem.pos) { if (currentParent.getStart(sourceFile) === currentItem.pos) {

View file

@ -0,0 +1,43 @@
///<reference path='fourslash.ts' />
////export let Things = [{
//// Hat: 'hat', /*1*/
//// Glove: 'glove',
//// Umbrella: 'umbrella'
////},{/*2*/
//// Salad: 'salad', /*3*/
//// Burrito: 'burrito',
//// Pie: 'pie'
//// }];/*4*/
////
////export let Things2 = [
////{
//// Hat: 'hat', /*5*/
//// Glove: 'glove',
//// Umbrella: 'umbrella'
////}/*6*/,
//// {
//// Salad: 'salad', /*7*/
//// Burrito: 'burrito',
//// Pie: 'pie'
//// }];/*8*/
format.document();
goTo.marker("1");
verify.currentLineContentIs(" Hat: 'hat',");
goTo.marker("2");
verify.currentLineContentIs("}, {");
goTo.marker("3");
verify.currentLineContentIs(" Salad: 'salad',");
goTo.marker("4");
verify.currentLineContentIs("}];");
goTo.marker("5");
verify.currentLineContentIs(" Hat: 'hat',");
goTo.marker("6");
verify.currentLineContentIs(" },");
goTo.marker("7");
verify.currentLineContentIs(" Salad: 'salad',");
goTo.marker("8");
verify.currentLineContentIs(" }];");