Merge pull request #8364 from zhengbli/i7503
Fix indentation for array items
This commit is contained in:
commit
3fb16f5930
|
@ -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) {
|
||||||
|
|
43
tests/cases/fourslash/formatArrayLiteralExpression.ts
Normal file
43
tests/cases/fourslash/formatArrayLiteralExpression.ts
Normal 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(" }];");
|
Loading…
Reference in a new issue