Skip to content

Commit

Permalink
fix paste edits range: include all completely selected identifiers (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
iisaduan authored Oct 29, 2024
1 parent cb44488 commit 2161893
Show file tree
Hide file tree
Showing 22 changed files with 3,969 additions and 6 deletions.
31 changes: 27 additions & 4 deletions src/services/pasteEdits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
fileShouldUseJavaScriptRequire,
findAncestor,
findIndex,
findTokenOnLeftOfPosition,
forEachChild,
formatting,
getNewLineOrDefaultFromHost,
Expand Down Expand Up @@ -50,11 +51,16 @@ export function pasteEditsProvider(
return { edits: changes, fixId };
}

interface CopiedFromInfo {
file: SourceFile;
range: TextRange[];
}

function pasteEdits(
targetFile: SourceFile,
pastedText: string[],
pasteLocations: TextRange[],
copiedFrom: { file: SourceFile; range: TextRange[]; } | undefined,
copiedFrom: CopiedFromInfo | undefined,
host: LanguageServiceHost,
preferences: UserPreferences,
formatContext: formatting.FormatContext,
Expand Down Expand Up @@ -93,11 +99,13 @@ function pasteEdits(
}
statements.push(...statementsInSourceFile.slice(startNodeIndex, endNodeIndex === -1 ? statementsInSourceFile.length : endNodeIndex + 1));
});
const usage = getUsageInfo(copiedFrom.file, statements, originalProgram!.getTypeChecker(), getExistingLocals(updatedFile, statements, originalProgram!.getTypeChecker()), { pos: copiedFrom.range[0].pos, end: copiedFrom.range[copiedFrom.range.length - 1].end });
Debug.assertIsDefined(originalProgram);
Debug.assertIsDefined(originalProgram, "no original program found");
const originalProgramTypeChecker = originalProgram.getTypeChecker();
const usageInfoRange = getUsageInfoRangeForPasteEdits(copiedFrom);
const usage = getUsageInfo(copiedFrom.file, statements, originalProgramTypeChecker, getExistingLocals(updatedFile, statements, originalProgramTypeChecker), usageInfoRange);
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);
addTargetFileImports(copiedFrom.file, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, originalProgramTypeChecker, updatedProgram, importAdder);
}
else {
const context: CodeFixContextBase = {
Expand Down Expand Up @@ -167,3 +175,18 @@ function pasteEdits(
);
});
}

/**
* Adjusts the range for `getUsageInfo` to correctly include identifiers at the edges of the copied text.
*/
function getUsageInfoRangeForPasteEdits({ file: sourceFile, range }: CopiedFromInfo) {
const pos = range[0].pos;
const end = range[range.length - 1].end;
const startToken = getTokenAtPosition(sourceFile, pos);
const endToken = findTokenOnLeftOfPosition(sourceFile, pos) ?? getTokenAtPosition(sourceFile, end);
// Since the range is only used to check identifiers, we do not need to adjust range when the tokens at the edges are not identifiers.
return {
pos: isIdentifier(startToken) && pos <= startToken.getStart(sourceFile) ? startToken.getFullStart() : pos,
end: isIdentifier(endToken) && end === endToken.getEnd() ? textChanges.getAdjustedEndPosition(sourceFile, endToken, {}) : end,
};
}
5 changes: 3 additions & 2 deletions src/services/textChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ function getAdjustedRange(sourceFile: SourceFile, startNode: Node, endNode: Node
return { pos: getAdjustedStartPosition(sourceFile, startNode, options), end: getAdjustedEndPosition(sourceFile, endNode, options) };
}

