Paste without imports (#59093)

This commit is contained in:
navya9singh 2024-07-01 15:00:29 -07:00 committed by GitHub
parent 962389313f
commit 7bd7dfc007
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 346 additions and 6 deletions

View File

@ -90,7 +90,7 @@ 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()));
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);
const useEsModuleSyntax = !fileShouldUseJavaScriptRequire(targetFile.fileName, originalProgram, host, !!copiedFrom.file.commonJsModuleIndicator);
addExportsInOldFile(copiedFrom.file, usage.targetFileImportsFromOldFile, changes, useEsModuleSyntax);

View File

@ -129,6 +129,7 @@ import {
PropertyAccessExpression,
PropertyAssignment,
QuotePreference,
rangeContainsRange,
RefactorContext,
RefactorEditInfo,
RequireOrImportCall,
@ -145,6 +146,7 @@ import {
SyntaxKind,
takeWhile,
textChanges,
TextRange,
TransformFlags,
tryCast,
TypeAliasDeclaration,
@ -861,7 +863,7 @@ function isPureImport(node: Node): boolean {
}
/** @internal */
export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[], checker: TypeChecker, existingTargetLocals: ReadonlySet<Symbol> = new Set()): UsageInfo {
export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[], checker: TypeChecker, existingTargetLocals: ReadonlySet<Symbol> = new Set(), enclosingRange?: TextRange): UsageInfo {
const movedSymbols = new Set<Symbol>();
const oldImportsNeededByTargetFile = new Map<Symbol, [/*isValidTypeOnlyUseSite*/ boolean, codefix.ImportOrRequireAliasDeclaration | undefined]>();
const targetFileImportsFromOldFile = new Map<Symbol, /*isValidTypeOnlyUseSite*/ boolean>();
@ -880,7 +882,7 @@ export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[],
const unusedImportsFromOldFile = new Set<Symbol>();
for (const statement of toMove) {
forEachReference(statement, checker, (symbol, isValidTypeOnlyUseSite) => {
forEachReference(statement, checker, enclosingRange, (symbol, isValidTypeOnlyUseSite) => {
if (!symbol.declarations || isGlobalType(checker, symbol)) {
return;
}
@ -916,7 +918,7 @@ export function getUsageInfo(oldFile: SourceFile, toMove: readonly Statement[],
unusedImportsFromOldFile.delete(jsxNamespaceSymbol);
}
forEachReference(statement, checker, (symbol, isValidTypeOnlyUseSite) => {
forEachReference(statement, checker, enclosingRange, (symbol, isValidTypeOnlyUseSite) => {
if (movedSymbols.has(symbol)) oldFileImportsFromTargetFile.set(symbol, isValidTypeOnlyUseSite);
unusedImportsFromOldFile.delete(symbol);
});
@ -959,9 +961,12 @@ function inferNewFileName(importsFromNewFile: Map<Symbol, unknown>, movedSymbols
return forEachKey(importsFromNewFile, symbolNameNoDefault) || forEachKey(movedSymbols, symbolNameNoDefault) || "newFile";
}
function forEachReference(node: Node, checker: TypeChecker, onReference: (s: Symbol, isValidTypeOnlyUseSite: boolean) => void) {
function forEachReference(node: Node, checker: TypeChecker, enclosingRange: TextRange | undefined, onReference: (s: Symbol, isValidTypeOnlyUseSite: boolean) => void) {
node.forEachChild(function cb(node) {
if (isIdentifier(node) && !isDeclarationName(node)) {
if (enclosingRange && !rangeContainsRange(enclosingRange, node)) {
return;
}
const sym = checker.getSymbolAtLocation(node);
if (sym) onReference(sym, isValidTypeOnlyAliasUseSite(node));
}
@ -1140,7 +1145,7 @@ export function getExistingLocals(sourceFile: SourceFile, statements: readonly S
}
for (const statement of statements) {
forEachReference(statement, checker, s => {
forEachReference(statement, checker, /*enclosingRange*/ undefined, s => {
const symbol = skipAlias(s, checker);
if (symbol.valueDeclaration && getSourceFileOfNode(symbol.valueDeclaration).path === sourceFile.path) {
existingLocals.add(symbol);

View File

@ -0,0 +1,310 @@
currentDirectory:: / useCaseSensitiveFileNames: false
Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist
//// [/a.ts]
export interface Foo { }
export const foo: Foo = {}
//// [/b.ts]
//// [/lib.d.ts]
lib.d.ts-Text
//// [/lib.decorators.d.ts]
lib.decorators.d.ts-Text
//// [/lib.decorators.legacy.d.ts]
lib.decorators.legacy.d.ts-Text
//// [/tsconfig.json]
{ "files": ["a.ts", "b.ts"] }
Info seq [hh:mm:ss:mss] request:
{
"seq": 0,
"type": "request",
"arguments": {
"file": "/b.ts"
},
"command": "open"
}
Info seq [hh:mm:ss:mss] getConfigFileNameForFile:: File: /b.ts ProjectRootPath: undefined:: Result: /tsconfig.json
Info seq [hh:mm:ss:mss] Creating configuration project /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /tsconfig.json 2000 undefined Project: /tsconfig.json WatchType: Config file
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "projectLoadingStart",
"body": {
"projectName": "/tsconfig.json",
"reason": "Creating possible configured project for /b.ts to open"
}
}
Info seq [hh:mm:ss:mss] Config: /tsconfig.json : {
"rootNames": [
"/a.ts",
"/b.ts"
],
"options": {
"configFilePath": "/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /lib.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /lib.decorators.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /lib.decorators.legacy.d.ts 500 undefined WatchType: Closed Script info
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
/lib.d.ts Text-1 lib.d.ts-Text
/lib.decorators.d.ts Text-1 lib.decorators.d.ts-Text
/lib.decorators.legacy.d.ts Text-1 lib.decorators.legacy.d.ts-Text
/a.ts Text-1 "export interface Foo { }\n\nexport const foo: Foo = {}"
/b.ts SVC-1-0 ""
lib.d.ts
Default library for target 'es5'
lib.decorators.d.ts
Library referenced via 'decorators' from file 'lib.d.ts'
lib.decorators.legacy.d.ts
Library referenced via 'decorators.legacy' from file 'lib.d.ts'
a.ts
Part of 'files' list in tsconfig.json
b.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": "/tsconfig.json"
}
}
Info seq [hh:mm:ss:mss] event:
{
"seq": 0,
"type": "event",
"event": "configFileDiag",
"body": {
"triggerFile": "/b.ts",
"configFile": "/tsconfig.json",
"diagnostics": []
}
}
Info seq [hh:mm:ss:mss] 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: /b.ts ProjectRootPath: undefined
Info seq [hh:mm:ss:mss] Projects: /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::
/a.ts: *new*
{"pollingInterval":500}
/lib.d.ts: *new*
{"pollingInterval":500}
/lib.decorators.d.ts: *new*
{"pollingInterval":500}
/lib.decorators.legacy.d.ts: *new*
{"pollingInterval":500}
/tsconfig.json: *new*
{"pollingInterval":2000}
Projects::
/tsconfig.json (Configured) *new*
projectStateVersion: 1
projectProgramVersion: 1
ScriptInfos::
/a.ts *new*
version: Text-1
containingProjects: 1
/tsconfig.json
/b.ts (Open) *new*
version: SVC-1-0
containingProjects: 1
/tsconfig.json *default*
/lib.d.ts *new*
version: Text-1
containingProjects: 1
/tsconfig.json
/lib.decorators.d.ts *new*
version: Text-1
containingProjects: 1
/tsconfig.json
/lib.decorators.legacy.d.ts *new*
version: Text-1
containingProjects: 1
/tsconfig.json
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": "/b.ts",
"pastedText": [
"export"
],
"pasteLocations": [
{
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 1
}
}
],
"copiedFrom": {
"file": "a.ts",
"spans": [
{
"start": {
"line": 3,
"offset": 1
},
"end": {
"line": 3,
"offset": 7
}
}
]
}
},
"command": "getPasteEdits"
}
Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json
Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 2 projectProgramVersion: 1 structureChanged: false structureIsReused:: Completely Elapsed:: *ms
Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured)
Info seq [hh:mm:ss:mss] Files (5)
/lib.d.ts Text-1 lib.d.ts-Text
/lib.decorators.d.ts Text-1 lib.decorators.d.ts-Text
/lib.decorators.legacy.d.ts Text-1 lib.decorators.legacy.d.ts-Text
/a.ts Text-1 "export interface Foo { }\n\nexport const foo: Foo = {}"
/b.ts SVC-1-1 "export"
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": [
{
"fileName": "/b.ts",
"textChanges": [
{
"start": {
"line": 1,
"offset": 1
},
"end": {
"line": 1,
"offset": 1
},
"newText": "export"
}
]
}
],
"fixId": "providePostPasteEdits"
}
}
After Request
Projects::
/tsconfig.json (Configured) *changed*
projectStateVersion: 3 *changed*
projectProgramVersion: 1
dirty: true *changed*
ScriptInfos::
/a.ts
version: Text-1
containingProjects: 1
/tsconfig.json
/b.ts (Open) *changed*
version: SVC-1-2 *changed*
containingProjects: 1
/tsconfig.json *default*
/lib.d.ts
version: Text-1
containingProjects: 1
/tsconfig.json
/lib.decorators.d.ts
version: Text-1
containingProjects: 1
/tsconfig.json
/lib.decorators.legacy.d.ts
version: Text-1
containingProjects: 1
/tsconfig.json

View File

@ -0,0 +1,25 @@
/// <reference path="../fourslash.ts" />
// @Filename: /b.ts
////[||]
// @Filename: /a.ts
//// export interface Foo { }
////
//// [|export|] const foo: Foo = {}
// @Filename: /tsconfig.json
////{ "files": ["a.ts", "b.ts"] }
const range = test.ranges();
verify.pasteEdits({
args: {
pastedText: [`export`],
pasteLocations: [range[0]],
copiedFrom: { file: "a.ts", range: [range[1]] },
},
newFileContents: {
"/b.ts":
`export`
}
});