From f571d26037a9b8bc69b25b56bded423ecce31940 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 18 Mar 2020 17:02:10 -0700 Subject: [PATCH 1/8] don't delete comment on variable declaration --- src/services/textChanges.ts | 9 ++++++++- .../fourslash/unusedVariableWithComment.ts | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/unusedVariableWithComment.ts diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 006bc8031deae..513a284220a06 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -42,6 +42,10 @@ namespace ts.textChanges { * include all trivia between the node and the previous token */ IncludeAll, + /** + * Only delete trivia one the same line as getStart() + */ + StartLineOnly, } export enum TrailingTriviaOption { @@ -162,6 +166,9 @@ namespace ts.textChanges { if (leadingTriviaOption === LeadingTriviaOption.Exclude) { return node.getStart(sourceFile); } + if (leadingTriviaOption === LeadingTriviaOption.StartLineOnly) { + return getLineStartPositionForPosition(node.getStart(sourceFile), sourceFile); + } const fullStart = node.getFullStart(); const start = node.getStart(sourceFile); if (fullStart === start) { @@ -1372,7 +1379,7 @@ namespace ts.textChanges { break; case SyntaxKind.VariableStatement: - deleteNode(changes, sourceFile, gp); + deleteNode(changes, sourceFile, gp, { leadingTriviaOption: LeadingTriviaOption.StartLineOnly }); break; default: diff --git a/tests/cases/fourslash/unusedVariableWithComment.ts b/tests/cases/fourslash/unusedVariableWithComment.ts new file mode 100644 index 0000000000000..fb6d48e1265b6 --- /dev/null +++ b/tests/cases/fourslash/unusedVariableWithComment.ts @@ -0,0 +1,20 @@ +/// + +// @noUnusedLocals: true +////[|namespace greeter { +//// // do not remove comment +//// let a = 0; +//// // comment +//// let b = 0; +//// b; +////}|] + +verify.codeFix({ + description: "Remove unused declaration for: 'a'", + newRangeContent: `namespace greeter { + // do not remove comment + // comment + let b = 0; + b; +}` +}); From e79ebdcbb16686af4662d2db6c8655a4ea251c89 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Fri, 20 Mar 2020 19:15:47 -0700 Subject: [PATCH 2/8] add more declaration kinds --- src/compiler/types.ts | 2 ++ src/compiler/utilitiesPublic.ts | 4 ++++ src/services/textChanges.ts | 17 ++++++++++++----- .../unusedClassInNamespaceWithTrivia.ts | 1 + .../fourslash/unusedFunctionInNamespace1.ts | 1 + 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cf083fac5e897..e7cedef206aa1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2479,6 +2479,8 @@ namespace ts { moduleSpecifier: Expression; } + export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; + export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 9c599c7c425af..732134f09d417 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1379,6 +1379,10 @@ namespace ts { return node.kind === SyntaxKind.ImportDeclaration; } + export function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration { + return node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration; + } + export function isImportClause(node: Node): node is ImportClause { return node.kind === SyntaxKind.ImportClause; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 513a284220a06..d624deceae823 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -43,7 +43,8 @@ namespace ts.textChanges { */ IncludeAll, /** - * Only delete trivia one the same line as getStart() + * Only delete trivia on the same line as getStart(). + * Used to avoid deleting leading comments */ StartLineOnly, } @@ -1256,10 +1257,11 @@ namespace ts.textChanges { } case SyntaxKind.ImportDeclaration: - const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isImportDeclaration); + case SyntaxKind.ImportEqualsDeclaration: + const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isImportLikeDeclaration); + // For first import, leave header comment in place, otherwise only delete JSDoc comments deleteNode(changes, sourceFile, node, - // For first import, leave header comment in place - isFirstImport ? { leadingTriviaOption: LeadingTriviaOption.Exclude } : undefined); + { leadingTriviaOption: isFirstImport ? LeadingTriviaOption.Exclude : hasJSDocNodes(node) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); break; case SyntaxKind.BindingElement: @@ -1303,6 +1305,11 @@ namespace ts.textChanges { deleteNode(changes, sourceFile, node, { leadingTriviaOption: LeadingTriviaOption.Exclude }); break; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.FunctionDeclaration: + deleteNode(changes, sourceFile, node, { leadingTriviaOption: hasJSDocNodes(node) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); + break; + default: if (isImportClause(node.parent) && node.parent.name === node) { deleteDefaultImport(changes, sourceFile, node.parent); @@ -1379,7 +1386,7 @@ namespace ts.textChanges { break; case SyntaxKind.VariableStatement: - deleteNode(changes, sourceFile, gp, { leadingTriviaOption: LeadingTriviaOption.StartLineOnly }); + deleteNode(changes, sourceFile, gp, { leadingTriviaOption: hasJSDocNodes(gp) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); break; default: diff --git a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts index cae0c52ac79b6..3298a3e0ab111 100644 --- a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts +++ b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts @@ -8,4 +8,5 @@ //// } |] verify.rangeAfterCodeFix(`namespace greeter { + /* comment1 */ }`); diff --git a/tests/cases/fourslash/unusedFunctionInNamespace1.ts b/tests/cases/fourslash/unusedFunctionInNamespace1.ts index 7a63ff13caa92..9c3a21c04dc12 100644 --- a/tests/cases/fourslash/unusedFunctionInNamespace1.ts +++ b/tests/cases/fourslash/unusedFunctionInNamespace1.ts @@ -8,4 +8,5 @@ //// } |] verify.rangeAfterCodeFix(`namespace greeter { + // some legit comments }`); From 3710f99b94692a68d78305aa881d7dc00f8dff8e Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 23 Mar 2020 13:05:03 -0700 Subject: [PATCH 3/8] don't copy comment in convertes6 class --- src/services/codefixes/convertFunctionToEs6Class.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 0769cb26162b7..0b21df2c5a3cc 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -48,7 +48,10 @@ namespace ts.codefix { return undefined; } - copyLeadingComments(ctorDeclaration, newClassDeclaration, sourceFile); + if (hasJSDocNodes(ctorDeclaration)) + { + copyLeadingComments(ctorDeclaration, newClassDeclaration, sourceFile); + } // Because the preceding node could be touched, we need to insert nodes before delete nodes. changes.insertNodeAfter(sourceFile, precedingNode!, newClassDeclaration); From 06738cf0fa40c70fcb57245f87e840adf238f215 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 25 Mar 2020 13:27:59 -0700 Subject: [PATCH 4/8] don't copy comments in convertToES6Class --- src/services/codefixes/convertFunctionToEs6Class.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/codefixes/convertFunctionToEs6Class.ts b/src/services/codefixes/convertFunctionToEs6Class.ts index 0b21df2c5a3cc..d9917596ff12c 100644 --- a/src/services/codefixes/convertFunctionToEs6Class.ts +++ b/src/services/codefixes/convertFunctionToEs6Class.ts @@ -48,8 +48,8 @@ namespace ts.codefix { return undefined; } - if (hasJSDocNodes(ctorDeclaration)) - { + // Deleting a declaration only deletes JSDoc style comments, so only copy those to the new node. + if (hasJSDocNodes(ctorDeclaration)) { copyLeadingComments(ctorDeclaration, newClassDeclaration, sourceFile); } From 2a39d52205c5819ee7d113989916b4baa8ee8b11 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 25 Mar 2020 13:31:12 -0700 Subject: [PATCH 5/8] add tests --- .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ ...s => convertFunctionToEs6Class-server1.ts} | 2 +- .../convertFunctionToEs6Class-server2.ts | 31 +++++++++++++++++++ ...s => unusedClassInNamespaceWithTrivia1.ts} | 0 .../unusedClassInNamespaceWithTrivia2.ts | 14 +++++++++ .../unusedFunctionInNamespaceWithTrivia.ts | 14 +++++++++ ...omment.ts => unusedVariableWithTrivia1.ts} | 0 .../fourslash/unusedVariableWithTrivia2.ts | 15 +++++++++ 9 files changed, 79 insertions(+), 1 deletion(-) rename tests/cases/fourslash/server/{convertFunctionToEs6Class-server.ts => convertFunctionToEs6Class-server1.ts} (92%) create mode 100644 tests/cases/fourslash/server/convertFunctionToEs6Class-server2.ts rename tests/cases/fourslash/{unusedClassInNamespaceWithTrivia.ts => unusedClassInNamespaceWithTrivia1.ts} (100%) create mode 100644 tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts create mode 100644 tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts rename tests/cases/fourslash/{unusedVariableWithComment.ts => unusedVariableWithTrivia1.ts} (100%) create mode 100644 tests/cases/fourslash/unusedVariableWithTrivia2.ts diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5c0326d63ca24..080999bb65ab6 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1492,6 +1492,7 @@ declare namespace ts { /** If this is not a StringLiteral it will be a grammar error. */ moduleSpecifier: Expression; } + export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; export interface ImportClause extends NamedDeclaration { @@ -3692,6 +3693,7 @@ declare namespace ts { function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration; function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration; function isImportDeclaration(node: Node): node is ImportDeclaration; + function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration; function isImportClause(node: Node): node is ImportClause; function isNamespaceImport(node: Node): node is NamespaceImport; function isNamespaceExport(node: Node): node is NamespaceExport; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5bd7da4518176..eb48c6f66fc45 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1492,6 +1492,7 @@ declare namespace ts { /** If this is not a StringLiteral it will be a grammar error. */ moduleSpecifier: Expression; } + export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; export interface ImportClause extends NamedDeclaration { @@ -3692,6 +3693,7 @@ declare namespace ts { function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration; function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration; function isImportDeclaration(node: Node): node is ImportDeclaration; + function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration; function isImportClause(node: Node): node is ImportClause; function isNamespaceImport(node: Node): node is NamespaceImport; function isNamespaceExport(node: Node): node is NamespaceExport; diff --git a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts b/tests/cases/fourslash/server/convertFunctionToEs6Class-server1.ts similarity index 92% rename from tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts rename to tests/cases/fourslash/server/convertFunctionToEs6Class-server1.ts index 24a7acbf8edc5..316572063a5ea 100644 --- a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts +++ b/tests/cases/fourslash/server/convertFunctionToEs6Class-server1.ts @@ -14,7 +14,7 @@ verify.codeFix({ description: "Convert function to an ES2015 class", newFileContent: -`// Comment\r +`// Comment class fn {\r constructor() {\r this.baz = 10;\r diff --git a/tests/cases/fourslash/server/convertFunctionToEs6Class-server2.ts b/tests/cases/fourslash/server/convertFunctionToEs6Class-server2.ts new file mode 100644 index 0000000000000..2f9c1a88bdf7c --- /dev/null +++ b/tests/cases/fourslash/server/convertFunctionToEs6Class-server2.ts @@ -0,0 +1,31 @@ +// @allowNonTsExtensions: true +// @Filename: test123.js + +/// + +//// /** +//// * JSDoc Comment +//// */ +//// function fn() { +//// this.baz = 10; +//// } +//// /*1*/fn.prototype.bar = function () { +//// console.log('hello world'); +//// } + +verify.codeFix({ + description: "Convert function to an ES2015 class", + newFileContent: +`/**\r + * JSDoc Comment\r + */\r +class fn {\r + constructor() {\r + this.baz = 10;\r + }\r + bar() {\r + console.log('hello world');\r + }\r +}\r +`, +}); diff --git a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia1.ts similarity index 100% rename from tests/cases/fourslash/unusedClassInNamespaceWithTrivia.ts rename to tests/cases/fourslash/unusedClassInNamespaceWithTrivia1.ts diff --git a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts new file mode 100644 index 0000000000000..c1a56d02d2d32 --- /dev/null +++ b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +//// [| namespace greeter { +//// /** +//// * JSDoc Comment +//// */ +//// class /* comment2 */ class1 { +//// } +//// } |] + +verify.rangeAfterCodeFix(`namespace greeter { +}`); + \ No newline at end of file diff --git a/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts b/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts new file mode 100644 index 0000000000000..b4ece0686068c --- /dev/null +++ b/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts @@ -0,0 +1,14 @@ +/// + +// @noUnusedLocals: true +//// [| namespace greeter { +//// /** +//// * JSDoc Comment +//// */ +//// function function1() { +//// }/*1*/ +//// } |] + +verify.rangeAfterCodeFix(`namespace greeter { + }`); + \ No newline at end of file diff --git a/tests/cases/fourslash/unusedVariableWithComment.ts b/tests/cases/fourslash/unusedVariableWithTrivia1.ts similarity index 100% rename from tests/cases/fourslash/unusedVariableWithComment.ts rename to tests/cases/fourslash/unusedVariableWithTrivia1.ts diff --git a/tests/cases/fourslash/unusedVariableWithTrivia2.ts b/tests/cases/fourslash/unusedVariableWithTrivia2.ts new file mode 100644 index 0000000000000..8e685a4fd0214 --- /dev/null +++ b/tests/cases/fourslash/unusedVariableWithTrivia2.ts @@ -0,0 +1,15 @@ +/// + +// @noUnusedLocals: true +////[|namespace greeter { +//// /** +//// * JSDoc Comment +//// */ +//// let a = 0; +////}|] + +verify.codeFix({ + description: "Remove unused declaration for: 'a'", + newRangeContent: `namespace greeter { +}` +}); From 5b3e429f8d27287acafa0c75aadf2769417b37bb Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Wed, 25 Mar 2020 15:44:42 -0700 Subject: [PATCH 6/8] use isAnyImportSyntax --- src/compiler/types.ts | 2 -- src/compiler/utilitiesPublic.ts | 4 ---- src/services/textChanges.ts | 2 +- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 -- tests/baselines/reference/api/typescript.d.ts | 2 -- 5 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e7cedef206aa1..cf083fac5e897 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2479,8 +2479,6 @@ namespace ts { moduleSpecifier: Expression; } - export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; - export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 732134f09d417..9c599c7c425af 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1379,10 +1379,6 @@ namespace ts { return node.kind === SyntaxKind.ImportDeclaration; } - export function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration { - return node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration; - } - export function isImportClause(node: Node): node is ImportClause { return node.kind === SyntaxKind.ImportClause; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index d624deceae823..b640d25d636b1 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1258,7 +1258,7 @@ namespace ts.textChanges { case SyntaxKind.ImportDeclaration: case SyntaxKind.ImportEqualsDeclaration: - const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isImportLikeDeclaration); + const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isAnyImportSyntax); // For first import, leave header comment in place, otherwise only delete JSDoc comments deleteNode(changes, sourceFile, node, { leadingTriviaOption: isFirstImport ? LeadingTriviaOption.Exclude : hasJSDocNodes(node) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 080999bb65ab6..5c0326d63ca24 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -1492,7 +1492,6 @@ declare namespace ts { /** If this is not a StringLiteral it will be a grammar error. */ moduleSpecifier: Expression; } - export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; export interface ImportClause extends NamedDeclaration { @@ -3693,7 +3692,6 @@ declare namespace ts { function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration; function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration; function isImportDeclaration(node: Node): node is ImportDeclaration; - function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration; function isImportClause(node: Node): node is ImportClause; function isNamespaceImport(node: Node): node is NamespaceImport; function isNamespaceExport(node: Node): node is NamespaceExport; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index eb48c6f66fc45..5bd7da4518176 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -1492,7 +1492,6 @@ declare namespace ts { /** If this is not a StringLiteral it will be a grammar error. */ moduleSpecifier: Expression; } - export type ImportLikeDeclaration = ImportDeclaration | ImportEqualsDeclaration; export type NamedImportBindings = NamespaceImport | NamedImports; export type NamedExportBindings = NamespaceExport | NamedExports; export interface ImportClause extends NamedDeclaration { @@ -3693,7 +3692,6 @@ declare namespace ts { function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration; function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration; function isImportDeclaration(node: Node): node is ImportDeclaration; - function isImportLikeDeclaration(node: Node): node is ImportLikeDeclaration; function isImportClause(node: Node): node is ImportClause; function isNamespaceImport(node: Node): node is NamespaceImport; function isNamespaceExport(node: Node): node is NamespaceExport; From 1eab700f2415d0bd03e064a8bc345e59b31113eb Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 30 Mar 2020 12:33:05 -0700 Subject: [PATCH 7/8] handle mixed comment types --- src/services/textChanges.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index b640d25d636b1..7d26f5e9cb6f2 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -42,11 +42,15 @@ namespace ts.textChanges { * include all trivia between the node and the previous token */ IncludeAll, + /** + * Include attached JSDoc comments + */ + JSDoc, /** * Only delete trivia on the same line as getStart(). * Used to avoid deleting leading comments */ - StartLineOnly, + StartLine, } export enum TrailingTriviaOption { @@ -167,9 +171,15 @@ namespace ts.textChanges { if (leadingTriviaOption === LeadingTriviaOption.Exclude) { return node.getStart(sourceFile); } - if (leadingTriviaOption === LeadingTriviaOption.StartLineOnly) { + if (leadingTriviaOption === LeadingTriviaOption.StartLine) { return getLineStartPositionForPosition(node.getStart(sourceFile), sourceFile); } + if (leadingTriviaOption === LeadingTriviaOption.JSDoc) { + const JSDocComments = getJSDocCommentRanges(node, sourceFile.text); + if (JSDocComments?.length) { + return getLineStartPositionForPosition(JSDocComments[0].pos, sourceFile); + } + } const fullStart = node.getFullStart(); const start = node.getStart(sourceFile); if (fullStart === start) { @@ -1261,7 +1271,7 @@ namespace ts.textChanges { const isFirstImport = sourceFile.imports.length && node === first(sourceFile.imports).parent || node === find(sourceFile.statements, isAnyImportSyntax); // For first import, leave header comment in place, otherwise only delete JSDoc comments deleteNode(changes, sourceFile, node, - { leadingTriviaOption: isFirstImport ? LeadingTriviaOption.Exclude : hasJSDocNodes(node) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); + { leadingTriviaOption: isFirstImport ? LeadingTriviaOption.Exclude : hasJSDocNodes(node) ? LeadingTriviaOption.JSDoc : LeadingTriviaOption.StartLine }); break; case SyntaxKind.BindingElement: @@ -1307,7 +1317,7 @@ namespace ts.textChanges { case SyntaxKind.ClassDeclaration: case SyntaxKind.FunctionDeclaration: - deleteNode(changes, sourceFile, node, { leadingTriviaOption: hasJSDocNodes(node) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); + deleteNode(changes, sourceFile, node, { leadingTriviaOption: hasJSDocNodes(node) ? LeadingTriviaOption.JSDoc : LeadingTriviaOption.StartLine }); break; default: @@ -1386,7 +1396,7 @@ namespace ts.textChanges { break; case SyntaxKind.VariableStatement: - deleteNode(changes, sourceFile, gp, { leadingTriviaOption: hasJSDocNodes(gp) ? LeadingTriviaOption.IncludeAll : LeadingTriviaOption.StartLineOnly }); + deleteNode(changes, sourceFile, gp, { leadingTriviaOption: hasJSDocNodes(gp) ? LeadingTriviaOption.JSDoc : LeadingTriviaOption.StartLine }); break; default: From 6cbbdbcc4c22f7dd82b023059ca8230a927707e7 Mon Sep 17 00:00:00 2001 From: Jesse Trinity Date: Mon, 30 Mar 2020 13:10:24 -0700 Subject: [PATCH 8/8] update tests --- tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts | 2 ++ tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts | 2 ++ tests/cases/fourslash/unusedVariableWithTrivia2.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts index c1a56d02d2d32..8578239756c1e 100644 --- a/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts +++ b/tests/cases/fourslash/unusedClassInNamespaceWithTrivia2.ts @@ -2,6 +2,7 @@ // @noUnusedLocals: true //// [| namespace greeter { +//// // Do not remove //// /** //// * JSDoc Comment //// */ @@ -10,5 +11,6 @@ //// } |] verify.rangeAfterCodeFix(`namespace greeter { + // Do not remove }`); \ No newline at end of file diff --git a/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts b/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts index b4ece0686068c..bfcaa5852b0cf 100644 --- a/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts +++ b/tests/cases/fourslash/unusedFunctionInNamespaceWithTrivia.ts @@ -2,6 +2,7 @@ // @noUnusedLocals: true //// [| namespace greeter { +//// // Do not remove //// /** //// * JSDoc Comment //// */ @@ -10,5 +11,6 @@ //// } |] verify.rangeAfterCodeFix(`namespace greeter { + // Do not remove }`); \ No newline at end of file diff --git a/tests/cases/fourslash/unusedVariableWithTrivia2.ts b/tests/cases/fourslash/unusedVariableWithTrivia2.ts index 8e685a4fd0214..c2ea84f3acbc5 100644 --- a/tests/cases/fourslash/unusedVariableWithTrivia2.ts +++ b/tests/cases/fourslash/unusedVariableWithTrivia2.ts @@ -2,6 +2,7 @@ // @noUnusedLocals: true ////[|namespace greeter { +//// // Do not remove //// /** //// * JSDoc Comment //// */ @@ -11,5 +12,6 @@ verify.codeFix({ description: "Remove unused declaration for: 'a'", newRangeContent: `namespace greeter { + // Do not remove }` });