Skip to content

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

Merged
merged 53 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
36e67a4
Testing basic changes
navya9singh Jan 18, 2024
5d931f8
Basix structure working
navya9singh Jan 26, 2024
17f9897
Allows different text in each cursor location
navya9singh Feb 1, 2024
64da77e
working changes
navya9singh Feb 1, 2024
89ef072
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 1, 2024
4fe48f6
removing extra code
navya9singh Feb 1, 2024
17ff23a
Fixing protocol changes and tests
navya9singh Feb 9, 2024
1b89d2e
Removing deleted files
navya9singh Feb 9, 2024
e6515ea
Removing deleted files
navya9singh Feb 9, 2024
bfe79a9
fixing eslint errors
navya9singh Feb 9, 2024
ab542b5
fiixng formatting and baseline changes
navya9singh Feb 12, 2024
9ef3d85
Minor fixes
navya9singh Feb 12, 2024
281e2f1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 12, 2024
7f41f30
Fixing baselines
navya9singh Feb 12, 2024
d3816d3
changes based on reviews 1
navya9singh Feb 20, 2024
3914539
changes based on review 2
navya9singh Feb 20, 2024
83a4328
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Feb 20, 2024
c6b6f23
fixing formatting
navya9singh Feb 20, 2024
c8c3e29
baseline changes
navya9singh Feb 20, 2024
5192ff2
accepting baselines
navya9singh Feb 20, 2024
215e778
Removing updateGraph for reverting the file
navya9singh Feb 22, 2024
ccd35b1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 4, 2024
1fe2754
changing protocol name
navya9singh Mar 4, 2024
c67137b
Removing extra baselines
navya9singh Mar 4, 2024
e83eae1
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 4, 2024
392dd75
adressing pr comments
navya9singh Mar 5, 2024
d7cbcd0
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 8, 2024
778e2b4
protocol changes and fixed tests
navya9singh Mar 19, 2024
f302b65
missed change for fourslash tests
navya9singh Mar 19, 2024
7027ea3
changes to avoid duplicated imports for symbols that are added to exi…
navya9singh Mar 19, 2024
a9f7127
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Mar 19, 2024
8e486d6
fixing tests
navya9singh Mar 20, 2024
835acda
small fixes
navya9singh Mar 20, 2024
3b79cb1
new fixes for non exported symbols in import adder
navya9singh Mar 28, 2024
d7f7b15
Merging with main
navya9singh Mar 28, 2024
82fc3bf
adressing pr comments
navya9singh Mar 28, 2024
5e7e39b
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 25, 2024
0ef7161
fixing merge conflicts
navya9singh Apr 25, 2024
4980cc5
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 25, 2024
7c6a146
resolving merge conflicts
navya9singh Apr 26, 2024
eaa1aaf
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh Apr 26, 2024
4ec4ccc
fixing tests
navya9singh Apr 29, 2024
1a36951
small fixes
navya9singh Apr 29, 2024
4d37a06
Adressing pr comments
navya9singh Apr 30, 2024
66ca5be
adressing pr comments
navya9singh Apr 30, 2024
e8189c3
adding `getPasteEdits` to session.ts and services.ts
navya9singh Apr 30, 2024
213587e
adding changes to handle pasted text
navya9singh May 1, 2024
68ce405
Removing baselines for deleted tests
navya9singh May 1, 2024
5e1ff78
adressing pr comments
navya9singh May 2, 2024
a3fbd69
adding changes tests and protocol
navya9singh May 2, 2024
8c23899
fixing test formatting
navya9singh May 7, 2024
1958d1a
Merge branch 'main' of https://github.com/navya9singh/TypeScript into…
navya9singh May 7, 2024
3fe8f48
fixing error in pasteEdit.ts
navya9singh May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/harness/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
ApplicableRefactorInfo,
arrayFrom,
CallHierarchyIncomingCall,
CallHierarchyItem,
CallHierarchyOutgoingCall,
Expand All @@ -13,6 +14,7 @@ import {
computeLineAndCharacterOfPosition,
computeLineStarts,
computePositionOfLineAndCharacter,
CopyRange,
createQueue,
createTextSpanFromBounds,
Debug,
Expand Down Expand Up @@ -50,6 +52,7 @@ import {
OrganizeImportsArgs,
OutliningSpan,
PatternMatchKind,
PostPasteImportFixes,
Program,
QuickInfo,
RefactorEditInfo,
Expand Down Expand Up @@ -1009,6 +1012,22 @@ export class SessionClient implements LanguageService {
return getSupportedCodeFixes();
}

getPostPasteImportFixes(targetFile: string, pastes: Array<{text: string; range: TextRange}>, _preferences: UserPreferences, _formatOptions: FormatCodeSettings, originalFile?: string, copyRange?: CopyRange): PostPasteImportFixes{
const args = this.createFileLocationOrRangeRequestArgs(pastes[0].range, targetFile) as protocol.GetPostPasteImportFixesRequestArgs;
args.targetFile = targetFile;
args.pastes = arrayFrom(pastes.map(paste => ({text: paste.text, range: this.createFileRangeRequestArgs(targetFile, paste.range.pos, paste.range.end)})));
args.originalFile = originalFile;
args.copyRange = copyRange;

const request = this.processRequest<protocol.GetPostPasteImportFixesRequest>(protocol.CommandTypes.GetPostPasteImportFixes, args);
const response = this.processResponse<protocol.GetPostPasteImportFixesResponse>(request);
if (!response.body) {
return { edits: []};
}
const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits);
return { edits: edits };
}

getProgram(): Program {
throw new Error("Program objects are not serializable through the server protocol.");
}
Expand Down
5 changes: 5 additions & 0 deletions src/harness/fourslashImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3562,6 +3562,11 @@ export class TestState {
assert.deepEqual(actualModuleSpecifiers, moduleSpecifiers);
}

public verifyPostPasteImportFixes(options: FourSlashInterface.PostPasteImportFixOptions): void{
const editInfo = this.languageService.getPostPasteImportFixes(this.activeFile.fileName, options.pastes, options.preferences, this.formatCodeSettings, options.originalFile, options.copyRange);
this.verifyNewContent({ newFileContent: options.newFileContents }, editInfo.edits);
}

public verifyDocCommentTemplate(expected: ts.TextInsertion | undefined, options?: ts.DocCommentTemplateOptions) {
const name = "verifyDocCommentTemplate";
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition, options || { generateReturnInDocTemplate: true }, this.formatCodeSettings)!;
Expand Down
20 changes: 20 additions & 0 deletions src/harness/fourslashInterfaceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ export class Verify extends VerifyNegatable {
public organizeImports(newContent: string, mode?: ts.OrganizeImportsMode, preferences?: ts.UserPreferences): void {
this.state.verifyOrganizeImports(newContent, mode, preferences);
}

public postPasteImportFix(options: PostPasteImportFixOptions): void {
this.state.verifyPostPasteImportFixes(options);
}
}

export class Edit {
Expand Down Expand Up @@ -1879,6 +1883,14 @@ export interface VerifyCodeFixAllOptions {
preferences?: ts.UserPreferences;
}

export interface verifyPostPasteImportFix {
targetFile: string;
pastes: Array<{text: string; range: ts.TextRange}>;
preferences: ts.UserPreferences;
originalFile?: string;
copyRange?: ts.CopyRange
}

export interface VerifyRefactorOptions {
name: string;
actionName: string;
Expand Down Expand Up @@ -1919,6 +1931,14 @@ export interface MoveToFileOptions {
readonly preferences?: ts.UserPreferences;
}

export interface PostPasteImportFixOptions {
readonly newFileContents: { readonly [fileName: string]: string; };
readonly pastes: Array<{text: string; range: ts.TextRange}>,//{ pos, end }: ts.TextRange
readonly preferences: ts.UserPreferences,
readonly originalFile?: string,
readonly copyRange?: ts.CopyRange
}

export type RenameLocationsOptions = readonly RenameLocationOptions[] | {
readonly findInStrings?: boolean;
readonly findInComments?: boolean;
Expand Down
20 changes: 20 additions & 0 deletions src/server/project.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { info } from "console";
import { FormatContext } from "../services/_namespaces/ts.formatting";
import { FixInfo, ImportFixWithModuleSpecifier, sortFixInfo } from "../services/codefixes/importFixes";
import * as ts from "./_namespaces/ts";
import {
addRange,
Expand All @@ -20,6 +23,7 @@ import {
containsPath,
createCacheableExportInfoMap,
createLanguageService,
createPackageJsonImportFilter,
createResolutionCache,
createSymlinkCache,
Debug,
Expand Down Expand Up @@ -65,6 +69,7 @@ import {
HasInvalidatedLibResolutions,
HasInvalidatedResolutions,
HostCancellationToken,
ImportClause,
inferredTypesContainingFile,
InstallPackageOptions,
IScriptSnapshot,
Expand Down Expand Up @@ -120,6 +125,7 @@ import {
StringLiteralLike,
stripQuotes,
StructureIsReused,
SymbolExportInfo,
SymlinkCache,
ThrottledCancellationToken,
timestamp,
Expand Down Expand Up @@ -2212,6 +2218,20 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
return this.noDtsResolutionProject;
}

/** @internal */
updateTargetFile(rootFile: string, targetFileText: string, pastedText: string): {updatedFile: SourceFile | undefined, updatedProgram: Program | undefined, originalProgram: ts.Program | undefined} {
const originalProgram = this.program;
this.getScriptInfo(rootFile)?.editContent(0, targetFileText.length-1, pastedText);
this.updateGraph();
return { updatedFile: (this.program?.getSourceFile(rootFile)), updatedProgram: this.program, originalProgram };
}

/** @internal */
revertUpdatedFile(rootFile: string, updatedText: string, originalText: string) {
this.getScriptInfo(rootFile)?.editContent(0, updatedText.length-1, originalText);
this.updateGraph();
}

/** @internal */
private getCompilerOptionsForNoDtsResolutionProject() {
return {
Expand Down
27 changes: 27 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const enum CommandTypes {
GetApplicableRefactors = "getApplicableRefactors",
GetEditsForRefactor = "getEditsForRefactor",
GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions",
GetPostPasteImportFixes = "getPostPasteImportFixes",
/** @internal */
GetEditsForRefactorFull = "getEditsForRefactor-full",

Expand Down Expand Up @@ -630,6 +631,32 @@ export interface GetMoveToRefactoringFileSuggestions extends Response {
};
}

/**
* Request refactorings at a given position post pasting text from some other location.
*/

export interface GetPostPasteImportFixesRequest extends Request {
command: CommandTypes.GetPostPasteImportFixes;
arguments: GetPostPasteImportFixesRequestArgs;
}
export type DocumentRange = FileRangeRequestArgs;
export type CopyRange = {
start: Location;
end: Location;
}
export type GetPostPasteImportFixesRequestArgs = FileLocationOrRangeRequestArgs & {
targetFile: string,
pastes: Array<{text: string; range: DocumentRange}>,
originalFile?: string,
copyRange?: CopyRange
}
export interface GetPostPasteImportFixesResponse extends Response {
body: PostPasteImportAction;
}
export interface PostPasteImportAction {
edits: FileCodeEdits[];
}

/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
Expand Down
18 changes: 18 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import {
perfLogger,
PerformanceEvent,
PossibleProgramFileInfo,
PostPasteImportFixes,
Program,
QuickInfo,
RefactorEditInfo,
Expand Down Expand Up @@ -2796,6 +2797,16 @@ export class Session<TMessage = string> implements EventSender {
return project.getLanguageService().getMoveToRefactoringFileSuggestions(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file));
}

private getPostPasteImportFixes(args: protocol.GetPostPasteImportFixesRequestArgs): protocol.PostPasteImportAction | undefined {
const { file, project } = this.getFileAndProject(args);
const result = project.getLanguageService().getPostPasteImportFixes(args.targetFile, arrayFrom(args.pastes).map(paste => ({text: paste.text, range: this.getRange(paste.range, project.getScriptInfoForNormalizedPath(file)!)})), this.getPreferences(file), this.getFormatOptions(file), args.originalFile, args.copyRange);
if (result === undefined) {
return undefined;
}
const allResults = this.mapPostPasteAction(result);
return allResults;
}

private organizeImports(args: protocol.OrganizeImportsRequestArgs, simplifiedResult: boolean): readonly protocol.FileCodeEdits[] | readonly FileTextChanges[] {
Debug.assert(args.scope.type === "file");
const { file, project } = this.getFileAndProject(args.scope.args);
Expand Down Expand Up @@ -2928,6 +2939,10 @@ export class Session<TMessage = string> implements EventSender {
return { fixName, description, changes: this.mapTextChangesToCodeEdits(changes), commands, fixId, fixAllDescription };
}

private mapPostPasteAction({ edits }: PostPasteImportFixes): protocol.PostPasteImportAction {
return { edits: this.mapTextChangesToCodeEdits(edits)};
}

private mapTextChangesToCodeEdits(textChanges: readonly FileTextChanges[]): protocol.FileCodeEdits[] {
return textChanges.map(change => this.mapTextChangeToCodeEdit(change));
}
Expand Down Expand Up @@ -3521,6 +3536,9 @@ export class Session<TMessage = string> implements EventSender {
[protocol.CommandTypes.GetMoveToRefactoringFileSuggestions]: (request: protocol.GetMoveToRefactoringFileSuggestionsRequest) => {
return this.requiredResponse(this.getMoveToRefactoringFileSuggestions(request.arguments));
},
[protocol.CommandTypes.GetPostPasteImportFixes]: (request: protocol.GetPostPasteImportFixesRequest) => {
return this.requiredResponse(this.getPostPasteImportFixes(request.arguments));
},
[protocol.CommandTypes.GetEditsForRefactorFull]: (request: protocol.GetEditsForRefactorRequest) => {
return this.requiredResponse(this.getEditsForRefactor(request.arguments, /*simplifiedResult*/ false));
},
Expand Down
1 change: 1 addition & 0 deletions src/services/_namespaces/ts.postPasteImportFixes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "../postPasteImportFix";
2 changes: 2 additions & 0 deletions src/services/_namespaces/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@ import * as textChanges from "./ts.textChanges";
export { textChanges };
import * as formatting from "./ts.formatting";
export { formatting };
import * as postPasteImportFixes from "./ts.postPasteImportFixes";
export { postPasteImportFixes };
25 changes: 19 additions & 6 deletions src/services/codefixes/importFixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export interface ImportAdder {
addImportFromDiagnostic: (diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) => void;
addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) => void;
writeFixes: (changeTracker: textChanges.ChangeTracker, oldFileQuotePreference?: QuotePreference) => void;
addImportsForUnknownSymbols: (context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean) => void;
}

/** @internal */
Expand All @@ -236,8 +237,14 @@ function createImportAdderWorker(sourceFile: SourceFile, program: Program, useAu
type NewImportsKey = `${0 | 1}|${string}`;
/** Use `getNewImportEntry` for access */
const newImports = new Map<NewImportsKey, Mutable<ImportsCollection & { useRequire: boolean; }>>();
return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes };
return { addImportFromDiagnostic, addImportFromExportedSymbol, writeFixes, hasFixes, addImportsForUnknownSymbols };

function addImportsForUnknownSymbols(context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean) {
const info = getFixInfosWithoutDiagnostic(context, symbolToken, useAutoImportProvider);
if (!info || !info.length) return;
addImport(first(info));
}

function addImportFromDiagnostic(diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) {
const info = getFixInfos(context, diagnostic.code, diagnostic.start, useAutoImportProvider);
if (!info || !info.length) return;
Expand Down Expand Up @@ -474,7 +481,7 @@ const enum AddAsTypeOnly {
NotAllowed = 1 << 2,
}
type ImportFix = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport | FixPromoteTypeOnlyImport;
type ImportFixWithModuleSpecifier = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport;
export type ImportFixWithModuleSpecifier = FixUseNamespaceImport | FixAddJsdocTypeImport | FixAddToExistingImport | FixAddNewImport;

// Properties are be undefined if fix is derived from an existing import
interface ImportFixBase {
Expand Down Expand Up @@ -626,7 +633,7 @@ function getSingleExportInfoForSymbol(symbol: Symbol, symbolName: string, module
}
}

function getImportFixes(
export function getImportFixes(
exportInfos: readonly SymbolExportInfo[],
usagePosition: number | undefined,
isValidTypeOnlyUseSite: boolean,
Expand Down Expand Up @@ -833,7 +840,7 @@ function createExistingImportMap(checker: TypeChecker, importingFile: SourceFile
};
}

function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
export function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
// 1. TypeScript files don't use require variable declarations
if (!isSourceFileJS(sourceFile)) {
return false;
Expand Down Expand Up @@ -974,7 +981,7 @@ function newImportInfoFromExistingSpecifier(
}
}

interface FixInfo {
export interface FixInfo {
readonly fix: ImportFix;
readonly symbolName: string;
readonly errorIdentifierText: string | undefined;
Expand Down Expand Up @@ -1002,14 +1009,20 @@ function getFixInfos(context: CodeFixContextBase, errorCode: number, pos: number
return info && sortFixInfo(info, context.sourceFile, context.program, packageJsonImportFilter, context.host);
}

function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecifier; })[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier; })[] {
export function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecifier; })[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): readonly (FixInfo & { fix: ImportFixWithModuleSpecifier; })[] {
const _toPath = (fileName: string) => toPath(fileName, host.getCurrentDirectory(), hostGetCanonicalFileName(host));
return sort(fixes, (a, b) =>
compareBooleans(!!a.isJsxNamespaceFix, !!b.isJsxNamespaceFix) ||
compareValues(a.fix.kind, b.fix.kind) ||
compareModuleSpecifiers(a.fix, b.fix, sourceFile, program, packageJsonImportFilter.allowsImportingSpecifier, _toPath));
}

function getFixInfosWithoutDiagnostic(context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean): readonly FixInfo[] | undefined {
const info = getFixesInfoForNonUMDImport(context, symbolToken, useAutoImportProvider);
const packageJsonImportFilter = createPackageJsonImportFilter(context.sourceFile, context.preferences, context.host);
return info && sortFixInfo(info, context.sourceFile, context.program, packageJsonImportFilter, context.host);
}

function getBestFix(fixes: readonly ImportFixWithModuleSpecifier[], sourceFile: SourceFile, program: Program, packageJsonImportFilter: PackageJsonImportFilter, host: LanguageServiceHost): ImportFixWithModuleSpecifier | undefined {
if (!some(fixes)) return;
// These will always be placed first if available, and are better than other kinds
Expand Down
Loading