Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 13.7k
Implementing copy/paste#57262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Implementing copy/paste #57262
Changes from all commits
36e67a45d931f817f989764da77e89ef0724fe48f617ff23a1b89d2ee6515eabfe79a9ab542b59ef3d85281e2f17f41f30d3816d3391453983a4328c6b6f23c8c3e295192ff2215e778ccd35b11fe2754c67137be83eae1392dd75d7cbcd0778e2b4f302b657027ea3a9f71278e486d6835acda3b79cb1d7f7b1582fc3bf5e7e39b0ef71614980cc57c6a146eaa1aaf4ec4ccc1a369514d37a0666ca5bee8189c3213587e68ce4055e1ff78a3fbd698c238991958d1a3fe8f48File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -2238,6 +2238,18 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo | ||
| return this.noDtsResolutionProject; | ||
| } | ||
| /** @internal */ | ||
| runWithTemporaryFileUpdate(rootFile: string, updatedText: string, cb: (updatedProgram: Program, originalProgram: Program | undefined, updatedFile: SourceFile) => void) { | ||
| const originalProgram = this.program; | ||
| const originalText = this.program?.getSourceFile(rootFile)?.getText(); | ||
| Debug.assert(this.program && this.program.getSourceFile(rootFile) && originalText); | ||
| this.getScriptInfo(rootFile)?.editContent(0, this.program.getSourceFile(rootFile)!.getText().length, updatedText); | ||
| this.updateGraph(); | ||
sheetalkamat marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| cb(this.program, originalProgram, (this.program?.getSourceFile(rootFile))!); | ||
| this.getScriptInfo(rootFile)?.editContent(0, this.program.getSourceFile(rootFile)!.getText().length, originalText); | ||
| } | ||
| /** @internal */ | ||
| private getCompilerOptionsForNoDtsResolutionProject() { | ||
| return { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -161,6 +161,7 @@ export const enum CommandTypes { | ||
| GetApplicableRefactors = "getApplicableRefactors", | ||
| GetEditsForRefactor = "getEditsForRefactor", | ||
| GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions", | ||
| GetPasteEdits = "getPasteEdits", | ||
| /** @internal */ | ||
| GetEditsForRefactorFull = "getEditsForRefactor-full", | ||
| @@ -625,6 +626,35 @@ export interface GetMoveToRefactoringFileSuggestions extends Response { | ||
| }; | ||
| } | ||
| /** | ||
| * Request refactorings at a given position post pasting text from some other location. | ||
| */ | ||
| export interface GetPasteEditsRequest extends Request { | ||
| command: CommandTypes.GetPasteEdits; | ||
| arguments: GetPasteEditsRequestArgs; | ||
| } | ||
| export interface GetPasteEditsRequestArgs extends FileRequestArgs { | ||
| /** The text that gets pasted in a file. */ | ||
| pastedText: string[]; | ||
| /** Locations of where the `pastedText` gets added in a file. If the length of the `pastedText` and `pastedLocations` are not the same, | ||
| * then the `pastedText` is combined into one and added at all the `pastedLocations`. | ||
| */ | ||
| pasteLocations: TextSpan[]; | ||
| /** The source location of each `pastedText`. If present, the length of `spans` must be equal to the length of `pastedText`. */ | ||
| copiedFrom?: { file: string; spans: TextSpan[]; }; | ||
| } | ||
| export interface GetPasteEditsResponse extends Response { | ||
| body: PasteEditsAction; | ||
| } | ||
| export interface PasteEditsAction { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the future we will likely want a navya9singh marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| edits: FileCodeEdits[]; | ||
| fixId?: {}; | ||
| } | ||
| export interface GetEditsForRefactorRequest extends Request { | ||
| command: CommandTypes.GetEditsForRefactor; | ||
| arguments: GetEditsForRefactorRequestArgs; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from "../pasteEdits.js"; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,115 @@ | ||
| import { addRange } from "../compiler/core.js"; | ||
| import { | ||
| CancellationToken, | ||
| Program, | ||
| SourceFile, | ||
| Statement, | ||
| SymbolFlags, | ||
| TextRange, | ||
| UserPreferences, | ||
| } from "../compiler/types.js"; | ||
| import { getLineOfLocalPosition } from "../compiler/utilities.js"; | ||
| import { | ||
| codefix, | ||
| Debug, | ||
| fileShouldUseJavaScriptRequire, | ||
| forEachChild, | ||
| formatting, | ||
| getQuotePreference, | ||
| isIdentifier, | ||
| textChanges, | ||
| } from "./_namespaces/ts.js"; | ||
| import { addTargetFileImports } from "./refactors/helpers.js"; | ||
| import { | ||
| addExportsInOldFile, | ||
| getExistingLocals, | ||
| getUsageInfo, | ||
| } from "./refactors/moveToFile.js"; | ||
| import { | ||
| CodeFixContextBase, | ||
| FileTextChanges, | ||
| LanguageServiceHost, | ||
| PasteEdits, | ||
| } from "./types.js"; | ||
| const fixId = "providePostPasteEdits"; | ||
| /** @internal */ | ||
| export function pasteEditsProvider( | ||
| targetFile: SourceFile, | ||
| pastedText: string[], | ||
| pasteLocations: TextRange[], | ||
| copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined, | ||
| host: LanguageServiceHost, | ||
| preferences: UserPreferences, | ||
| formatContext: formatting.FormatContext, | ||
| cancellationToken: CancellationToken, | ||
| ): PasteEdits { | ||
| const changes: FileTextChanges[] = textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => pasteEdits(targetFile, pastedText, pasteLocations, copiedFrom, host, preferences, formatContext, cancellationToken, changeTracker)); | ||
| return { edits: changes, fixId }; | ||
| } | ||
| function pasteEdits( | ||
| targetFile: SourceFile, | ||
| pastedText: string[], | ||
| pasteLocations: TextRange[], | ||
| copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined, | ||
| host: LanguageServiceHost, | ||
| preferences: UserPreferences, | ||
| formatContext: formatting.FormatContext, | ||
| cancellationToken: CancellationToken, | ||
| changes: textChanges.ChangeTracker, | ||
| ) { | ||
| let actualPastedText: string[] | undefined; | ||
| if (pastedText.length !== pasteLocations.length) { | ||
| actualPastedText = pastedText.length === 1 ? pastedText : [pastedText.join("\n")]; | ||
| } | ||
| pasteLocations.forEach((paste, i) => { | ||
| changes.replaceRangeWithText( | ||
| targetFile, | ||
| { pos: paste.pos, end: paste.end }, | ||
| actualPastedText ? | ||
| actualPastedText[0] : pastedText[i], | ||
| ); | ||
| }); | ||
| const statements: Statement[] = []; | ||
| let newText = targetFile.text; | ||
| for (let i = pasteLocations.length - 1; i >= 0; i--) { | ||
| const { pos, end } = pasteLocations[i]; | ||
| newText = actualPastedText ? newText.slice(0, pos) + actualPastedText[0] + newText.slice(end) : newText.slice(0, pos) + pastedText[i] + newText.slice(end); | ||
| } | ||
| Debug.checkDefined(host.runWithTemporaryFileUpdate).call(host, targetFile.fileName, newText, (updatedProgram: Program, originalProgram: Program | undefined, updatedFile: SourceFile) => { | ||
| const importAdder = codefix.createImportAdder(updatedFile, updatedProgram, preferences, host); | ||
| if (copiedFrom?.range) { | ||
| Debug.assert(copiedFrom.range.length === pastedText.length); | ||
| copiedFrom.range.forEach(copy => { | ||
| addRange(statements, copiedFrom.file.statements, getLineOfLocalPosition(copiedFrom.file, copy.pos), getLineOfLocalPosition(copiedFrom.file, copy.end) + 1); | ||
| }); | ||
| const usage = getUsageInfo(copiedFrom.file, statements, originalProgram!.getTypeChecker(), getExistingLocals(updatedFile, statements, originalProgram!.getTypeChecker())); | ||
| Debug.assertIsDefined(originalProgram); | ||
| const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(targetFile.fileName, originalProgram, host, !!copiedFrom.file.commonJsModuleIndicator); | ||
| addExportsInOldFile(copiedFrom.file, usage.targetFileImportsFromOldFile, changes, useEsModuleSyntax); | ||
| addTargetFileImports(copiedFrom.file, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, originalProgram.getTypeChecker(), updatedProgram, importAdder); | ||
| } | ||
| else { | ||
| const context: CodeFixContextBase = { | ||
| sourceFile: updatedFile, | ||
| program: originalProgram!, | ||
| cancellationToken, | ||
| host, | ||
| preferences, | ||
| formatContext, | ||
| }; | ||
| forEachChild(updatedFile, function cb(node) { | ||
| if (isIdentifier(node) && !originalProgram?.getTypeChecker().resolveName(node.text, node, SymbolFlags.All, /*excludeGlobals*/ false)) { | ||
| // generate imports | ||
| importAdder.addImportForUnresolvedIdentifier(context, node, /*useAutoImportProvider*/ true); | ||
| } | ||
| node.forEachChild(cb); | ||
| }); | ||
| } | ||
| importAdder.writeFixes(changes, getQuotePreference(copiedFrom ? copiedFrom.file : targetFile, preferences)); | ||
| }); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.