From e27bc9646a3f6cec82650b6f7243cd1cbabae6e3 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 11 Sep 2019 10:35:52 -0700 Subject: [PATCH] Fix some edge cases of semicolon deletion --- src/services/formatting/formatting.ts | 10 +++-- src/services/formatting/rules.ts | 32 ++++++++++++- .../fourslash/formatRemoveSemicolons1.ts | 45 +++++++++++++++---- .../TypeScript-Node-Starter | 2 +- tests/cases/user/axios-src/axios-src | 2 +- .../user/create-react-app/create-react-app | 2 +- tests/cases/user/prettier/prettier | 2 +- tests/cases/user/puppeteer/puppeteer | 2 +- tests/cases/user/webpack/webpack | 2 +- 9 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 6469605c8eb..47cf646ba69 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1147,7 +1147,11 @@ namespace ts.formatting { function recordDelete(start: number, len: number) { if (len) { - edits.push(createTextChangeFromStartLength(start, len, "")); + const newEdit = createTextChangeFromStartLength(start, len, ""); + const lastEdit = lastOrUndefined(edits); + if (!lastEdit || !textSpansEqual(lastEdit.span, newEdit.span) || lastEdit.newText !== newEdit.newText) { + edits.push(newEdit); + } } } @@ -1157,7 +1161,7 @@ namespace ts.formatting { } } - function recordInsertionOnce(start: number, text: string) { + function recordInsert(start: number, text: string) { if (text) { const newEdit = createTextChangeFromStartLength(start, 0, text); const lastEdit = lastOrUndefined(edits); @@ -1216,7 +1220,7 @@ namespace ts.formatting { } break; case RuleAction.TrailingSemicolon: - recordInsertionOnce(previousRange.end, ";"); + recordInsert(previousRange.end, ";"); } return LineAction.None; } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index cc49a129e81..f08f8c96585 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -319,8 +319,34 @@ namespace ts.formatting { function isSemicolonDeletionContext(context: FormattingContext): boolean { if (context.TokensAreOnSameLine()) { - return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken; + return context.nextTokenSpan.kind === SyntaxKind.CloseBraceToken + || context.nextTokenSpan.kind === SyntaxKind.EndOfFileToken; } + + if (context.nextTokenSpan.kind === SyntaxKind.SemicolonClassElement || + context.nextTokenSpan.kind === SyntaxKind.SemicolonToken + ) { + return false; + } + + if (context.contextNode.kind === SyntaxKind.InterfaceDeclaration || + context.contextNode.kind === SyntaxKind.TypeAliasDeclaration + ) { + // Can’t remove semicolon after `foo`; it would parse as a + // method declaration: + // interface I { + // foo; + // (): void + // } + return !(isPropertySignature(context.currentTokenParent) + && !context.currentTokenParent.type + && context.nextTokenSpan.kind === SyntaxKind.OpenParenToken); + } + + if (isPropertyDeclaration(context.currentTokenParent)) { + return !context.currentTokenParent.initializer; + } + return context.currentTokenParent.kind !== SyntaxKind.ForStatement && context.currentTokenParent.kind !== SyntaxKind.EmptyStatement && context.currentTokenParent.kind !== SyntaxKind.SemicolonClassElement @@ -329,7 +355,11 @@ namespace ts.formatting { && context.nextTokenSpan.kind !== SyntaxKind.PlusToken && context.nextTokenSpan.kind !== SyntaxKind.MinusToken && context.nextTokenSpan.kind !== SyntaxKind.SlashToken + && context.nextTokenSpan.kind !== SyntaxKind.RegularExpressionLiteral && context.nextTokenSpan.kind !== SyntaxKind.CommaToken + && context.nextTokenSpan.kind !== SyntaxKind.TemplateExpression + && context.nextTokenSpan.kind !== SyntaxKind.TemplateHead + && context.nextTokenSpan.kind !== SyntaxKind.NoSubstitutionTemplateLiteral && context.nextTokenSpan.kind !== SyntaxKind.DotToken; } diff --git a/tests/cases/fourslash/formatRemoveSemicolons1.ts b/tests/cases/fourslash/formatRemoveSemicolons1.ts index d5084df8147..8b4150e4229 100644 --- a/tests/cases/fourslash/formatRemoveSemicolons1.ts +++ b/tests/cases/fourslash/formatRemoveSemicolons1.ts @@ -1,6 +1,6 @@ /// -////;(function f() { })(); +////; (function f() { })(); ////const a = 3; ////+ 4; ////const b = 3 @@ -8,6 +8,9 @@ ////const c = 3 + ////4; ////class C { +//// prop; +//// ["p"]; +//// zero: void; //// ["one"] = {}; //// ["two"]; //// ; @@ -21,28 +24,54 @@ ////; ////[]; /////** blah */[0]; +////interface I { +//// new; +//// (); +//// foo; +//// (); +////} +////type T = { +//// new; +//// (); +//// foo; +//// (); +////} format.setFormatOptions({ ...format.copyFormatOptions(), insertTrailingSemicolon: false }); format.document(); -verify.currentFileContentIs(`;(function f() { })() +verify.currentFileContentIs(`; (function f() { })() const a = 3; + 4 const b = 3 - + 4 ++ 4 const c = 3 + - 4 +4 class C { + prop + ["p"] zero: void ["one"] = {}; ["two"]; ; } a; -\`b\`; +\`b\` b; -(3); +(3) 4; - / regex /; +/ regex /; ; []; -/** blah */[0]`); +/** blah */[0] +interface I { + new; + () + foo; + () +} +type T = { + new; + () + foo; + () +}`); \ No newline at end of file diff --git a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter index 722ebf8053d..1bf5836cae5 160000 --- a/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter +++ b/tests/cases/user/TypeScript-Node-Starter/TypeScript-Node-Starter @@ -1 +1 @@ -Subproject commit 722ebf8053d2bf82bb66134b21c7e291ccae35c4 +Subproject commit 1bf5836cae5246b89bbf7063c3e84e110222fcdf diff --git a/tests/cases/user/axios-src/axios-src b/tests/cases/user/axios-src/axios-src index 98e4acd893f..2ee3b482456 160000 --- a/tests/cases/user/axios-src/axios-src +++ b/tests/cases/user/axios-src/axios-src @@ -1 +1 @@ -Subproject commit 98e4acd893fe024ae9e6074894c6164802b3af63 +Subproject commit 2ee3b482456cd2a09ccbd3a4b0c20f3d0c5a5644 diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app index 6dec056de3c..6560858398d 160000 --- a/tests/cases/user/create-react-app/create-react-app +++ b/tests/cases/user/create-react-app/create-react-app @@ -1 +1 @@ -Subproject commit 6dec056de3c646fa1bce41acadc31641237863a0 +Subproject commit 6560858398ddc8d1c5b8d7f51929fcb3d9c3055c diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 223443c057e..2f40dba3177 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 223443c057e64ca04cda5c0f37f5d15daaf69337 +Subproject commit 2f40dba3177c6edd3ceb88b26cdf4718e892a3e5 diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer index c2651c2b5cc..cba0f98a2ac 160000 --- a/tests/cases/user/puppeteer/puppeteer +++ b/tests/cases/user/puppeteer/puppeteer @@ -1 +1 @@ -Subproject commit c2651c2b5cc888ebd0ca6be87063d9f98b9eb59c +Subproject commit cba0f98a2ac7edd3c2bffd0ac53185877403da6b diff --git a/tests/cases/user/webpack/webpack b/tests/cases/user/webpack/webpack index b2b5b278ddf..b16ca509d12 160000 --- a/tests/cases/user/webpack/webpack +++ b/tests/cases/user/webpack/webpack @@ -1 +1 @@ -Subproject commit b2b5b278ddfa49cea5a2a74b42917633e2d58237 +Subproject commit b16ca509d12faf36573b65fffcbae50c5b3e7ee3