@@ -3,7 +3,7 @@ namespace ts.refactor {
33 const refactorName = "Move to a new file" ;
44 registerRefactor ( refactorName , {
55 getAvailableActions ( context ) : ApplicableRefactorInfo [ ] {
6- if ( ! context . preferences . allowTextChangesInNewFiles || getStatementsToMove ( context ) === undefined ) return undefined ;
6+ if ( ! context . preferences . allowTextChangesInNewFiles || getFirstAndLastStatementToMove ( context ) === undefined ) return undefined ;
77 const description = getLocaleSpecificMessage ( Diagnostics . Move_to_a_new_file ) ;
88 return [ { name : refactorName , description, actions : [ { name : refactorName , description } ] } ] ;
99 } ,
@@ -15,7 +15,7 @@ namespace ts.refactor {
1515 }
1616 } ) ;
1717
18- function getStatementsToMove ( context : RefactorContext ) : ReadonlyArray < Statement > | undefined {
18+ function getFirstAndLastStatementToMove ( context : RefactorContext ) : { readonly first : number , readonly afterLast : number } | undefined {
1919 const { file } = context ;
2020 const range = createTextRangeFromSpan ( getRefactorContextSpan ( context ) ) ;
2121 const { statements } = file ;
@@ -28,12 +28,12 @@ namespace ts.refactor {
2828 // Can't be partially into the next node
2929 if ( afterEndNodeIndex !== - 1 && ( afterEndNodeIndex === 0 || statements [ afterEndNodeIndex ] . getStart ( file ) < range . end ) ) return undefined ;
3030
31- return statements . slice ( startNodeIndex , afterEndNodeIndex === - 1 ? statements . length : afterEndNodeIndex ) ;
31+ return { first : startNodeIndex , afterLast : afterEndNodeIndex === - 1 ? statements . length : afterEndNodeIndex } ;
3232 }
3333
34- function doChange ( oldFile : SourceFile , program : Program , toMove : ReadonlyArray < Statement > , changes : textChanges . ChangeTracker , host : LanguageServiceHost ) : void {
34+ function doChange ( oldFile : SourceFile , program : Program , toMove : ToMove , changes : textChanges . ChangeTracker , host : LanguageServiceHost ) : void {
3535 const checker = program . getTypeChecker ( ) ;
36- const usage = getUsageInfo ( oldFile , toMove , checker ) ;
36+ const usage = getUsageInfo ( oldFile , toMove . all , checker ) ;
3737
3838 const currentDirectory = getDirectoryPath ( oldFile . fileName ) ;
3939 const extension = extensionFromPath ( oldFile . fileName ) ;
@@ -46,6 +46,42 @@ namespace ts.refactor {
4646 addNewFileToTsconfig ( program , changes , oldFile . fileName , newFileNameWithExtension , hostGetCanonicalFileName ( host ) ) ;
4747 }
4848
49+ interface StatementRange {
50+ readonly first : Statement ;
51+ readonly last : Statement ;
52+ }
53+ interface ToMove {
54+ readonly all : ReadonlyArray < Statement > ;
55+ readonly ranges : ReadonlyArray < StatementRange > ;
56+ }
57+
58+ // Filters imports out of the range of statements to move. Imports will be copied to the new file anyway, and may still be needed in the old file.
59+ function getStatementsToMove ( context : RefactorContext ) : ToMove | undefined {
60+ const { statements } = context . file ;
61+ const { first, afterLast } = getFirstAndLastStatementToMove ( context ) ! ;
62+ const all : Statement [ ] = [ ] ;
63+ const ranges : StatementRange [ ] = [ ] ;
64+ const rangeToMove = statements . slice ( first , afterLast ) ;
65+ getRangesWhere ( rangeToMove , s => ! isPureImport ( s ) , ( start , afterEnd ) => {
66+ for ( let i = start ; i < afterEnd ; i ++ ) all . push ( rangeToMove [ i ] ) ;
67+ ranges . push ( { first : rangeToMove [ start ] , last : rangeToMove [ afterEnd - 1 ] } ) ;
68+ } ) ;
69+ return { all, ranges } ;
70+ }
71+
72+ function isPureImport ( node : Node ) : boolean {
73+ switch ( node . kind ) {
74+ case SyntaxKind . ImportDeclaration :
75+ return true ;
76+ case SyntaxKind . ImportEqualsDeclaration :
77+ return ! hasModifier ( node , ModifierFlags . Export ) ;
78+ case SyntaxKind . VariableStatement :
79+ return ( node as VariableStatement ) . declarationList . declarations . every ( d => isRequireCall ( d . initializer , /*checkArgumentIsStringLiteralLike*/ true ) ) ;
80+ default :
81+ return false ;
82+ }
83+ }
84+
4985 function addNewFileToTsconfig ( program : Program , changes : textChanges . ChangeTracker , oldFileName : string , newFileNameWithExtension : string , getCanonicalFileName : GetCanonicalFileName ) : void {
5086 const cfg = program . getCompilerOptions ( ) . configFile ;
5187 if ( ! cfg ) return ;
@@ -62,13 +98,13 @@ namespace ts.refactor {
6298 }
6399
64100 function getNewStatements (
65- oldFile : SourceFile , usage : UsageInfo , changes : textChanges . ChangeTracker , toMove : ReadonlyArray < Statement > , program : Program , newModuleName : string ,
101+ oldFile : SourceFile , usage : UsageInfo , changes : textChanges . ChangeTracker , toMove : ToMove , program : Program , newModuleName : string ,
66102 ) : ReadonlyArray < Statement > {
67103 const checker = program . getTypeChecker ( ) ;
68104
69105 if ( ! oldFile . externalModuleIndicator && ! oldFile . commonJsModuleIndicator ) {
70- changes . deleteNodeRange ( oldFile , first ( toMove ) , last ( toMove ) ) ;
71- return toMove ;
106+ deleteMovedStatements ( oldFile , toMove . ranges , changes ) ;
107+ return toMove . all ;
72108 }
73109
74110 const useEs6ModuleSyntax = ! ! oldFile . externalModuleIndicator ;
@@ -77,17 +113,23 @@ namespace ts.refactor {
77113 changes . insertNodeBefore ( oldFile , oldFile . statements [ 0 ] , importsFromNewFile , /*blankLineBetween*/ true ) ;
78114 }
79115
80- deleteUnusedOldImports ( oldFile , toMove , changes , usage . unusedImportsFromOldFile , checker ) ;
81- changes . deleteNodeRange ( oldFile , first ( toMove ) , last ( toMove ) ) ;
116+ deleteUnusedOldImports ( oldFile , toMove . all , changes , usage . unusedImportsFromOldFile , checker ) ;
117+ deleteMovedStatements ( oldFile , toMove . ranges , changes ) ;
82118
83119 updateImportsInOtherFiles ( changes , program , oldFile , usage . movedSymbols , newModuleName ) ;
84120
85121 return [
86122 ...getNewFileImportsAndAddExportInOldFile ( oldFile , usage . oldImportsNeededByNewFile , usage . newFileImportsFromOldFile , changes , checker , useEs6ModuleSyntax ) ,
87- ...addExports ( oldFile , toMove , usage . oldFileImportsFromNewFile , useEs6ModuleSyntax ) ,
123+ ...addExports ( oldFile , toMove . all , usage . oldFileImportsFromNewFile , useEs6ModuleSyntax ) ,
88124 ] ;
89125 }
90126
127+ function deleteMovedStatements ( sourceFile : SourceFile , moved : ReadonlyArray < StatementRange > , changes : textChanges . ChangeTracker ) {
128+ for ( const { first, last } of moved ) {
129+ changes . deleteNodeRange ( sourceFile , first , last ) ;
130+ }
131+ }
132+
91133 function deleteUnusedOldImports ( oldFile : SourceFile , toMove : ReadonlyArray < Statement > , changes : textChanges . ChangeTracker , toDelete : ReadonlySymbolSet , checker : TypeChecker ) {
92134 for ( const statement of oldFile . statements ) {
93135 if ( contains ( toMove , statement ) ) continue ;
0 commit comments