From 6e0447fdf165b1cec9fc80802abcc15bd23a268f Mon Sep 17 00:00:00 2001 From: Minh Quy Date: Tue, 5 Apr 2022 20:53:19 +0200 Subject: [PATCH] [Feature] - Automatically create sort groups based on newlines (#48330) Co-authored-by: Jake Bailey <5341706+jakebailey@users.noreply.github.com> --- src/services/organizeImports.ts | 51 +++++++++++++++++-- .../unittests/services/organizeImports.ts | 17 ------- .../reference/organizeImports/SortComments.ts | 17 ------- .../TopLevelAndAmbientModule.ts | 2 +- tests/cases/fourslash/organizeImports6.ts | 10 +++- .../organizeImportsGroup_CommentInNewline.ts | 21 ++++++++ .../organizeImportsGroup_MultiNewlines.ts | 21 ++++++++ ...eImportsGroup_MultilineCommentInNewline.ts | 25 +++++++++ .../fourslash/organizeImportsGroup_Newline.ts | 19 +++++++ 9 files changed, 141 insertions(+), 42 deletions(-) delete mode 100644 tests/baselines/reference/organizeImports/SortComments.ts create mode 100644 tests/cases/fourslash/organizeImportsGroup_CommentInNewline.ts create mode 100644 tests/cases/fourslash/organizeImportsGroup_MultiNewlines.ts create mode 100644 tests/cases/fourslash/organizeImportsGroup_MultilineCommentInNewline.ts create mode 100644 tests/cases/fourslash/organizeImportsGroup_Newline.ts diff --git a/src/services/organizeImports.ts b/src/services/organizeImports.ts index d01f2eae5f6d6..7533a54893397 100644 --- a/src/services/organizeImports.ts +++ b/src/services/organizeImports.ts @@ -15,7 +15,6 @@ namespace ts.OrganizeImports { preferences: UserPreferences, skipDestructiveCodeActions?: boolean ) { - const changeTracker = textChanges.ChangeTracker.fromContext({ host, formatContext, preferences }); const coalesceAndOrganizeImports = (importGroup: readonly ImportDeclaration[]) => stableSort( @@ -23,8 +22,8 @@ namespace ts.OrganizeImports { (s1, s2) => compareImportsOrRequireStatements(s1, s2)); // All of the old ImportDeclarations in the file, in syntactic order. - const topLevelImportDecls = sourceFile.statements.filter(isImportDeclaration); - organizeImportsWorker(topLevelImportDecls, coalesceAndOrganizeImports); + const topLevelImportGroupDecls = groupImportsByNewlineContiguous(sourceFile, sourceFile.statements.filter(isImportDeclaration)); + topLevelImportGroupDecls.forEach(importGroupDecl => organizeImportsWorker(importGroupDecl, coalesceAndOrganizeImports)); // All of the old ExportDeclarations in the file, in syntactic order. const topLevelExportDecls = sourceFile.statements.filter(isExportDeclaration); @@ -33,8 +32,8 @@ namespace ts.OrganizeImports { for (const ambientModule of sourceFile.statements.filter(isAmbientModule)) { if (!ambientModule.body) continue; - const ambientModuleImportDecls = ambientModule.body.statements.filter(isImportDeclaration); - organizeImportsWorker(ambientModuleImportDecls, coalesceAndOrganizeImports); + const ambientModuleImportGroupDecls = groupImportsByNewlineContiguous(sourceFile, ambientModule.body.statements.filter(isImportDeclaration)); + ambientModuleImportGroupDecls.forEach(importGroupDecl => organizeImportsWorker(importGroupDecl, coalesceAndOrganizeImports)); const ambientModuleExportDecls = ambientModule.body.statements.filter(isExportDeclaration); organizeImportsWorker(ambientModuleExportDecls, coalesceExports); @@ -88,6 +87,48 @@ namespace ts.OrganizeImports { } } + function groupImportsByNewlineContiguous(sourceFile: SourceFile, importDecls: ImportDeclaration[]): ImportDeclaration[][] { + const scanner = createScanner(sourceFile.languageVersion, /*skipTrivia*/ false, sourceFile.languageVariant); + const groupImports: ImportDeclaration[][] = []; + let groupIndex = 0; + for (const topLevelImportDecl of importDecls) { + if (isNewGroup(sourceFile, topLevelImportDecl, scanner)) { + groupIndex++; + } + + if (!groupImports[groupIndex]) { + groupImports[groupIndex] = []; + } + + groupImports[groupIndex].push(topLevelImportDecl); + } + + return groupImports; + } + + // a new group is created if an import includes at least two new line + // new line from multi-line comment doesn't count + function isNewGroup(sourceFile: SourceFile, topLevelImportDecl: ImportDeclaration, scanner: Scanner) { + const startPos = topLevelImportDecl.getFullStart(); + const endPos = topLevelImportDecl.getStart(); + scanner.setText(sourceFile.text, startPos, endPos - startPos); + + let numberOfNewLines = 0; + while (scanner.getTokenPos() < endPos) { + const tokenKind = scanner.scan(); + + if (tokenKind === SyntaxKind.NewLineTrivia) { + numberOfNewLines++; + + if (numberOfNewLines >= 2) { + return true; + } + } + } + + return false; + } + function removeUnusedImports(oldImports: readonly ImportDeclaration[], sourceFile: SourceFile, program: Program, skipDestructiveCodeActions: boolean | undefined) { // As a precaution, consider unused import detection to be destructive (GH #43051) if (skipDestructiveCodeActions) { diff --git a/src/testRunner/unittests/services/organizeImports.ts b/src/testRunner/unittests/services/organizeImports.ts index f3b222bb76791..4ddd1b608d003 100644 --- a/src/testRunner/unittests/services/organizeImports.ts +++ b/src/testRunner/unittests/services/organizeImports.ts @@ -679,23 +679,6 @@ import "lib1"; { path: "/lib1.ts", content: "" }, { path: "/lib2.ts", content: "" }); - testOrganizeImports("SortComments", - /*skipDestructiveCodeActions*/ false, - { - path: "/test.ts", - content: ` -// Header -import "lib3"; -// Comment2 -import "lib2"; -// Comment1 -import "lib1"; -`, - }, - { path: "/lib1.ts", content: "" }, - { path: "/lib2.ts", content: "" }, - { path: "/lib3.ts", content: "" }); - testOrganizeImports("AmbientModule", /*skipDestructiveCodeActions*/ false, { diff --git a/tests/baselines/reference/organizeImports/SortComments.ts b/tests/baselines/reference/organizeImports/SortComments.ts deleted file mode 100644 index 761bca1a2127b..0000000000000 --- a/tests/baselines/reference/organizeImports/SortComments.ts +++ /dev/null @@ -1,17 +0,0 @@ -// ==ORIGINAL== - -// Header -import "lib3"; -// Comment2 -import "lib2"; -// Comment1 -import "lib1"; - -// ==ORGANIZED== - -// Header -// Comment1 -import "lib1"; -// Comment2 -import "lib2"; -import "lib3"; diff --git a/tests/baselines/reference/organizeImports/TopLevelAndAmbientModule.ts b/tests/baselines/reference/organizeImports/TopLevelAndAmbientModule.ts index d6f3158efe859..8bbab4f4ff5d8 100644 --- a/tests/baselines/reference/organizeImports/TopLevelAndAmbientModule.ts +++ b/tests/baselines/reference/organizeImports/TopLevelAndAmbientModule.ts @@ -17,7 +17,6 @@ D(); // ==ORGANIZED== -import "lib"; import D from "lib"; declare module "mod" { @@ -26,5 +25,6 @@ declare module "mod" { function F(f1: {} = F1, f2: {} = F2) {} } +import "lib"; D(); diff --git a/tests/cases/fourslash/organizeImports6.ts b/tests/cases/fourslash/organizeImports6.ts index f5fa3b8269d9d..75e1491c1d19c 100644 --- a/tests/cases/fourslash/organizeImports6.ts +++ b/tests/cases/fourslash/organizeImports6.ts @@ -16,6 +16,12 @@ //// anotherThing; verify.organizeImports( -`import * as anotherThing from "someopath"; /* small comment */ // single line one. +`/* some comment here +* and there +*/ +import * as anotherThing from "someopath"; /* small comment */ // single line one. +/* some comment here +* and there +*/ -anotherThing;`); \ No newline at end of file +anotherThing;`); diff --git a/tests/cases/fourslash/organizeImportsGroup_CommentInNewline.ts b/tests/cases/fourslash/organizeImportsGroup_CommentInNewline.ts new file mode 100644 index 0000000000000..8a20bf185e97a --- /dev/null +++ b/tests/cases/fourslash/organizeImportsGroup_CommentInNewline.ts @@ -0,0 +1,21 @@ +/// + +////// polyfill +////import c from "C"; +////// not polyfill +////import d from "D"; +////import a from "A"; +////import b from "B"; +//// +////console.log(a, b, c, d) + +verify.organizeImports( +`// polyfill +import c from "C"; +// not polyfill +import a from "A"; +import b from "B"; +import d from "D"; + +console.log(a, b, c, d)` +); diff --git a/tests/cases/fourslash/organizeImportsGroup_MultiNewlines.ts b/tests/cases/fourslash/organizeImportsGroup_MultiNewlines.ts new file mode 100644 index 0000000000000..cce8b8f603fa5 --- /dev/null +++ b/tests/cases/fourslash/organizeImportsGroup_MultiNewlines.ts @@ -0,0 +1,21 @@ +/// + +////import c from "C"; +//// +//// +////import d from "D"; +////import a from "A"; +////import b from "B"; +//// +////console.log(a, b, c, d) + +verify.organizeImports( +`import c from "C"; + + +import a from "A"; +import b from "B"; +import d from "D"; + +console.log(a, b, c, d)` +); diff --git a/tests/cases/fourslash/organizeImportsGroup_MultilineCommentInNewline.ts b/tests/cases/fourslash/organizeImportsGroup_MultilineCommentInNewline.ts new file mode 100644 index 0000000000000..04fff1675ac30 --- /dev/null +++ b/tests/cases/fourslash/organizeImportsGroup_MultilineCommentInNewline.ts @@ -0,0 +1,25 @@ +/// + +////// polyfill +////import c from "C"; +/////* +////* demo +////*/ +////import d from "D"; +////import a from "A"; +////import b from "B"; +//// +////console.log(a, b, c, d) + +verify.organizeImports( +`// polyfill +import c from "C"; +/* +* demo +*/ +import a from "A"; +import b from "B"; +import d from "D"; + +console.log(a, b, c, d)` +); diff --git a/tests/cases/fourslash/organizeImportsGroup_Newline.ts b/tests/cases/fourslash/organizeImportsGroup_Newline.ts new file mode 100644 index 0000000000000..9f904ef376904 --- /dev/null +++ b/tests/cases/fourslash/organizeImportsGroup_Newline.ts @@ -0,0 +1,19 @@ +/// + +////import c from "C"; +//// +////import d from "D"; +////import a from "A"; // not count +////import b from "B"; +//// +////console.log(a, b, c, d) + +verify.organizeImports( +`import c from "C"; + +import a from "A"; // not count +import b from "B"; +import d from "D"; + +console.log(a, b, c, d)` +);