Fix indentation of multiline conditional expressions (#42484)

* Fix indentation of multiline conditional expressions

* Add more comment

* Fix comment typo

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>

* Fix suggestion formatting

Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
Andrew Branch
2021-01-29 12:07:57 -08:00
committed by GitHub
co-authored by Nathan Shively-Sanders
parent c15f40abfa
commit 9d21a5b56c
3 changed files with 89 additions and 4 deletions
+5 -4
View File
@@ -498,10 +498,11 @@ namespace ts.formatting {
// - we need to get the indentation on last line and the delta of parent
return { indentation: indentationOnLastIndentedLine, delta: parentDynamicIndentation.getDelta(node) };
}
else if (SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile)) {
return { indentation: parentDynamicIndentation.getIndentation(), delta };
}
else if (SmartIndenter.argumentStartsOnSameLineAsPreviousArgument(parent, node, startLine, sourceFile)) {
else if (
SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(parent, node, startLine, sourceFile) ||
SmartIndenter.childIsUnindentedBranchOfConditionalExpression(parent, node, startLine, sourceFile) ||
SmartIndenter.argumentStartsOnSameLineAsPreviousArgument(parent, node, startLine, sourceFile)
) {
return { indentation: parentDynamicIndentation.getIndentation(), delta };
}
else {
+43
View File
@@ -341,6 +341,49 @@ namespace ts.formatting {
return false;
}
// A multiline conditional typically increases the indentation of its whenTrue and whenFalse children:
//
// condition
// ? whenTrue
// : whenFalse;
//
// However, that indentation does not apply if the subexpressions themselves span multiple lines,
// applying their own indentation:
//
// (() => {
// return complexCalculationForCondition();
// })() ? {
// whenTrue: 'multiline object literal'
// } : (
// whenFalse('multiline parenthesized expression')
// );
//
// In these cases, we must discard the indentation increase that would otherwise be applied to the
// whenTrue and whenFalse children to avoid double-indenting their contents. To identify this scenario,
// we check for the whenTrue branch beginning on the line that the condition ends, and the whenFalse
// branch beginning on the line that the whenTrue branch ends.
export function childIsUnindentedBranchOfConditionalExpression(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFileLike): boolean {
if (isConditionalExpression(parent) && (child === parent.whenTrue || child === parent.whenFalse)) {
const conditionEndLine = getLineAndCharacterOfPosition(sourceFile, parent.condition.end).line;
if (child === parent.whenTrue) {
return childStartLine === conditionEndLine;
}
else {
// On the whenFalse side, we have to look at the whenTrue side, because if that one was
// indented, whenFalse must also be indented:
//
// const y = true
// ? 1 : ( L1: whenTrue indented because it's on a new line
// 0 L2: indented two stops, one because whenTrue was indented
// ); and one because of the parentheses spanning multiple lines
const trueStartLine = getStartLineAndCharacterForNode(parent.whenTrue, sourceFile).line;
const trueEndLine = getLineAndCharacterOfPosition(sourceFile, parent.whenTrue.end).line;
return conditionEndLine === trueStartLine && trueEndLine === childStartLine;
}
}
return false;
}
export function argumentStartsOnSameLineAsPreviousArgument(parent: Node, child: TextRangeWithKind, childStartLine: number, sourceFile: SourceFileLike): boolean {
if (isCallOrNewExpression(parent)) {
if (!parent.arguments) return false;
@@ -0,0 +1,41 @@
/// <reference path="fourslash.ts" />
// @Filename: index.tsx
//// !!true ? (
//// <div>a</div>
//// ) : (
//// <div>b</div>
//// )
////
//// const y = true
//// ? 0 : (
//// 1
//// );
////
//// const yy = true
//// ? 0
//// : (
//// 1
//// );
////
//// const z = true ? (() => {
//// console.log('hello');
//// }) : (
//// 1
//// );
////
//// const a = true ? {
//// p: true
//// } : {
//// p: false
//// };
////
//// const b = {
//// x: 'x'
//// } ? (
//// 1
//// ) : (
//// 0
//// );
verify.formatDocumentChangesNothing();