|
1 | 1 | import { |
2 | | - append, |
3 | 2 | ApplicableRefactorInfo, |
| 3 | + codefix, |
| 4 | + createFutureSourceFile, |
4 | 5 | Debug, |
5 | 6 | Diagnostics, |
6 | 7 | emptyArray, |
7 | | - fileShouldUseJavaScriptRequire, |
8 | | - getBaseFileName, |
9 | 8 | getLineAndCharacterOfPosition, |
10 | 9 | getLocaleSpecificMessage, |
11 | | - getQuotePreference, |
12 | | - hasSyntacticModifier, |
13 | 10 | hostGetCanonicalFileName, |
14 | | - Identifier, |
15 | | - insertImports, |
16 | | - isPrologueDirective, |
17 | 11 | LanguageServiceHost, |
18 | 12 | last, |
19 | | - ModifierFlags, |
20 | | - nodeSeenTracker, |
| 13 | + ModuleKind, |
21 | 14 | Program, |
22 | | - QuotePreference, |
| 15 | + RefactorContext, |
23 | 16 | RefactorEditInfo, |
24 | 17 | SourceFile, |
25 | | - Symbol, |
26 | | - SyntaxKind, |
27 | | - takeWhile, |
28 | 18 | textChanges, |
29 | | - TypeChecker, |
30 | 19 | UserPreferences, |
31 | 20 | } from "../_namespaces/ts"; |
32 | 21 | import { |
33 | | - addExports, |
34 | | - addExportToChanges, |
35 | 22 | addNewFileToTsconfig, |
36 | 23 | createNewFileName, |
37 | | - createOldFileImportsFromTargetFile, |
38 | | - deleteMovedStatements, |
39 | | - deleteUnusedOldImports, |
40 | | - filterImport, |
41 | | - forEachImportInStatement, |
| 24 | + getNewStatementsAndRemoveFromOldFile, |
42 | 25 | getStatementsToMove, |
43 | | - getTopLevelDeclarationStatement, |
44 | 26 | getUsageInfo, |
45 | | - isTopLevelDeclaration, |
46 | | - makeImportOrRequire, |
47 | | - moduleSpecifierFromImport, |
48 | | - nameOfTopLevelDeclaration, |
49 | 27 | registerRefactor, |
50 | | - SupportedImportStatement, |
51 | 28 | ToMove, |
52 | | - updateImportsInOtherFiles, |
53 | | - UsageInfo, |
54 | 29 | } from "../_namespaces/ts.refactor"; |
55 | 30 |
|
56 | 31 | const refactorName = "Move to a new file"; |
@@ -81,112 +56,19 @@ registerRefactor(refactorName, { |
81 | 56 | getEditsForAction: function getRefactorEditsToMoveToNewFile(context, actionName): RefactorEditInfo { |
82 | 57 | Debug.assert(actionName === refactorName, "Wrong refactor invoked"); |
83 | 58 | const statements = Debug.checkDefined(getStatementsToMove(context)); |
84 | | - const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context.preferences)); |
| 59 | + const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, statements, t, context.host, context, context.preferences)); |
85 | 60 | return { edits, renameFilename: undefined, renameLocation: undefined }; |
86 | 61 | }, |
87 | 62 | }); |
88 | 63 |
|
89 | | -function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, preferences: UserPreferences): void { |
| 64 | +function doChange(oldFile: SourceFile, program: Program, toMove: ToMove, changes: textChanges.ChangeTracker, host: LanguageServiceHost, context: RefactorContext, preferences: UserPreferences): void { |
90 | 65 | const checker = program.getTypeChecker(); |
91 | 66 | const usage = getUsageInfo(oldFile, toMove.all, checker); |
92 | 67 | const newFilename = createNewFileName(oldFile, program, host, toMove); |
| 68 | + const newSourceFile = createFutureSourceFile(newFilename, oldFile.externalModuleIndicator ? ModuleKind.ESNext : oldFile.commonJsModuleIndicator ? ModuleKind.CommonJS : undefined, program, host); |
93 | 69 |
|
94 | | - // If previous file was global, this is easy. |
95 | | - changes.createNewFile(oldFile, newFilename, getNewStatementsAndRemoveFromOldFile(oldFile, usage, changes, toMove, program, host, newFilename, preferences)); |
96 | | - |
| 70 | + const importAdderForOldFile = codefix.createImportAdder(oldFile, context.program, context.preferences, context.host); |
| 71 | + const importAdderForNewFile = codefix.createImportAdder(newSourceFile, context.program, context.preferences, context.host); |
| 72 | + getNewStatementsAndRemoveFromOldFile(oldFile, newSourceFile, usage, changes, toMove, program, host, preferences, importAdderForNewFile, importAdderForOldFile); |
97 | 73 | addNewFileToTsconfig(program, changes, oldFile.fileName, newFilename, hostGetCanonicalFileName(host)); |
98 | 74 | } |
99 | | - |
100 | | -function getNewStatementsAndRemoveFromOldFile( |
101 | | - oldFile: SourceFile, |
102 | | - usage: UsageInfo, |
103 | | - changes: textChanges.ChangeTracker, |
104 | | - toMove: ToMove, |
105 | | - program: Program, |
106 | | - host: LanguageServiceHost, |
107 | | - newFilename: string, |
108 | | - preferences: UserPreferences, |
109 | | -) { |
110 | | - const checker = program.getTypeChecker(); |
111 | | - const prologueDirectives = takeWhile(oldFile.statements, isPrologueDirective); |
112 | | - if (oldFile.externalModuleIndicator === undefined && oldFile.commonJsModuleIndicator === undefined && usage.oldImportsNeededByTargetFile.size === 0) { |
113 | | - deleteMovedStatements(oldFile, toMove.ranges, changes); |
114 | | - return [...prologueDirectives, ...toMove.all]; |
115 | | - } |
116 | | - |
117 | | - const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(newFilename, program, host, !!oldFile.commonJsModuleIndicator); |
118 | | - const quotePreference = getQuotePreference(oldFile, preferences); |
119 | | - const importsFromNewFile = createOldFileImportsFromTargetFile(oldFile, usage.oldFileImportsFromTargetFile, newFilename, program, host, useEsModuleSyntax, quotePreference); |
120 | | - if (importsFromNewFile) { |
121 | | - insertImports(changes, oldFile, importsFromNewFile, /*blankLineBetween*/ true, preferences); |
122 | | - } |
123 | | - |
124 | | - deleteUnusedOldImports(oldFile, toMove.all, changes, usage.unusedImportsFromOldFile, checker); |
125 | | - deleteMovedStatements(oldFile, toMove.ranges, changes); |
126 | | - updateImportsInOtherFiles(changes, program, host, oldFile, usage.movedSymbols, newFilename, quotePreference); |
127 | | - |
128 | | - const imports = getNewFileImportsAndAddExportInOldFile(oldFile, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, changes, checker, program, host, useEsModuleSyntax, quotePreference); |
129 | | - const body = addExports(oldFile, toMove.all, usage.oldFileImportsFromTargetFile, useEsModuleSyntax); |
130 | | - if (imports.length && body.length) { |
131 | | - return [ |
132 | | - ...prologueDirectives, |
133 | | - ...imports, |
134 | | - SyntaxKind.NewLineTrivia as const, |
135 | | - ...body, |
136 | | - ]; |
137 | | - } |
138 | | - |
139 | | - return [ |
140 | | - ...prologueDirectives, |
141 | | - ...imports, |
142 | | - ...body, |
143 | | - ]; |
144 | | -} |
145 | | - |
146 | | -function getNewFileImportsAndAddExportInOldFile( |
147 | | - oldFile: SourceFile, |
148 | | - importsToCopy: Map<Symbol, boolean>, |
149 | | - newFileImportsFromOldFile: Set<Symbol>, |
150 | | - changes: textChanges.ChangeTracker, |
151 | | - checker: TypeChecker, |
152 | | - program: Program, |
153 | | - host: LanguageServiceHost, |
154 | | - useEsModuleSyntax: boolean, |
155 | | - quotePreference: QuotePreference, |
156 | | -): readonly SupportedImportStatement[] { |
157 | | - const copiedOldImports: SupportedImportStatement[] = []; |
158 | | - for (const oldStatement of oldFile.statements) { |
159 | | - forEachImportInStatement(oldStatement, i => { |
160 | | - append(copiedOldImports, filterImport(i, moduleSpecifierFromImport(i), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); |
161 | | - }); |
162 | | - } |
163 | | - |
164 | | - // Also, import things used from the old file, and insert 'export' modifiers as necessary in the old file. |
165 | | - let oldFileDefault: Identifier | undefined; |
166 | | - const oldFileNamedImports: string[] = []; |
167 | | - const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`. |
168 | | - newFileImportsFromOldFile.forEach(symbol => { |
169 | | - if (!symbol.declarations) { |
170 | | - return; |
171 | | - } |
172 | | - for (const decl of symbol.declarations) { |
173 | | - if (!isTopLevelDeclaration(decl)) continue; |
174 | | - const name = nameOfTopLevelDeclaration(decl); |
175 | | - if (!name) continue; |
176 | | - |
177 | | - const top = getTopLevelDeclarationStatement(decl); |
178 | | - if (markSeenTop(top)) { |
179 | | - addExportToChanges(oldFile, top, name, changes, useEsModuleSyntax); |
180 | | - } |
181 | | - if (hasSyntacticModifier(decl, ModifierFlags.Default)) { |
182 | | - oldFileDefault = name; |
183 | | - } |
184 | | - else { |
185 | | - oldFileNamedImports.push(name.text); |
186 | | - } |
187 | | - } |
188 | | - }); |
189 | | - |
190 | | - append(copiedOldImports, makeImportOrRequire(oldFile, oldFileDefault, oldFileNamedImports, getBaseFileName(oldFile.fileName), program, host, useEsModuleSyntax, quotePreference)); |
191 | | - return copiedOldImports; |
192 | | -} |
0 commit comments