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\`; +`);