From 44b97459420534d028d7dbc5aeed66823ee7fb89 Mon Sep 17 00:00:00 2001
From: TRCYX <2915154295@qq.com>
Date: Wed, 1 Jun 2022 07:03:17 +0800
Subject: [PATCH] fix(49151): format type parameters/arguments (#49165)
Before, the formatter did not consider these constructs as comma
separated lists in general, leading to wrong indentation of '>' after
the list.
---
src/services/formatting/formatting.ts | 21 ++-
.../fourslash/genericsFormattingMultiline.ts | 140 ++++++++++++++++++
2 files changed, 159 insertions(+), 2 deletions(-)
create mode 100644 tests/cases/fourslash/genericsFormattingMultiline.ts
diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts
index 256398d9a20..789fcb12c58 100644
--- a/src/services/formatting/formatting.ts
+++ b/src/services/formatting/formatting.ts
@@ -617,7 +617,6 @@ namespace ts.formatting {
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
case SyntaxKind.JsxSelfClosingElement:
- case SyntaxKind.ExpressionWithTypeArguments:
return false;
}
break;
@@ -835,7 +834,7 @@ namespace ts.formatting {
const listEndToken = getCloseTokenForOpenToken(listStartToken);
if (listEndToken !== SyntaxKind.Unknown && formattingScanner.isOnToken() && formattingScanner.getStartPos() < originalRange.end) {
let tokenInfo: TokenInfo | undefined = formattingScanner.readTokenInfo(parent);
- if (tokenInfo.token.kind === SyntaxKind.CommaToken && isCallLikeExpression(parent)) {
+ if (tokenInfo.token.kind === SyntaxKind.CommaToken) {
// consume the comma
consumeTokenAndAdvanceScanner(tokenInfo, parent, listDynamicIndentation, parent);
tokenInfo = formattingScanner.isOnToken() ? formattingScanner.readTokenInfo(parent) : undefined;
@@ -1311,6 +1310,12 @@ namespace ts.formatting {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.ArrowFunction:
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
if ((node as FunctionDeclaration).typeParameters === list) {
return SyntaxKind.LessThanToken;
}
@@ -1327,7 +1332,19 @@ namespace ts.formatting {
return SyntaxKind.OpenParenToken;
}
break;
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ if ((node as ClassDeclaration).typeParameters === list) {
+ return SyntaxKind.LessThanToken;
+ }
+ break;
case SyntaxKind.TypeReference:
+ case SyntaxKind.TaggedTemplateExpression:
+ case SyntaxKind.TypeQuery:
+ case SyntaxKind.ExpressionWithTypeArguments:
+ case SyntaxKind.ImportType:
if ((node as TypeReferenceNode).typeArguments === list) {
return SyntaxKind.LessThanToken;
}
diff --git a/tests/cases/fourslash/genericsFormattingMultiline.ts b/tests/cases/fourslash/genericsFormattingMultiline.ts
new file mode 100644
index 00000000000..8ee8b09a07d
--- /dev/null
+++ b/tests/cases/fourslash/genericsFormattingMultiline.ts
@@ -0,0 +1,140 @@
+///
+
+////
+//// class Foo <
+//// T1 extends unknown,
+//// T2
+//// > {
+//// public method <
+//// T3,
+//// > (a: T1, b: Array <
+//// string
+//// > ): Map <
+//// T1 ,
+//// Array < T3 >
+//// > { throw new Error(); }
+//// }
+////
+//// interface IFoo<
+//// T,
+//// > {
+//// new < T
+//// > ( a: T);
+//// op?<
+//// T,
+//// M
+//// > (a: T, b : M );
+//// <
+//// T,
+//// >(x: T): T;
+//// }
+////
+//// type foo<
+//// T
+//// > = Foo <
+//// number, Array < number > > ;
+////
+//// function bar <
+//// T, U extends T
+//// > () {
+//// return class <
+//// T2,
+//// > {
+//// }
+//// }
+////
+//// bar<
+//// string,
+//// "s"
+//// > ();
+////
+//// declare const func: <
+//// T extends number[],
+//// > (x: T) => new <
+//// U
+//// > () => U;
+////
+//// class A < T > extends bar <
+//// T,number
+//// >( ) < T
+//// > {
+//// }
+////
+//// function s(x: TemplateStringsArray, ...args: any[]) { return x.join(); }
+////
+//// const t = s<
+//// number ,
+//// string[] & ArrayLike
+//// >`abc${1}def` ;
+////
+
+
+format.document();
+
+verify.currentFileContentIs(`
+class Foo<
+ T1 extends unknown,
+ T2
+> {
+ public method<
+ T3,
+ >(a: T1, b: Array<
+ string
+ >): Map<
+ T1,
+ Array
+ > { throw new Error(); }
+}
+
+interface IFoo<
+ T,
+> {
+ new (a: T);
+ op?<
+ T,
+ M
+ >(a: T, b: M);
+ <
+ T,
+ >(x: T): T;
+}
+
+type foo<
+ T
+> = Foo<
+ number, Array>;
+
+function bar<
+ T, U extends T
+>() {
+ return class <
+ T2,
+ > {
+ }
+}
+
+bar<
+ string,
+ "s"
+>();
+
+declare const func: <
+ T extends number[],
+> (x: T) => new <
+ U
+> () => U;
+
+class A extends bar<
+ T, number
+>() {
+}
+
+function s(x: TemplateStringsArray, ...args: any[]) { return x.join(); }
+
+const t = s<
+ number,
+ string[] & ArrayLike
+>\`abc\${1}def\`;
+`);