|
| 1 | +import { addRange } from "../compiler/core.js"; |
| 2 | +import { |
| 3 | + CancellationToken, |
| 4 | + Program, |
| 5 | + SourceFile, |
| 6 | + Statement, |
| 7 | + SymbolFlags, |
| 8 | + TextRange, |
| 9 | + UserPreferences, |
| 10 | +} from "../compiler/types.js"; |
| 11 | +import { getLineOfLocalPosition } from "../compiler/utilities.js"; |
| 12 | +import { |
| 13 | + codefix, |
| 14 | + Debug, |
| 15 | + fileShouldUseJavaScriptRequire, |
| 16 | + forEachChild, |
| 17 | + formatting, |
| 18 | + getQuotePreference, |
| 19 | + isIdentifier, |
| 20 | + textChanges, |
| 21 | +} from "./_namespaces/ts.js"; |
| 22 | +import { addTargetFileImports } from "./refactors/helpers.js"; |
| 23 | +import { |
| 24 | + addExportsInOldFile, |
| 25 | + getExistingLocals, |
| 26 | + getUsageInfo, |
| 27 | +} from "./refactors/moveToFile.js"; |
| 28 | +import { |
| 29 | + CodeFixContextBase, |
| 30 | + FileTextChanges, |
| 31 | + LanguageServiceHost, |
| 32 | + PasteEdits, |
| 33 | +} from "./types.js"; |
| 34 | + |
| 35 | +const fixId = "providePostPasteEdits"; |
| 36 | +/** @internal */ |
| 37 | +export function pasteEditsProvider( |
| 38 | + targetFile: SourceFile, |
| 39 | + pastedText: string[], |
| 40 | + pasteLocations: TextRange[], |
| 41 | + copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined, |
| 42 | + host: LanguageServiceHost, |
| 43 | + preferences: UserPreferences, |
| 44 | + formatContext: formatting.FormatContext, |
| 45 | + cancellationToken: CancellationToken, |
| 46 | +): PasteEdits { |
| 47 | + const changes: FileTextChanges[] = textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => pasteEdits(targetFile, pastedText, pasteLocations, copiedFrom, host, preferences, formatContext, cancellationToken, changeTracker)); |
| 48 | + return { edits: changes, fixId }; |
| 49 | +} |
| 50 | + |
| 51 | +function pasteEdits( |
| 52 | + targetFile: SourceFile, |
| 53 | + pastedText: string[], |
| 54 | + pasteLocations: TextRange[], |
| 55 | + copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined, |
| 56 | + host: LanguageServiceHost, |
| 57 | + preferences: UserPreferences, |
| 58 | + formatContext: formatting.FormatContext, |
| 59 | + cancellationToken: CancellationToken, |
| 60 | + changes: textChanges.ChangeTracker, |
| 61 | +) { |
| 62 | + let actualPastedText: string[] | undefined; |
| 63 | + if (pastedText.length !== pasteLocations.length) { |
| 64 | + actualPastedText = pastedText.length === 1 ? pastedText : [pastedText.join("\n")]; |
| 65 | + } |
| 66 | + pasteLocations.forEach((paste, i) => { |
| 67 | + changes.replaceRangeWithText( |
| 68 | + targetFile, |
| 69 | + { pos: paste.pos, end: paste.end }, |
| 70 | + actualPastedText ? |
| 71 | + actualPastedText[0] : pastedText[i], |
| 72 | + ); |
| 73 | + }); |
| 74 | + |
| 75 | + const statements: Statement[] = []; |
| 76 | + |
| 77 | + let newText = targetFile.text; |
| 78 | + for (let i = pasteLocations.length - 1; i >= 0; i--) { |
| 79 | + const { pos, end } = pasteLocations[i]; |
| 80 | + newText = actualPastedText ? newText.slice(0, pos) + actualPastedText[0] + newText.slice(end) : newText.slice(0, pos) + pastedText[i] + newText.slice(end); |
| 81 | + } |
| 82 | + |
| 83 | + Debug.checkDefined(host.runWithTemporaryFileUpdate).call(host, targetFile.fileName, newText, (updatedProgram: Program, originalProgram: Program | undefined, updatedFile: SourceFile) => { |
| 84 | + const importAdder = codefix.createImportAdder(updatedFile, updatedProgram, preferences, host); |
| 85 | + if (copiedFrom?.range) { |
| 86 | + Debug.assert(copiedFrom.range.length === pastedText.length); |
| 87 | + copiedFrom.range.forEach(copy => { |
| 88 | + addRange(statements, copiedFrom.file.statements, getLineOfLocalPosition(copiedFrom.file, copy.pos), getLineOfLocalPosition(copiedFrom.file, copy.end) + 1); |
| 89 | + }); |
| 90 | + const usage = getUsageInfo(copiedFrom.file, statements, originalProgram!.getTypeChecker(), getExistingLocals(updatedFile, statements, originalProgram!.getTypeChecker())); |
| 91 | + Debug.assertIsDefined(originalProgram); |
| 92 | + const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(targetFile.fileName, originalProgram, host, !!copiedFrom.file.commonJsModuleIndicator); |
| 93 | + addExportsInOldFile(copiedFrom.file, usage.targetFileImportsFromOldFile, changes, useEsModuleSyntax); |
| 94 | + addTargetFileImports(copiedFrom.file, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, originalProgram.getTypeChecker(), updatedProgram, importAdder); |
| 95 | + } |
| 96 | + else { |
| 97 | + const context: CodeFixContextBase = { |
| 98 | + sourceFile: updatedFile, |
| 99 | + program: originalProgram!, |
| 100 | + cancellationToken, |
| 101 | + host, |
| 102 | + preferences, |
| 103 | + formatContext, |
| 104 | + }; |
| 105 | + forEachChild(updatedFile, function cb(node) { |
| 106 | + if (isIdentifier(node) && !originalProgram?.getTypeChecker().resolveName(node.text, node, SymbolFlags.All, /*excludeGlobals*/ false)) { |
| 107 | + // generate imports |
| 108 | + importAdder.addImportForUnresolvedIdentifier(context, node, /*useAutoImportProvider*/ true); |
| 109 | + } |
| 110 | + node.forEachChild(cb); |
| 111 | + }); |
| 112 | + } |
| 113 | + importAdder.writeFixes(changes, getQuotePreference(copiedFrom ? copiedFrom.file : targetFile, preferences)); |
| 114 | + }); |
| 115 | +} |
0 commit comments