@@ -3,12 +3,17 @@ import {
33    codefix , 
44    Debug , 
55    fileShouldUseJavaScriptRequire , 
6+     findAncestor , 
67    findIndex , 
78    forEachChild , 
89    formatting , 
10+     getNewLineOrDefaultFromHost , 
911    getQuotePreference , 
12+     getTokenAtPosition , 
1013    isIdentifier , 
1114    Program , 
15+     rangeContainsPosition , 
16+     rangeContainsRange , 
1217    SourceFile , 
1318    Statement , 
1419    SymbolFlags , 
@@ -56,17 +61,16 @@ function pasteEdits(
5661    cancellationToken : CancellationToken , 
5762    changes : textChanges . ChangeTracker , 
5863)  { 
59-     let  actualPastedText : string [ ]  |  undefined ; 
64+     let  actualPastedText : string  |  undefined ; 
6065    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 ) ) ; 
6267    } 
6368
6469    const  statements : Statement [ ]  =  [ ] ; 
65- 
6670    let  newText  =  targetFile . text ; 
6771    for  ( let  i  =  pasteLocations . length  -  1 ;  i  >=  0 ;  i -- )  { 
6872        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 ) ; 
7074    } 
7175
7276    let  importAdder : codefix . ImportAdder ; 
@@ -104,12 +108,46 @@ function pasteEdits(
104108                preferences, 
105109                formatContext, 
106110            } ; 
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+                 } ) ; 
113151            } ) ; 
114152        } 
115153        importAdder . writeFixes ( changes ,  getQuotePreference ( copiedFrom  ? copiedFrom . file  : targetFile ,  preferences ) ) ; 
@@ -125,8 +163,7 @@ function pasteEdits(
125163        changes . replaceRangeWithText ( 
126164            targetFile , 
127165            {  pos : paste . pos ,  end : paste . end  } , 
128-             actualPastedText  ?
129-                 actualPastedText [ 0 ]  : pastedText [ i ] , 
166+             actualPastedText  ??  pastedText [ i ] , 
130167        ) ; 
131168    } ) ; 
132169} 
0 commit comments