@@ -3,12 +3,17 @@ import {
3
3
codefix ,
4
4
Debug ,
5
5
fileShouldUseJavaScriptRequire ,
6
+ findAncestor ,
6
7
findIndex ,
7
8
forEachChild ,
8
9
formatting ,
10
+ getNewLineOrDefaultFromHost ,
9
11
getQuotePreference ,
12
+ getTokenAtPosition ,
10
13
isIdentifier ,
11
14
Program ,
15
+ rangeContainsPosition ,
16
+ rangeContainsRange ,
12
17
SourceFile ,
13
18
Statement ,
14
19
SymbolFlags ,
@@ -56,17 +61,16 @@ function pasteEdits(
56
61
cancellationToken : CancellationToken ,
57
62
changes : textChanges . ChangeTracker ,
58
63
) {
59
- let actualPastedText : string [ ] | undefined ;
64
+ let actualPastedText : string | undefined ;
60
65
if ( pastedText . length !== pasteLocations . length ) {
61
- actualPastedText = pastedText . length === 1 ? pastedText : [ pastedText . join ( "\n" ) ] ;
66
+ actualPastedText = pastedText . length === 1 ? pastedText [ 0 ] : pastedText . join ( getNewLineOrDefaultFromHost ( formatContext . host , formatContext . options ) ) ;
62
67
}
63
68
64
69
const statements : Statement [ ] = [ ] ;
65
-
66
70
let newText = targetFile . text ;
67
71
for ( let i = pasteLocations . length - 1 ; i >= 0 ; i -- ) {
68
72
const { pos, end } = pasteLocations [ i ] ;
69
- newText = actualPastedText ? newText . slice ( 0 , pos ) + actualPastedText [ 0 ] + newText . slice ( end ) : newText . slice ( 0 , pos ) + pastedText [ i ] + newText . slice ( end ) ;
73
+ newText = actualPastedText ? newText . slice ( 0 , pos ) + actualPastedText + newText . slice ( end ) : newText . slice ( 0 , pos ) + pastedText [ i ] + newText . slice ( end ) ;
70
74
}
71
75
72
76
let importAdder : codefix . ImportAdder ;
@@ -104,12 +108,46 @@ function pasteEdits(
104
108
preferences,
105
109
formatContext,
106
110
} ;
107
- forEachChild ( updatedFile , function cb ( node ) {
108
- if ( isIdentifier ( node ) && ! originalProgram ?. getTypeChecker ( ) . resolveName ( node . text , node , SymbolFlags . All , /*excludeGlobals*/ false ) ) {
109
- // generate imports
110
- importAdder . addImportForUnresolvedIdentifier ( context , node , /*useAutoImportProvider*/ true ) ;
111
- }
112
- node . forEachChild ( cb ) ;
111
+
112
+ // `updatedRanges` represent the new ranges that account for the offset changes caused by pasting new text and
113
+ // `offset` represents by how much the starting position of `pasteLocations` needs to be changed.
114
+ //
115
+ // We iterate over each updated range to get the node that wholly encloses the updated range.
116
+ // For each child of that node, we checked for unresolved identifiers
117
+ // within the updated range and try importing it.
118
+ let offset = 0 ;
119
+ pasteLocations . forEach ( ( location , i ) => {
120
+ const oldTextLength = location . end - location . pos ;
121
+ const textToBePasted = actualPastedText ?? pastedText [ i ] ;
122
+ const startPos = location . pos + offset ;
123
+ const endPos = startPos + textToBePasted . length ;
124
+ const range : TextRange = { pos : startPos , end : endPos } ;
125
+ offset += textToBePasted . length - oldTextLength ;
126
+
127
+ const enclosingNode = findAncestor (
128
+ getTokenAtPosition ( context . sourceFile , range . pos ) ,
129
+ ancestorNode => rangeContainsRange ( ancestorNode , range ) ,
130
+ ) ;
131
+ if ( ! enclosingNode ) return ;
132
+
133
+ forEachChild ( enclosingNode , function importUnresolvedIdentifiers ( node ) {
134
+ const isImportCandidate = isIdentifier ( node ) &&
135
+ rangeContainsPosition ( range , node . getStart ( updatedFile ) ) &&
136
+ ! updatedProgram ?. getTypeChecker ( ) . resolveName (
137
+ node . text ,
138
+ node ,
139
+ SymbolFlags . All ,
140
+ /*excludeGlobals*/ false ,
141
+ ) ;
142
+ if ( isImportCandidate ) {
143
+ return importAdder . addImportForUnresolvedIdentifier (
144
+ context ,
145
+ node ,
146
+ /*useAutoImportProvider*/ true ,
147
+ ) ;
148
+ }
149
+ node . forEachChild ( importUnresolvedIdentifiers ) ;
150
+ } ) ;
113
151
} ) ;
114
152
}
115
153
importAdder . writeFixes ( changes , getQuotePreference ( copiedFrom ? copiedFrom . file : targetFile , preferences ) ) ;
@@ -125,8 +163,7 @@ function pasteEdits(
125
163
changes . replaceRangeWithText (
126
164
targetFile ,
127
165
{ pos : paste . pos , end : paste . end } ,
128
- actualPastedText ?
129
- actualPastedText [ 0 ] : pastedText [ i ] ,
166
+ actualPastedText ?? pastedText [ i ] ,
130
167
) ;
131
168
} ) ;
132
169
}
0 commit comments