function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd, hasTrailingComment = false) {
function getAdjustedStartPosition(sourceFile: SourceFile, node: Node, options: ConfigurableStartEnd, hasTrailingComment = false): number {
const { leadingTriviaOption } = options;
if (leadingTriviaOption === LeadingTriviaOption.Exclude) {
return node.getStart(sourceFile);
Expand Down Expand Up @@ -436,7 +436,8 @@ function getEndPositionOfMultilineTrailingComment(sourceFile: SourceFile, node:
return undefined;
}

function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd): number {
/** @internal */
export function getAdjustedEndPosition(sourceFile: SourceFile, node: Node, options: ConfigurableEnd): number {
const { end } = node;
const { trailingTriviaOption } = options;
if (trailingTriviaOption === TrailingTriviaOption.Exclude) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
Info seq [hh:mm:ss:mss] currentDirectory:: /home/src/Vscode/Projects/bin useCaseSensitiveFileNames:: false
Info seq [hh:mm:ss:mss] libs Location:: /home/src/tslibs/TS/Lib
Info seq [hh:mm:ss:mss] globalTypingsCacheLocation:: /home/src/Library/Caches/typescript
Info seq [hh:mm:ss:mss] Provided types map file "/home/src/tslibs/TS/Lib/typesMap.json" doesn't exist
//// [/home/src/tslibs/TS/Lib/lib.d.ts]
lib.d.ts-Text
//// [/home/src/tslibs/TS/Lib/lib.decorators.d.ts]
lib.decorators.d.ts-Text
//// [/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts]
lib.decorators.legacy.d.ts-Text
//// [/home/src/workspaces/project/a.ts]
function foo() { }
const x = foo()
foo()
x
//// [/home/src/workspaces/project/folder/target.ts]
//// [/home/src/workspaces/project/tsconfig.json]
{ "files": ["a.ts", "folder/target.ts"] }
Info seq [hh:mm:ss:mss] request:
{
"seq": 0,
"type": "request",
"arguments": {
"file": "/home/src/workspaces/project/folder/target.ts"
},
"command": "open"
}
Info seq [hh:mm:ss:mss] getConfigFileNameForFile:: File: /home/src/workspaces/project/folder/target.ts ProjectRootPath: undefined:: Result: /home/src/workspaces/project/tsconfig.json
Info seq [hh:mm:ss:mss] Creating ConfiguredProject: /home/src/workspaces/project/tsconfig.json, currentDirectory: /home/src/workspaces/project
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/workspaces/project/tsconfig.json 2000 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] Config: /home/src/workspaces/project/tsconfig.json : {
"rootNames": [
"/home/src/workspaces/project/a.ts",
"/home/src/workspaces/project/folder/target.ts"
],
"options": {
"configFilePath": "/home/src/workspaces/project/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "projectLoadingStart",
"body": {
"projectName": "/home/src/workspaces/project/tsconfig.json",
"reason": "Creating possible configured project for /home/src/workspaces/project/folder/target.ts to open"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/workspaces/project/a.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /home/src/workspaces/project/tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/tslibs/TS/Lib/lib.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/project/node_modules 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/project/node_modules 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/node_modules 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/node_modules 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Failed Lookup Locations
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/tslibs/TS/Lib/lib.decorators.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/project/node_modules/@types 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/project/node_modules/@types 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/node_modules/@types 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /home/src/workspaces/node_modules/@types 1 undefined Project: /home/src/workspaces/project/tsconfig.json WatchType: Type roots
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /home/src/workspaces/project/tsconfig.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/home/src/workspaces/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
/home/src/tslibs/TS/Lib/lib.d.ts Text-1 lib.d.ts-Text
/home/src/tslibs/TS/Lib/lib.decorators.d.ts Text-1 lib.decorators.d.ts-Text
/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts Text-1 lib.decorators.legacy.d.ts-Text
/home/src/workspaces/project/a.ts Text-1 "function foo() { }\nconst x = foo()\n\nfoo()\nx"
/home/src/workspaces/project/folder/target.ts SVC-1-0 ""
../../tslibs/TS/Lib/lib.d.ts
Default library for target 'es5'
../../tslibs/TS/Lib/lib.decorators.d.ts
Library referenced via 'decorators' from file '../../tslibs/TS/Lib/lib.d.ts'
../../tslibs/TS/Lib/lib.decorators.legacy.d.ts
Library referenced via 'decorators.legacy' from file '../../tslibs/TS/Lib/lib.d.ts'
a.ts
Part of 'files' list in tsconfig.json
folder/target.ts
Part of 'files' list in tsconfig.json
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "projectLoadingFinish",
"body": {
"projectName": "/home/src/workspaces/project/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "configFileDiag",
"body": {
"triggerFile": "/home/src/workspaces/project/folder/target.ts",
"configFile": "/home/src/workspaces/project/tsconfig.json",
"diagnostics": []
}
}
Info seq [hh:mm:ss:mss] Project '/home/src/workspaces/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] Open files:
Info seq [hh:mm:ss:mss] FileName: /home/src/workspaces/project/folder/target.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /home/src/workspaces/project/tsconfig.json
Info seq [hh:mm:ss:mss] response:
{
"seq": 0,
"type": "response",
"command": "open",
"request_seq": 0,
"success": true,
"performanceData": {
"updateGraphDurationMs": *
}
}
After Request
watchedFiles::
/home/src/tslibs/TS/Lib/lib.d.ts: *new*
{"pollingInterval":500}
/home/src/tslibs/TS/Lib/lib.decorators.d.ts: *new*
{"pollingInterval":500}
/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts: *new*
{"pollingInterval":500}
/home/src/workspaces/project/a.ts: *new*
{"pollingInterval":500}
/home/src/workspaces/project/tsconfig.json: *new*
{"pollingInterval":2000}
watchedDirectoriesRecursive::
/home/src/workspaces/node_modules: *new*
{}
/home/src/workspaces/node_modules/@types: *new*
{}
/home/src/workspaces/project/node_modules: *new*
{}
/home/src/workspaces/project/node_modules/@types: *new*
{}
Projects::
/home/src/workspaces/project/tsconfig.json (Configured) *new*
projectStateVersion: 1
projectProgramVersion: 1
autoImportProviderHost: false
ScriptInfos::
/home/src/tslibs/TS/Lib/lib.d.ts *new*
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/tslibs/TS/Lib/lib.decorators.d.ts *new*
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts *new*
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/workspaces/project/a.ts *new*
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/workspaces/project/folder/target.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/home/src/workspaces/project/tsconfig.json *default*
Info seq [hh:mm:ss:mss] request:
{
"seq": 1,
"type": "request",
"arguments": {
"formatOptions": {
"indentSize": 4,
"tabSize": 4,
"newLineCharacter": "\n",
"convertTabsToSpaces": true,
"indentStyle": 2,
"insertSpaceAfterConstructor": false,
"insertSpaceAfterCommaDelimiter": true,
"insertSpaceAfterSemicolonInForStatements": true,
"insertSpaceBeforeAndAfterBinaryOperators": true,
"insertSpaceAfterKeywordsInControlFlowStatements": true,
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
"insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false,
"insertSpaceBeforeFunctionParenthesis": false,
"placeOpenBraceOnNewLineForFunctions": false,
"placeOpenBraceOnNewLineForControlBlocks": false,
"semicolons": "ignore",
"trimTrailingWhitespace": true,
"indentSwitchCase": true
}
},
"command": "configure"
}
Info seq [hh:mm:ss:mss] Format host information updated
Info seq [hh:mm:ss:mss] response:
{
"seq": 0,
"type": "response",
"command": "configure",
"request_seq": 1,
"success": true
}
Info seq [hh:mm:ss:mss] request:
{
"seq": 2,
"type": "request",
"arguments": {
"file": "/home/src/workspaces/project/folder/target.ts",
"pastedText": [
""
],
"pasteLocations": [
{
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 1
}
}
],
"copiedFrom": {
"file": "/home/src/workspaces/project/a.ts",
"spans": [
{
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 1
}
}
]
}
},
"command": "getPasteEdits"
}
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /home/src/workspaces/project/tsconfig.json
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /home/src/workspaces/project/tsconfig.json projectStateVersion: 2 projectProgramVersion: 1 structureChanged: false structureIsReused:: Completely Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/home/src/workspaces/project/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
/home/src/tslibs/TS/Lib/lib.d.ts Text-1 lib.d.ts-Text
/home/src/tslibs/TS/Lib/lib.decorators.d.ts Text-1 lib.decorators.d.ts-Text
/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts Text-1 lib.decorators.legacy.d.ts-Text
/home/src/workspaces/project/a.ts Text-1 "function foo() { }\nconst x = foo()\n\nfoo()\nx"
/home/src/workspaces/project/folder/target.ts SVC-1-1 ""

Info seq [hh:mm:ss:mss] -----------------------------------------------
Info seq [hh:mm:ss:mss] response:
{
"seq": 0,
"type": "response",
"command": "getPasteEdits",
"request_seq": 2,
"success": true,
"performanceData": {
"updateGraphDurationMs": *
},
"body": {
"edits": [],
"fixId": "providePostPasteEdits"
}
}
After Request
Projects::
/home/src/workspaces/project/tsconfig.json (Configured) *changed*
projectStateVersion: 3 *changed*
projectProgramVersion: 1
dirty: true *changed*
autoImportProviderHost: false

ScriptInfos::
/home/src/tslibs/TS/Lib/lib.d.ts
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/tslibs/TS/Lib/lib.decorators.d.ts
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/tslibs/TS/Lib/lib.decorators.legacy.d.ts
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/workspaces/project/a.ts
version: Text-1
containingProjects: 1
/home/src/workspaces/project/tsconfig.json
/home/src/workspaces/project/folder/target.ts (Open) *changed*
version: SVC-1-2 *changed*
containingProjects: 1
/home/src/workspaces/project/tsconfig.json *default*
Loading

0 comments on commit 2161893

Please sign in to comment.