@@ -19,15 +19,15 @@ namespace ts.codefix {
19
19
const changes = textChanges . ChangeTracker . with ( context , t => t . deleteNode ( sourceFile , importDecl ) ) ;
20
20
return [ createCodeFixAction ( fixName , changes , [ Diagnostics . Remove_import_from_0 , showModuleSpecifier ( importDecl ) ] , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ] ;
21
21
}
22
- const delDestructure = textChanges . ChangeTracker . with ( context , t => tryDeleteFullDestructure ( t , sourceFile , context . span . start ) ) ;
22
+ const delDestructure = textChanges . ChangeTracker . with ( context , t => tryDeleteFullDestructure ( t , sourceFile , context . span . start , /*deleted*/ undefined ) ) ;
23
23
if ( delDestructure . length ) {
24
24
return [ createCodeFixAction ( fixName , delDestructure , Diagnostics . Remove_destructuring , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ] ;
25
25
}
26
26
27
27
const token = getToken ( sourceFile , textSpanEnd ( context . span ) ) ;
28
28
const result : CodeFixAction [ ] = [ ] ;
29
29
30
- const deletion = textChanges . ChangeTracker . with ( context , t => tryDeleteDeclaration ( t , sourceFile , token ) ) ;
30
+ const deletion = textChanges . ChangeTracker . with ( context , t => tryDeleteDeclaration ( t , sourceFile , token , /*deleted*/ undefined ) ) ;
31
31
if ( deletion . length ) {
32
32
result . push ( createCodeFixAction ( fixName , deletion , [ Diagnostics . Remove_declaration_for_Colon_0 , token . getText ( sourceFile ) ] , fixIdDelete , Diagnostics . Delete_all_unused_declarations ) ) ;
33
33
}
@@ -40,30 +40,37 @@ namespace ts.codefix {
40
40
return result ;
41
41
} ,
42
42
fixIds : [ fixIdPrefix , fixIdDelete ] ,
43
- getAllCodeActions : context => codeFixAll ( context , errorCodes , ( changes , diag ) => {
44
- const { sourceFile } = context ;
45
- const token = findPrecedingToken ( textSpanEnd ( diag ) , diag . file ! ) ;
46
- switch ( context . fixId ) {
47
- case fixIdPrefix :
48
- if ( isIdentifier ( token ) && canPrefix ( token ) ) {
49
- tryPrefixDeclaration ( changes , diag . code , sourceFile , token ) ;
50
- }
51
- break ;
52
- case fixIdDelete :
53
- const importDecl = tryGetFullImport ( diag . file ! , diag . start ! ) ;
54
- if ( importDecl ) {
55
- changes . deleteNode ( sourceFile , importDecl ) ;
56
- }
57
- else {
58
- if ( ! tryDeleteFullDestructure ( changes , sourceFile , diag . start ! ) ) {
59
- tryDeleteDeclaration ( changes , sourceFile , token ) ;
43
+ getAllCodeActions : context => {
44
+ // Track a set of deleted nodes that may be ancestors of other marked for deletion -- only delete the ancestors.
45
+ const deleted = new NodeSet ( ) ;
46
+ return codeFixAll ( context , errorCodes , ( changes , diag ) => {
47
+ const { sourceFile } = context ;
48
+ const token = findPrecedingToken ( textSpanEnd ( diag ) , diag . file ! ) ;
49
+ switch ( context . fixId ) {
50
+ case fixIdPrefix :
51
+ if ( isIdentifier ( token ) && canPrefix ( token ) ) {
52
+ tryPrefixDeclaration ( changes , diag . code , sourceFile , token ) ;
60
53
}
61
- }
62
- break ;
63
- default :
64
- Debug . fail ( JSON . stringify ( context . fixId ) ) ;
65
- }
66
- } ) ,
54
+ break ;
55
+ case fixIdDelete :
56
+ // Ignore if this range was already deleted.
57
+ if ( deleted . some ( d => rangeContainsPosition ( d , diag . start ! ) ) ) break ;
58
+
59
+ const importDecl = tryGetFullImport ( diag . file ! , diag . start ! ) ;
60
+ if ( importDecl ) {
61
+ changes . deleteNode ( sourceFile , importDecl ) ;
62
+ }
63
+ else {
64
+ if ( ! tryDeleteFullDestructure ( changes , sourceFile , diag . start ! , deleted ) ) {
65
+ tryDeleteDeclaration ( changes , sourceFile , token , deleted ) ;
66
+ }
67
+ }
68
+ break ;
69
+ default :
70
+ Debug . fail ( JSON . stringify ( context . fixId ) ) ;
71
+ }
72
+ } ) ;
73
+ } ,
67
74
} ) ;
68
75
69
76
// Sometimes the diagnostic span is an entire ImportDeclaration, so we should remove the whole thing.
@@ -72,18 +79,20 @@ namespace ts.codefix {
72
79
return startToken . kind === SyntaxKind . ImportKeyword ? tryCast ( startToken . parent , isImportDeclaration ) : undefined ;
73
80
}
74
81
75
- function tryDeleteFullDestructure ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , pos : number ) : boolean {
82
+ function tryDeleteFullDestructure ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , pos : number , deletedAncestors : NodeSet | undefined ) : boolean {
76
83
const startToken = getTokenAtPosition ( sourceFile , pos , /*includeJsDocComment*/ false ) ;
77
84
if ( startToken . kind !== SyntaxKind . OpenBraceToken || ! isObjectBindingPattern ( startToken . parent ) ) return false ;
78
85
const decl = startToken . parent . parent ;
79
86
switch ( decl . kind ) {
80
87
case SyntaxKind . VariableDeclaration :
81
- tryDeleteVariableDeclaration ( changes , sourceFile , decl ) ;
88
+ tryDeleteVariableDeclaration ( changes , sourceFile , decl , deletedAncestors ) ;
82
89
break ;
83
90
case SyntaxKind . Parameter :
91
+ if ( deletedAncestors ) deletedAncestors . add ( decl ) ;
84
92
changes . deleteNodeInList ( sourceFile , decl ) ;
85
93
break ;
86
94
case SyntaxKind . BindingElement :
95
+ if ( deletedAncestors ) deletedAncestors . add ( decl ) ;
87
96
changes . deleteNode ( sourceFile , decl ) ;
88
97
break ;
89
98
default :
@@ -121,34 +130,37 @@ namespace ts.codefix {
121
130
return false ;
122
131
}
123
132
124
- function tryDeleteDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node ) : void {
133
+ function tryDeleteDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node , deletedAncestors : NodeSet | undefined ) : void {
125
134
switch ( token . kind ) {
126
135
case SyntaxKind . Identifier :
127
- tryDeleteIdentifier ( changes , sourceFile , < Identifier > token ) ;
136
+ tryDeleteIdentifier ( changes , sourceFile , < Identifier > token , deletedAncestors ) ;
128
137
break ;
129
138
case SyntaxKind . PropertyDeclaration :
130
139
case SyntaxKind . NamespaceImport :
140
+ if ( deletedAncestors ) deletedAncestors . add ( token . parent ) ;
131
141
changes . deleteNode ( sourceFile , token . parent ) ;
132
142
break ;
133
143
default :
134
- tryDeleteDefault ( changes , sourceFile , token ) ;
144
+ tryDeleteDefault ( changes , sourceFile , token , deletedAncestors ) ;
135
145
}
136
146
}
137
147
138
- function tryDeleteDefault ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node ) : void {
148
+ function tryDeleteDefault ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , token : Node , deletedAncestors : NodeSet | undefined ) : void {
139
149
if ( isDeclarationName ( token ) ) {
150
+ if ( deletedAncestors ) deletedAncestors . add ( token . parent ) ;
140
151
changes . deleteNode ( sourceFile , token . parent ) ;
141
152
}
142
153
else if ( isLiteralComputedPropertyDeclarationName ( token ) ) {
154
+ if ( deletedAncestors ) deletedAncestors . add ( token . parent . parent ) ;
143
155
changes . deleteNode ( sourceFile , token . parent . parent ) ;
144
156
}
145
157
}
146
158
147
- function tryDeleteIdentifier ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , identifier : Identifier ) : void {
159
+ function tryDeleteIdentifier ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , identifier : Identifier , deletedAncestors : NodeSet | undefined ) : void {
148
160
const parent = identifier . parent ;
149
161
switch ( parent . kind ) {
150
162
case SyntaxKind . VariableDeclaration :
151
- tryDeleteVariableDeclaration ( changes , sourceFile , < VariableDeclaration > parent ) ;
163
+ tryDeleteVariableDeclaration ( changes , sourceFile , < VariableDeclaration > parent , deletedAncestors ) ;
152
164
break ;
153
165
154
166
case SyntaxKind . TypeParameter :
@@ -255,7 +267,7 @@ namespace ts.codefix {
255
267
break ;
256
268
257
269
default :
258
- tryDeleteDefault ( changes , sourceFile , identifier ) ;
270
+ tryDeleteDefault ( changes , sourceFile , identifier , deletedAncestors ) ;
259
271
break ;
260
272
}
261
273
}
@@ -280,15 +292,17 @@ namespace ts.codefix {
280
292
}
281
293
282
294
// token.parent is a variableDeclaration
283
- function tryDeleteVariableDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , varDecl : VariableDeclaration ) : void {
295
+ function tryDeleteVariableDeclaration ( changes : textChanges . ChangeTracker , sourceFile : SourceFile , varDecl : VariableDeclaration , deletedAncestors : NodeSet | undefined ) : void {
284
296
switch ( varDecl . parent . parent . kind ) {
285
297
case SyntaxKind . ForStatement : {
286
298
const forStatement = varDecl . parent . parent ;
287
299
const forInitializer = < VariableDeclarationList > forStatement . initializer ;
288
300
if ( forInitializer . declarations . length === 1 ) {
301
+ if ( deletedAncestors ) deletedAncestors . add ( forInitializer ) ;
289
302
changes . deleteNode ( sourceFile , forInitializer ) ;
290
303
}
291
304
else {
305
+ if ( deletedAncestors ) deletedAncestors . add ( varDecl ) ;
292
306
changes . deleteNodeInList ( sourceFile , varDecl ) ;
293
307
}
294
308
break ;
@@ -298,6 +312,7 @@ namespace ts.codefix {
298
312
const forOfStatement = varDecl . parent . parent ;
299
313
Debug . assert ( forOfStatement . initializer . kind === SyntaxKind . VariableDeclarationList ) ;
300
314
const forOfInitializer = < VariableDeclarationList > forOfStatement . initializer ;
315
+ if ( deletedAncestors ) deletedAncestors . add ( forOfInitializer . declarations [ 0 ] ) ;
301
316
changes . replaceNode ( sourceFile , forOfInitializer . declarations [ 0 ] , createObjectLiteral ( ) ) ;
302
317
break ;
303
318
@@ -308,11 +323,25 @@ namespace ts.codefix {
308
323
default :
309
324
const variableStatement = varDecl . parent . parent ;
310
325
if ( variableStatement . declarationList . declarations . length === 1 ) {
326
+ if ( deletedAncestors ) deletedAncestors . add ( variableStatement ) ;
311
327
changes . deleteNode ( sourceFile , variableStatement ) ;
312
328
}
313
329
else {
330
+ if ( deletedAncestors ) deletedAncestors . add ( varDecl ) ;
314
331
changes . deleteNodeInList ( sourceFile , varDecl ) ;
315
332
}
316
333
}
317
334
}
335
+
336
+ class NodeSet {
337
+ private map = createMap < Node > ( ) ;
338
+
339
+ add ( node : Node ) : void {
340
+ this . map . set ( String ( getNodeId ( node ) ) , node ) ;
341
+ }
342
+
343
+ some ( pred : ( node : Node ) => boolean ) : boolean {
344
+ return forEachEntry ( this . map , pred ) || false ;
345
+ }
346
+ }
318
347
}
0 commit comments