@@ -15,16 +15,15 @@ namespace ts.OrganizeImports {
15
15
preferences : UserPreferences ,
16
16
skipDestructiveCodeActions ?: boolean
17
17
) {
18
-
19
18
const changeTracker = textChanges . ChangeTracker . fromContext ( { host, formatContext, preferences } ) ;
20
19
21
20
const coalesceAndOrganizeImports = ( importGroup : readonly ImportDeclaration [ ] ) => stableSort (
22
21
coalesceImports ( removeUnusedImports ( importGroup , sourceFile , program , skipDestructiveCodeActions ) ) ,
23
22
( s1 , s2 ) => compareImportsOrRequireStatements ( s1 , s2 ) ) ;
24
23
25
24
// All of the old ImportDeclarations in the file, in syntactic order.
26
- const topLevelImportDecls = sourceFile . statements . filter ( isImportDeclaration ) ;
27
- organizeImportsWorker ( topLevelImportDecls , coalesceAndOrganizeImports ) ;
25
+ const topLevelImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , sourceFile . statements . filter ( isImportDeclaration ) ) ;
26
+ topLevelImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , coalesceAndOrganizeImports ) ) ;
28
27
29
28
// All of the old ExportDeclarations in the file, in syntactic order.
30
29
const topLevelExportDecls = sourceFile . statements . filter ( isExportDeclaration ) ;
@@ -33,8 +32,8 @@ namespace ts.OrganizeImports {
33
32
for ( const ambientModule of sourceFile . statements . filter ( isAmbientModule ) ) {
34
33
if ( ! ambientModule . body ) continue ;
35
34
36
- const ambientModuleImportDecls = ambientModule . body . statements . filter ( isImportDeclaration ) ;
37
- organizeImportsWorker ( ambientModuleImportDecls , coalesceAndOrganizeImports ) ;
35
+ const ambientModuleImportGroupDecls = groupImportsByNewlineContiguous ( sourceFile , ambientModule . body . statements . filter ( isImportDeclaration ) ) ;
36
+ ambientModuleImportGroupDecls . forEach ( importGroupDecl => organizeImportsWorker ( importGroupDecl , coalesceAndOrganizeImports ) ) ;
38
37
39
38
const ambientModuleExportDecls = ambientModule . body . statements . filter ( isExportDeclaration ) ;
40
39
organizeImportsWorker ( ambientModuleExportDecls , coalesceExports ) ;
@@ -88,6 +87,48 @@ namespace ts.OrganizeImports {
88
87
}
89
88
}
90
89
90
+ function groupImportsByNewlineContiguous ( sourceFile : SourceFile , importDecls : ImportDeclaration [ ] ) : ImportDeclaration [ ] [ ] {
91
+ const scanner = createScanner ( sourceFile . languageVersion , /*skipTrivia*/ false , sourceFile . languageVariant ) ;
92
+ const groupImports : ImportDeclaration [ ] [ ] = [ ] ;
93
+ let groupIndex = 0 ;
94
+ for ( const topLevelImportDecl of importDecls ) {
95
+ if ( isNewGroup ( sourceFile , topLevelImportDecl , scanner ) ) {
96
+ groupIndex ++ ;
97
+ }
98
+
99
+ if ( ! groupImports [ groupIndex ] ) {
100
+ groupImports [ groupIndex ] = [ ] ;
101
+ }
102
+
103
+ groupImports [ groupIndex ] . push ( topLevelImportDecl ) ;
104
+ }
105
+
106
+ return groupImports ;
107
+ }
108
+
109
+ // a new group is created if an import includes at least two new line
110
+ // new line from multi-line comment doesn't count
111
+ function isNewGroup ( sourceFile : SourceFile , topLevelImportDecl : ImportDeclaration , scanner : Scanner ) {
112
+ const startPos = topLevelImportDecl . getFullStart ( ) ;
113
+ const endPos = topLevelImportDecl . getStart ( ) ;
114
+ scanner . setText ( sourceFile . text , startPos , endPos - startPos ) ;
115
+
116
+ let numberOfNewLines = 0 ;
117
+ while ( scanner . getTokenPos ( ) < endPos ) {
118
+ const tokenKind = scanner . scan ( ) ;
119
+
120
+ if ( tokenKind === SyntaxKind . NewLineTrivia ) {
121
+ numberOfNewLines ++ ;
122
+
123
+ if ( numberOfNewLines >= 2 ) {
124
+ return true ;
125
+ }
126
+ }
127
+ }
128
+
129
+ return false ;
130
+ }
131
+
91
132
function removeUnusedImports ( oldImports : readonly ImportDeclaration [ ] , sourceFile : SourceFile , program : Program , skipDestructiveCodeActions : boolean | undefined ) {
92
133
// As a precaution, consider unused import detection to be destructive (GH #43051)
93
134
if ( skipDestructiveCodeActions ) {
0 commit comments