diff --git a/src/harness/client.ts b/src/harness/client.ts index 8663fa48a9ede..20cf9cfded645 100644 --- a/src/harness/client.ts +++ b/src/harness/client.ts @@ -50,6 +50,7 @@ import { OrganizeImportsArgs, OutliningSpan, PatternMatchKind, + PostPasteImportFixes, Program, QuickInfo, RefactorEditInfo, @@ -834,7 +835,11 @@ export class SessionClient implements LanguageService { if (preferences) { // Restore preferences this.configure(oldPreferences || {}); } - return response.body!; // TODO: GH#18217 + // TODO: GH#18217 + return response.body!.map(result => ({ + ...result, + actions: result.actions.map(entry => ({ ...entry, range: entry.range ? createTextSpanFromBounds(this.lineOffsetToPosition(fileName, entry.range.start), this.lineOffsetToPosition(fileName, entry.range.end)) : undefined })), + })); } getMoveToRefactoringFileSuggestions(fileName: string, positionOrRange: number | TextRange): { newFileName: string; files: string[]; } { @@ -1009,6 +1014,27 @@ export class SessionClient implements LanguageService { return getSupportedCodeFixes(); } + getPostPasteImportFixes( + targetFile: string, + copies: { text: string; copyRange?: { file: string; range: TextRange; }; }[], + pastes: TextRange[], + _preferences: UserPreferences, + _formatOptions: FormatCodeSettings, + ): PostPasteImportFixes { + const args: protocol.GetPostPasteImportFixesRequestArgs = { + file: targetFile, + copies: copies.map(copy => ({ text: copy.text, range: copy.copyRange ? { file: copy.copyRange.file, start: this.positionToOneBasedLineOffset(copy.copyRange.file, copy.copyRange.range.pos), end: this.positionToOneBasedLineOffset(copy.copyRange.file, copy.copyRange.range.end) } : undefined })), + pastes: pastes.map(paste => ({ start: this.positionToOneBasedLineOffset(targetFile, paste.pos), end: this.positionToOneBasedLineOffset(targetFile, paste.end) })), + }; + 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 }; + } + getProgram(): Program { throw new Error("Program objects are not serializable through the server protocol."); } diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 08698bf8b2721..77db29c222208 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -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.copies, options.pastes, options.preferences, this.formatCodeSettings); + 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)!; diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts index 64577f69ced7c..93c8b999871a6 100644 --- a/src/harness/fourslashInterfaceImpl.ts +++ b/src/harness/fourslashInterfaceImpl.ts @@ -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 postPasteImportFixes(options: PostPasteImportFixOptions): void { + this.state.verifyPostPasteImportFixes(options); + } } export class Edit { @@ -1883,6 +1887,13 @@ export interface VerifyCodeFixAllOptions { preferences?: ts.UserPreferences; } +export interface VerifyPostPasteImportFix { + targetFile: string; + pastes: { text: string; range: ts.TextRange; }[]; + copySpan?: { file: string; range: ts.TextRange; }; + preferences: ts.UserPreferences; +} + export interface VerifyRefactorOptions { name: string; actionName: string; @@ -1923,6 +1934,13 @@ export interface MoveToFileOptions { readonly preferences?: ts.UserPreferences; } +export interface PostPasteImportFixOptions { + readonly newFileContents: { readonly [fileName: string]: string; }; + readonly copies: { text: string; copyRange?: { file: string; range: ts.TextRange; }; }[]; + readonly pastes: ts.TextRange[]; + readonly preferences: ts.UserPreferences; +} + export type RenameLocationsOptions = readonly RenameLocationOptions[] | { readonly findInStrings?: boolean; readonly findInComments?: boolean; diff --git a/src/server/project.ts b/src/server/project.ts index 109541097f7ca..540dc65140de0 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -2215,6 +2215,19 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo return this.noDtsResolutionProject; } + /** @internal */ + runWithTemporaryFileUpdate(rootFile: string, pastedText: string, cb: (updatedProgram: Program | undefined, 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, pastedText); + this.updateGraph(); + cb(this.program, originalProgram, (this.program?.getSourceFile(rootFile))!); + this.getScriptInfo(rootFile)?.editContent(0, this.program.getSourceFile(rootFile)!.getText().length, originalText); + this.updateGraph(); + } + /** @internal */ private getCompilerOptionsForNoDtsResolutionProject() { return { diff --git a/src/server/protocol.ts b/src/server/protocol.ts index e61c2393f1d14..0f8ca06d7720a 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -144,6 +144,7 @@ export const enum CommandTypes { GetApplicableRefactors = "getApplicableRefactors", GetEditsForRefactor = "getEditsForRefactor", GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions", + GetPostPasteImportFixes = "getPostPasteImportFixes", /** @internal */ GetEditsForRefactorFull = "getEditsForRefactor-full", @@ -630,6 +631,29 @@ 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 GetPostPasteImportFixesRequestArgs = FileRequestArgs & { + copies: { + text: string; + range?: FileSpan; + }[]; + pastes: TextSpan[]; +}; +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. */ @@ -688,6 +712,11 @@ export interface RefactorActionInfo { * when calling 'GetEditsForRefactor'. */ isInteractive?: boolean; + + /** + * Range of code the refactoring will be applied to. + */ + range?: TextSpan; } export interface GetEditsForRefactorRequest extends Request { diff --git a/src/server/session.ts b/src/server/session.ts index dc2efe45ee4f2..61efcb17d1171 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -106,6 +106,7 @@ import { perfLogger, PerformanceEvent, PossibleProgramFileInfo, + PostPasteImportFixes, Program, QuickInfo, RefactorEditInfo, @@ -2751,7 +2752,8 @@ export class Session<TMessage = string> implements EventSender { private getApplicableRefactors(args: protocol.GetApplicableRefactorsRequestArgs): protocol.ApplicableRefactorInfo[] { const { file, project } = this.getFileAndProject(args); const scriptInfo = project.getScriptInfoForNormalizedPath(file)!; - return project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind, args.includeInteractiveActions); + const result = project.getLanguageService().getApplicableRefactors(file, this.extractPositionOrRange(args, scriptInfo), this.getPreferences(file), args.triggerReason, args.kind, args.includeInteractiveActions); + return result.map(result => ({ ...result, actions: result.actions.map(action => ({ ...action, range: action.range ? toProtocolTextSpan(action.range, scriptInfo) : undefined })) })); } private getEditsForRefactor(args: protocol.GetEditsForRefactorRequestArgs, simplifiedResult: boolean): RefactorEditInfo | protocol.RefactorEditInfo { @@ -2796,6 +2798,26 @@ 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 copyFile = args.copies[0].range ? args.copies[0].range.file : undefined; + const result = project.getLanguageService().getPostPasteImportFixes( + file, + args.copies.map(copy => ({ + text: copy.text, + copyRange: copy.range && copyFile + ? { file: copy.range.file, range: this.getRange({ file: copyFile, startLine: copy.range.start.line, startOffset: copy.range.start.offset, endLine: copy.range.end.line, endOffset: copy.range.end.offset }, project.getScriptInfoForNormalizedPath(toNormalizedPath(copyFile))!) } : undefined, + })), + args.pastes.map(paste => this.getRange({ file, startLine: paste.start.line, startOffset: paste.start.offset, endLine: paste.end.line, endOffset: paste.end.offset }, project.getScriptInfoForNormalizedPath(file)!)), + this.getPreferences(file), + this.getFormatOptions(file), + ); + if (result === undefined) { + return undefined; + } + return this.mapPostPasteAction(result); + } + 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); @@ -2928,6 +2950,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)); } @@ -3521,6 +3547,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)); }, diff --git a/src/services/_namespaces/ts.postPasteImportFixes.ts b/src/services/_namespaces/ts.postPasteImportFixes.ts new file mode 100644 index 0000000000000..0f1f3e8189d63 --- /dev/null +++ b/src/services/_namespaces/ts.postPasteImportFixes.ts @@ -0,0 +1 @@ +export * from "../postPasteImportFixes"; diff --git a/src/services/_namespaces/ts.ts b/src/services/_namespaces/ts.ts index e30ea818a04b5..a0fb6bf9ca8ff 100644 --- a/src/services/_namespaces/ts.ts +++ b/src/services/_namespaces/ts.ts @@ -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 }; diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index f6e192dc4b9a0..9980d418fa7a1 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -210,6 +210,7 @@ export interface ImportAdder { hasFixes(): boolean; addImportFromDiagnostic: (diagnostic: DiagnosticWithLocation, context: CodeFixContextBase) => void; addImportFromExportedSymbol: (exportedSymbol: Symbol, isValidTypeOnlyUseSite?: boolean) => void; + addImportForUnresolvedIdentifier: (context: CodeFixContextBase, symbolToken: Identifier, useAutoImportProvider: boolean) => void; writeFixes: (changeTracker: textChanges.ChangeTracker, oldFileQuotePreference?: QuotePreference) => void; } @@ -235,7 +236,13 @@ 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, addImportForUnresolvedIdentifier: 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); @@ -1009,6 +1016,12 @@ function sortFixInfo(fixes: readonly (FixInfo & { fix: ImportFixWithModuleSpecif 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 diff --git a/src/services/postPasteImportFixes.ts b/src/services/postPasteImportFixes.ts new file mode 100644 index 0000000000000..029aa78aed44a --- /dev/null +++ b/src/services/postPasteImportFixes.ts @@ -0,0 +1,103 @@ +import { + addRange, +} from "../compiler/core"; +import { + CancellationToken, + SourceFile, + Statement, + SymbolFlags, + TextRange, + UserPreferences, +} from "../compiler/types"; +import { + getLineOfLocalPosition, +} from "../compiler/utilities"; +import { + codefix, + fileShouldUseJavaScriptRequire, + forEachChild, + formatting, + getQuotePreference, + getTargetFileImportsAndAddExportInOldFile, + insertImports, + isIdentifier, + textChanges, +} from "./_namespaces/ts"; +import { + getExistingLocals, + getUsageInfo, +} from "./refactors/moveToFile"; +import { + CodeFixContextBase, + FileTextChanges, + LanguageServiceHost, + PostPasteImportFixes, +} from "./types"; + +/** @internal */ +export function postPasteImportFixesProvider( + targetFile: SourceFile, + copies: { text: string; copyRange?: { file: SourceFile; range: TextRange; }; }[], + pastes: TextRange[], + host: LanguageServiceHost, + preferences: UserPreferences, + formatContext: formatting.FormatContext, + cancellationToken: CancellationToken, +): PostPasteImportFixes { + const changes: FileTextChanges[] = textChanges.ChangeTracker.with({ host, formatContext, preferences }, changeTracker => postPasteFixes(targetFile, copies, pastes, host, preferences, formatContext, cancellationToken, changeTracker)); + return { edits: changes }; +} + +function postPasteFixes( + targetFile: SourceFile, + copies: { text: string; copyRange?: { file: SourceFile; range: TextRange; }; }[], + pastes: TextRange[], + host: LanguageServiceHost, + preferences: UserPreferences, + formatContext: formatting.FormatContext, + cancellationToken: CancellationToken, + changes: textChanges.ChangeTracker, +) { + const copy = copies[0]; + const statements: Statement[] = []; + + host.runWithTemporaryFileUpdate?.(targetFile.fileName, targetFile.getText().slice(0, pastes[0].pos) + copy.text + targetFile.getText().slice(pastes[0].end), (updatedProgram, originalProgram, updatedFile) => { + if (copy.copyRange) { + addRange(statements, copy.copyRange.file.statements, getLineOfLocalPosition(copy.copyRange.file, copy.copyRange.range.pos), getLineOfLocalPosition(copy.copyRange.file, copy.copyRange.range.end) + 1); + const usage = getUsageInfo(copy.copyRange.file, statements, originalProgram!.getTypeChecker(), getExistingLocals(updatedFile, statements, originalProgram!.getTypeChecker())); + const importAdder = codefix.createImportAdder(updatedFile, updatedProgram!, preferences, host); + + const imports = getTargetFileImportsAndAddExportInOldFile(copy.copyRange.file, targetFile.fileName, usage.oldImportsNeededByTargetFile, usage.targetFileImportsFromOldFile, changes, originalProgram!.getTypeChecker(), updatedProgram!, host, !fileShouldUseJavaScriptRequire(targetFile.fileName, updatedProgram!, host, !!copy.copyRange.file.commonJsModuleIndicator), getQuotePreference(targetFile, preferences), importAdder); + if (imports.length > 0) { + insertImports(changes, targetFile, imports, /*blankLineBetween*/ true, preferences); + } + importAdder.writeFixes(changes, getQuotePreference(copy.copyRange.file, preferences)); + } + else { + const context: CodeFixContextBase = { + sourceFile: updatedFile, + program: originalProgram!, + cancellationToken, + host, + preferences, + formatContext, + }; + const importAdder = codefix.createImportAdder(updatedFile, updatedProgram!, preferences, host); + forEachChild(updatedFile, function cb(node) { + if (isIdentifier(node)) { + if (!originalProgram?.getTypeChecker().resolveName(node.text, node, SymbolFlags.All, /*excludeGlobals*/ false)) { + // generate imports + importAdder.addImportForUnresolvedIdentifier(context, node, /*useAutoImportProvider*/ true); + } + } + else { + node.forEachChild(cb); + } + }); + importAdder.writeFixes(changes, getQuotePreference(targetFile, preferences)); + } + }); + pastes.forEach(paste => { + changes.replaceRangeWithText(targetFile, { pos: paste.pos, end: paste.end }, copy.text); + }); +} diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index b9406e8f1b594..0634eed2a3660 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -219,7 +219,7 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea return errors; } - const extractions = getPossibleExtractions(targetRange, context); + const { affectedTextRange, extractions } = getPossibleExtractions(targetRange, context); if (extractions === undefined) { // No extractions possible return emptyArray; @@ -247,6 +247,7 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea description, name: `function_scope_${i}`, kind: extractFunctionAction.kind, + range: { start: affectedTextRange.pos, length: affectedTextRange.end - affectedTextRange.pos }, }); } } @@ -272,6 +273,7 @@ export function getRefactorActionsToExtractSymbol(context: RefactorContext): rea description, name: `constant_scope_${i}`, kind: extractConstantAction.kind, + range: { start: affectedTextRange.pos, length: affectedTextRange.end - affectedTextRange.pos }, }); } } @@ -909,8 +911,8 @@ interface ScopeExtractions { * Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes * or an error explaining why we can't extract into that scope. */ -function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): readonly ScopeExtractions[] | undefined { - const { scopes, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); +function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): { readonly affectedTextRange: TextRange; readonly extractions: ScopeExtractions[] | undefined; } { + const { scopes, affectedTextRange, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); // Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547 const extractions = scopes.map((scope, i): ScopeExtractions => { const functionDescriptionPart = getDescriptionForFunctionInScope(scope); @@ -953,10 +955,10 @@ function getPossibleExtractions(targetRange: TargetRange, context: RefactorConte }, }; }); - return extractions; + return { affectedTextRange, extractions }; } -function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[]; readonly readsAndWrites: ReadsAndWrites; } { +function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[]; readonly affectedTextRange: TextRange; readonly readsAndWrites: ReadsAndWrites; } { const { file: sourceFile } = context; const scopes = collectEnclosingScopes(targetRange); @@ -969,7 +971,7 @@ function getPossibleExtractionsWorker(targetRange: TargetRange, context: Refacto context.program.getTypeChecker(), context.cancellationToken!, ); - return { scopes, readsAndWrites }; + return { scopes, affectedTextRange: enclosingTextRange, readsAndWrites }; } function getDescriptionForFunctionInScope(scope: Scope): string { diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts index e9d43a1a3c219..3dfcef71511a7 100644 --- a/src/services/refactors/extractType.ts +++ b/src/services/refactors/extractType.ts @@ -99,16 +99,20 @@ registerRefactor(refactorName, { extractToTypeDefAction.kind, ], getAvailableActions: function getRefactorActionsToExtractType(context): readonly ApplicableRefactorInfo[] { - const info = getRangeToExtract(context, context.triggerReason === "invoked"); + const { info, affectedTextRange } = getRangeToExtract(context, context.triggerReason === "invoked"); if (!info) return emptyArray; if (!isRefactorErrorInfo(info)) { - return [{ + const refactorInfo: ApplicableRefactorInfo[] = [{ name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_type), actions: info.isJS ? [extractToTypeDefAction] : append([extractToTypeAliasAction], info.typeElements && extractToInterfaceAction), }]; + return refactorInfo.map(info => ({ + ...info, + actions: info.actions.map(action => ({ ...action, range: affectedTextRange ? { start: affectedTextRange.pos, length: affectedTextRange.end - affectedTextRange.pos } : undefined })), + })); } if (context.preferences.provideRefactorNotApplicableReason) { @@ -127,7 +131,7 @@ registerRefactor(refactorName, { }, getEditsForAction: function getRefactorEditsToExtractType(context, actionName): RefactorEditInfo { const { file } = context; - const info = getRangeToExtract(context); + const { info } = getRangeToExtract(context); Debug.assert(info && !isRefactorErrorInfo(info), "Expected to find a range to extract"); const name = getUniqueName("NewType", file); @@ -171,20 +175,20 @@ interface InterfaceInfo { type ExtractInfo = TypeAliasInfo | InterfaceInfo; -function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): ExtractInfo | RefactorErrorInfo | undefined { +function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): { info: ExtractInfo | RefactorErrorInfo | undefined; affectedTextRange?: TextRange; } { const { file, startPosition } = context; const isJS = isSourceFileJS(file); const range = createTextRangeFromSpan(getRefactorContextSpan(context)); const isCursorRequest = range.pos === range.end && considerEmptySpans; const firstType = getFirstTypeAt(file, startPosition, range, isCursorRequest); - if (!firstType || !isTypeNode(firstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }; + if (!firstType || !isTypeNode(firstType)) return { info: { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }, affectedTextRange: undefined }; const checker = context.program.getTypeChecker(); const enclosingNode = getEnclosingNode(firstType, isJS); - if (enclosingNode === undefined) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }; + if (enclosingNode === undefined) return { info: { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }, affectedTextRange: undefined }; const expandedFirstType = getExpandedSelectionNode(firstType, enclosingNode); - if (!isTypeNode(expandedFirstType)) return { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }; + if (!isTypeNode(expandedFirstType)) return { info: { error: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_type_node) }, affectedTextRange: undefined }; const typeList: TypeNode[] = []; if ((isUnionTypeNode(expandedFirstType.parent) || isIntersectionTypeNode(expandedFirstType.parent)) && range.end > firstType.end) { @@ -198,11 +202,11 @@ function getRangeToExtract(context: RefactorContext, considerEmptySpans = true): } const selection = typeList.length > 1 ? typeList : expandedFirstType; - const typeParameters = collectTypeParameters(checker, selection, enclosingNode, file); - if (!typeParameters) return { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }; + const { typeParameters, affectedTextRange } = collectTypeParameters(checker, selection, enclosingNode, file); + if (!typeParameters) return { info: { error: getLocaleSpecificMessage(Diagnostics.No_type_could_be_extracted_from_this_type_node) }, affectedTextRange: undefined }; const typeElements = flattenTypeLiteralNodeReference(checker, selection); - return { isJS, selection, enclosingNode, typeParameters, typeElements }; + return { info: { isJS, selection, enclosingNode, typeParameters, typeElements }, affectedTextRange }; } function getFirstTypeAt(file: SourceFile, startPosition: number, range: TextRange, isCursorRequest: boolean): Node | undefined { @@ -260,14 +264,14 @@ function rangeContainsSkipTrivia(r1: TextRange, node: TextRange, file: SourceFil return rangeContainsStartEnd(r1, skipTrivia(file.text, node.pos), node.end); } -function collectTypeParameters(checker: TypeChecker, selection: TypeNode | TypeNode[], enclosingNode: Node, file: SourceFile): TypeParameterDeclaration[] | undefined { +function collectTypeParameters(checker: TypeChecker, selection: TypeNode | TypeNode[], enclosingNode: Node, file: SourceFile): { typeParameters: TypeParameterDeclaration[] | undefined; affectedTextRange: TextRange | undefined; } { const result: TypeParameterDeclaration[] = []; const selectionArray = toArray(selection); const selectionRange = { pos: selectionArray[0].pos, end: selectionArray[selectionArray.length - 1].end }; for (const t of selectionArray) { - if (visitor(t)) return undefined; + if (visitor(t)) return { typeParameters: undefined, affectedTextRange: undefined }; } - return result; + return { typeParameters: result, affectedTextRange: selectionRange }; function visitor(node: Node): true | undefined { if (isTypeReferenceNode(node)) { diff --git a/src/services/refactors/moveToFile.ts b/src/services/refactors/moveToFile.ts index 1d3d072d89c07..a3ac9d9e4bde9 100644 --- a/src/services/refactors/moveToFile.ts +++ b/src/services/refactors/moveToFile.ts @@ -3,7 +3,6 @@ import { } from "../../compiler/moduleSpecifiers"; import { AnyImportOrRequireStatement, - append, ApplicableRefactorInfo, arrayFrom, AssignmentDeclarationKind, @@ -59,6 +58,7 @@ import { getRelativePathFromFile, getSourceFileOfNode, getSynthesizedDeepClone, + getTargetFileImportsAndAddExportInOldFile, getTokenAtPosition, getUniqueName, hasJSFileExtension, @@ -115,7 +115,6 @@ import { NamedImportBindings, Node, NodeFlags, - nodeSeenTracker, normalizePath, ObjectBindingElementWithoutPropertyName, Program, @@ -179,7 +178,8 @@ registerRefactor(refactorNameForMoveToFile, { } } if (context.preferences.allowTextChangesInNewFiles && statements) { - return [{ name: refactorNameForMoveToFile, description, actions: [moveToFileAction] }]; + const affectedTextRange = { start: statements.all[0].pos, length: statements.all.length > 1 ? last(statements.all).end - statements.all[0].pos : statements.all[0].end - statements.all[0].pos }; + return [{ name: refactorNameForMoveToFile, description, actions: [{ ...moveToFileAction, range: affectedTextRange }] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [{ name: refactorNameForMoveToFile, description, actions: [{ ...moveToFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }]; @@ -285,95 +285,6 @@ function getNewStatementsAndRemoveFromOldFile( ]; } -function getTargetFileImportsAndAddExportInOldFile( - oldFile: SourceFile, - targetFile: string, - importsToCopy: Map<Symbol, boolean>, - targetFileImportsFromOldFile: Set<Symbol>, - changes: textChanges.ChangeTracker, - checker: TypeChecker, - program: Program, - host: LanguageServiceHost, - useEsModuleSyntax: boolean, - quotePreference: QuotePreference, - importAdder?: codefix.ImportAdder, -): readonly AnyImportOrRequireStatement[] { - const copiedOldImports: AnyImportOrRequireStatement[] = []; - /** - * Recomputing the imports is preferred with importAdder because it manages multiple import additions for a file and writes then to a ChangeTracker, - * but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file). - * So in that case, fall back to copying the import verbatim. - */ - if (importAdder) { - importsToCopy.forEach((isValidTypeOnlyUseSite, symbol) => { - try { - importAdder.addImportFromExportedSymbol(skipAlias(symbol, checker), isValidTypeOnlyUseSite); - } - catch { - for (const oldStatement of oldFile.statements) { - forEachImportInStatement(oldStatement, i => { - append(copiedOldImports, filterImport(i, factory.createStringLiteral(moduleSpecifierFromImport(i).text), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); - }); - } - } - }); - } - else { - const targetSourceFile = program.getSourceFile(targetFile); // Would be undefined for a new file - for (const oldStatement of oldFile.statements) { - forEachImportInStatement(oldStatement, i => { - // Recomputing module specifier - const moduleSpecifier = moduleSpecifierFromImport(i); - const compilerOptions = program.getCompilerOptions(); - const resolved = program.getResolvedModuleFromModuleSpecifier(moduleSpecifier); - const fileName = resolved?.resolvedModule?.resolvedFileName; - if (fileName && targetSourceFile) { - const newModuleSpecifier = getModuleSpecifier(compilerOptions, targetSourceFile, targetSourceFile.fileName, fileName, createModuleSpecifierResolutionHost(program, host)); - append(copiedOldImports, filterImport(i, makeStringLiteral(newModuleSpecifier, quotePreference), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); - } - else { - append(copiedOldImports, filterImport(i, factory.createStringLiteral(moduleSpecifierFromImport(i).text), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); - } - }); - } - } - - // Also, import things used from the old file, and insert 'export' modifiers as necessary in the old file. - const targetFileSourceFile = program.getSourceFile(targetFile); - let oldFileDefault: Identifier | undefined; - const oldFileNamedImports: string[] = []; - const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`. - targetFileImportsFromOldFile.forEach(symbol => { - if (!symbol.declarations) { - return; - } - for (const decl of symbol.declarations) { - if (!isTopLevelDeclaration(decl)) continue; - const name = nameOfTopLevelDeclaration(decl); - if (!name) continue; - - const top = getTopLevelDeclarationStatement(decl); - if (markSeenTop(top)) { - addExportToChanges(oldFile, top, name, changes, useEsModuleSyntax); - } - if (importAdder && checker.isUnknownSymbol(symbol)) { - importAdder.addImportFromExportedSymbol(skipAlias(symbol, checker)); - } - else { - if (hasSyntacticModifier(decl, ModifierFlags.Default)) { - oldFileDefault = name; - } - else { - oldFileNamedImports.push(name.text); - } - } - } - }); - return targetFileSourceFile - ? append(copiedOldImports, makeImportOrRequire(targetFileSourceFile, oldFileDefault, oldFileNamedImports, oldFile.fileName, program, host, useEsModuleSyntax, quotePreference)) - : append(copiedOldImports, makeImportOrRequire(oldFile, oldFileDefault, oldFileNamedImports, oldFile.fileName, program, host, useEsModuleSyntax, quotePreference)); -} - /** @internal */ export function addNewFileToTsconfig(program: Program, changes: textChanges.ChangeTracker, oldFileName: string, newFileNameWithExtension: string, getCanonicalFileName: GetCanonicalFileName): void { const cfg = program.getCompilerOptions().configFile; @@ -1257,7 +1168,8 @@ function getOverloadRangeToMove(sourceFile: SourceFile, statement: Statement) { return undefined; } -function getExistingLocals(sourceFile: SourceFile, statements: readonly Statement[], checker: TypeChecker) { +/** @internal */ +export function getExistingLocals(sourceFile: SourceFile, statements: readonly Statement[], checker: TypeChecker) { const existingLocals = new Set<Symbol>(); for (const moduleSpecifier of sourceFile.imports) { const declaration = importFromModuleSpecifier(moduleSpecifier); diff --git a/src/services/refactors/moveToNewFile.ts b/src/services/refactors/moveToNewFile.ts index d2cb36084499c..8f0d11fbd15fe 100644 --- a/src/services/refactors/moveToNewFile.ts +++ b/src/services/refactors/moveToNewFile.ts @@ -14,6 +14,7 @@ import { insertImports, isPrologueDirective, LanguageServiceHost, + last, ModifierFlags, nodeSeenTracker, Program, @@ -64,7 +65,8 @@ registerRefactor(refactorName, { getAvailableActions: function getRefactorActionsToMoveToNewFile(context): readonly ApplicableRefactorInfo[] { const statements = getStatementsToMove(context); if (context.preferences.allowTextChangesInNewFiles && statements) { - return [{ name: refactorName, description, actions: [moveToNewFileAction] }]; + const affectedTextRange = { start: statements.all[0].pos, length: statements.all.length > 1 ? last(statements.all).end - statements.all[0].pos : statements.all[0].end - statements.all[0].pos }; + return [{ name: refactorName, description, actions: [{ ...moveToNewFileAction, range: affectedTextRange }] }]; } if (context.preferences.provideRefactorNotApplicableReason) { return [{ name: refactorName, description, actions: [{ ...moveToNewFileAction, notApplicableReason: getLocaleSpecificMessage(Diagnostics.Selection_is_not_a_valid_statement_or_statements) }] }]; diff --git a/src/services/services.ts b/src/services/services.ts index 32a54a897242b..166e2b7c85243 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -241,6 +241,8 @@ import { Path, positionIsSynthesized, PossibleProgramFileInfo, + PostPasteImportFixes, + postPasteImportFixes, PragmaMap, PrivateIdentifier, Program, @@ -2091,6 +2093,25 @@ export function createLanguageService( }; } + function getPostPasteImportFixes( + targetFile: string, + copies: { text: string; copyRange?: { file: string; range: TextRange; }; }[], + pastes: TextRange[], + preferences: UserPreferences, + formatOptions: FormatCodeSettings, + ): PostPasteImportFixes { + synchronizeHostData(); + return postPasteImportFixes.postPasteImportFixesProvider( + getValidSourceFile(targetFile), + copies.map(({ text, copyRange }) => ({ text, copyRange: copyRange ? { file: getValidSourceFile(copyRange.file), range: copyRange.range } : undefined })), + pastes, + host, + preferences, + formatting.getFormatContext(formatOptions, host), + cancellationToken, + ); + } + function getNodeForQuickInfo(node: Node): Node { if (isNewExpression(node.parent) && node.pos === node.parent.pos) { return node.parent.expression; @@ -3169,6 +3190,7 @@ export function createLanguageService( uncommentSelection, provideInlayHints, getSupportedCodeFixes, + getPostPasteImportFixes, }; switch (languageServiceMode) { diff --git a/src/services/types.ts b/src/services/types.ts index 9bb560af02ac9..6221754a300d9 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -431,7 +431,7 @@ export interface LanguageServiceHost extends GetEffectiveTypeRootsHost, MinimalR getParsedCommandLine?(fileName: string): ParsedCommandLine | undefined; /** @internal */ onReleaseParsedCommandLine?(configFileName: string, oldResolvedRef: ResolvedProjectReference | undefined, optionOptions: CompilerOptions): void; /** @internal */ getIncompleteCompletionsCache?(): IncompleteCompletionsCache; - + /** @internal */ runWithTemporaryFileUpdate?(rootFile: string, pastedText: string, cb: (updatedProgram: Program | undefined, originalProgram: Program | undefined, updatedPastedText: SourceFile) => void): void; jsDocParsingMode?: JSDocParsingMode | undefined; } @@ -684,6 +684,13 @@ export interface LanguageService { getSupportedCodeFixes(fileName?: string): readonly string[]; dispose(): void; + getPostPasteImportFixes( + targetFile: string, + copies: { text: string; range?: { file: string; range: TextRange; }; }[], + pastes: TextRange[], + preferences: UserPreferences, + formatOptions: FormatCodeSettings, + ): PostPasteImportFixes; } export interface JsxClosingTagInfo { @@ -706,6 +713,10 @@ export const enum OrganizeImportsMode { RemoveUnused = "RemoveUnused", } +export interface PostPasteImportFixes { + edits: readonly FileTextChanges[]; +} + export interface OrganizeImportsArgs extends CombinedCodeFixScope { /** @deprecated Use `mode` instead */ skipDestructiveCodeActions?: boolean; @@ -1008,6 +1019,11 @@ export interface RefactorActionInfo { * when calling `getEditsForRefactor`. */ isInteractive?: boolean; + + /** + * Range of code the refactoring will be applied to. + */ + range?: TextSpan; } /** diff --git a/src/services/utilities.ts b/src/services/utilities.ts index dc5c6c777528e..e476ceb506cf0 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1,9 +1,13 @@ +import { + getModuleSpecifier, +} from "../compiler/_namespaces/ts.moduleSpecifiers"; import { __String, addEmitFlags, addSyntheticLeadingComment, addSyntheticTrailingComment, AnyImportOrRequireStatement, + append, assertType, AssignmentDeclarationKind, BinaryExpression, @@ -378,6 +382,16 @@ import { walkUpParenthesizedExpressions, YieldExpression, } from "./_namespaces/ts"; +import { + addExportToChanges, + filterImport, + forEachImportInStatement, + getTopLevelDeclarationStatement, + isTopLevelDeclaration, + makeImportOrRequire, + moduleSpecifierFromImport, + nameOfTopLevelDeclaration, +} from "./_namespaces/ts.refactor"; // These utilities are common to multiple language service features. // #region @@ -4288,3 +4302,93 @@ export function isBlockLike(node: Node): node is BlockLike { return false; } } + +/** @internal */ +export function getTargetFileImportsAndAddExportInOldFile( + oldFile: SourceFile, + targetFile: string, + importsToCopy: Map<Symbol, boolean>, + targetFileImportsFromOldFile: Set<Symbol>, + changes: textChanges.ChangeTracker, + checker: TypeChecker, + program: Program, + host: LanguageServiceHost, + useEsModuleSyntax: boolean, + quotePreference: QuotePreference, + importAdder?: codefix.ImportAdder, +): readonly AnyImportOrRequireStatement[] { + const copiedOldImports: AnyImportOrRequireStatement[] = []; + /** + * Recomputing the imports is preferred with importAdder because it manages multiple import additions for a file and writes then to a ChangeTracker, + * but sometimes it fails because of unresolved imports from files, or when a source file is not available for the target file (in this case when creating a new file). + * So in that case, fall back to copying the import verbatim. + */ + if (importAdder) { + importsToCopy.forEach((isValidTypeOnlyUseSite, symbol) => { + try { + importAdder.addImportFromExportedSymbol(skipAlias(symbol, checker), isValidTypeOnlyUseSite); + } + catch { + for (const oldStatement of oldFile.statements) { + forEachImportInStatement(oldStatement, i => { + append(copiedOldImports, filterImport(i, factory.createStringLiteral(moduleSpecifierFromImport(i).text), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); + }); + } + } + }); + } + else { + const targetSourceFile = program.getSourceFile(targetFile); // Would be undefined for a new file + for (const oldStatement of oldFile.statements) { + forEachImportInStatement(oldStatement, i => { + // Recomputing module specifier + const moduleSpecifier = moduleSpecifierFromImport(i); + const compilerOptions = program.getCompilerOptions(); + const resolved = program.getResolvedModuleFromModuleSpecifier(moduleSpecifier); + const fileName = resolved?.resolvedModule?.resolvedFileName; + if (fileName && targetSourceFile) { + const newModuleSpecifier = getModuleSpecifier(compilerOptions, targetSourceFile, targetSourceFile.fileName, fileName, createModuleSpecifierResolutionHost(program, host)); + append(copiedOldImports, filterImport(i, makeStringLiteral(newModuleSpecifier, quotePreference), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); + } + else { + append(copiedOldImports, filterImport(i, factory.createStringLiteral(moduleSpecifierFromImport(i).text), name => importsToCopy.has(checker.getSymbolAtLocation(name)!))); + } + }); + } + } + + // Also, import things used from the old file, and insert 'export' modifiers as necessary in the old file. + const targetFileSourceFile = program.getSourceFile(targetFile); + let oldFileDefault: Identifier | undefined; + const oldFileNamedImports: string[] = []; + const markSeenTop = nodeSeenTracker(); // Needed because multiple declarations may appear in `const x = 0, y = 1;`. + targetFileImportsFromOldFile.forEach(symbol => { + if (!symbol.declarations) { + return; + } + for (const decl of symbol.declarations) { + if (!isTopLevelDeclaration(decl)) continue; + const name = nameOfTopLevelDeclaration(decl); + if (!name) continue; + + const top = getTopLevelDeclarationStatement(decl); + if (markSeenTop(top)) { + addExportToChanges(oldFile, top, name, changes, useEsModuleSyntax); + } + if (importAdder && checker.isUnknownSymbol(symbol)) { + importAdder.addImportFromExportedSymbol(skipAlias(symbol, checker)); + } + else { + if (hasSyntacticModifier(decl, ModifierFlags.Default)) { + oldFileDefault = name; + } + else { + oldFileNamedImports.push(name.text); + } + } + } + }); + return targetFileSourceFile + ? append(copiedOldImports, makeImportOrRequire(targetFileSourceFile, oldFileDefault, oldFileNamedImports, oldFile.fileName, program, host, useEsModuleSyntax, quotePreference)) + : append(copiedOldImports, makeImportOrRequire(oldFile, oldFileDefault, oldFileNamedImports, oldFile.fileName, program, host, useEsModuleSyntax, quotePreference)); +} diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index 01d5ddea11d4d..4be8d6dd1d3f8 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -214,3 +214,4 @@ import "./unittests/debugDeprecation"; import "./unittests/tsserver/inconsistentErrorInEditor"; import "./unittests/tsserver/getMoveToRefactoringFileSuggestions"; import "./unittests/skipJSDocParsing"; +import "./unittests/tsserver/postPasteImportFixes"; diff --git a/src/testRunner/unittests/tsserver/getApplicableRefactors.ts b/src/testRunner/unittests/tsserver/getApplicableRefactors.ts index 6ee8a48be6ce7..92cbc2b0abd49 100644 --- a/src/testRunner/unittests/tsserver/getApplicableRefactors.ts +++ b/src/testRunner/unittests/tsserver/getApplicableRefactors.ts @@ -21,4 +21,69 @@ describe("unittests:: tsserver:: getApplicableRefactors", () => { }); baselineTsserverLogs("getApplicableRefactors", "works when taking position", session); }); + + it("returns the affected range of text for extract symbol refactor", () => { + const file1: File = { + path: "/a.ts", + content: `class Foo { + someMethod(m: number) { + var x = m; + x = x * 3; + var y = 30; + var j = 10; + var z = y + j; + console.log(z); + var q = 10; + return q; + } +}`, + }; + const host = createServerHost([file1]); + const session = new TestSession(host); + openFilesForSession([file1], session); + session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({ + command: ts.server.protocol.CommandTypes.GetApplicableRefactors, + arguments: { file: file1.path, startLine: 3, startOffset: 9, endLine: 5, endOffset: 20 }, + }); + baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for extract symbol refactor", session); + }); + + it("returns the affected range of text for extract type refactor", () => { + const file1: File = { + path: "/a.ts", + content: `type A<B, C, D = B> = Partial<C | string | D> & D | C;`, + }; + const host = createServerHost([file1]); + const session = new TestSession(host); + openFilesForSession([file1], session); + session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({ + command: ts.server.protocol.CommandTypes.GetApplicableRefactors, + arguments: { file: file1.path, startLine: 1, startOffset: 26, endLine: 1, endOffset: 38 }, + }); + baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for extract type refactor", session); + }); + + it("returns the affected range of text for 'move to file' and 'move to new file' refactors", () => { + const file1: File = { + path: "/a.ts", + content: `const a = 1; +const b = 1; +function foo() { }`, + }; + const host = createServerHost([file1]); + const session = new TestSession(host); + openFilesForSession([file1], session); + + session.executeCommandSeq<ts.server.protocol.ConfigureRequest>({ + command: ts.server.protocol.CommandTypes.Configure, + arguments: { + preferences: { allowTextChangesInNewFiles: true }, + }, + }); + session.executeCommandSeq<ts.server.protocol.GetApplicableRefactorsRequest>({ + command: ts.server.protocol.CommandTypes.GetApplicableRefactors, + arguments: { file: file1.path, startLine: 1, startOffset: 3, endLine: 2, endOffset: 3, includeInteractiveActions: true }, + }); + baselineTsserverLogs("getApplicableRefactors", "returns the affected range of text for 'move to file' and 'move to new file' refactors", session); + }); }); diff --git a/src/testRunner/unittests/tsserver/postPasteImportFixes.ts b/src/testRunner/unittests/tsserver/postPasteImportFixes.ts new file mode 100644 index 0000000000000..90c0c4c3bc954 --- /dev/null +++ b/src/testRunner/unittests/tsserver/postPasteImportFixes.ts @@ -0,0 +1,40 @@ +import * as ts from "../../_namespaces/ts"; +import { + baselineTsserverLogs, + openFilesForSession, + TestSession, +} from "../helpers/tsserver"; +import { + createServerHost, + File, +} from "../helpers/virtualFileSystemWithWatch"; + +describe("unittests:: tsserver:: postPasteImportFixes", () => { + it("returns the same file unchanged, after updating and reverting changes added to a file", () => { + const target: File = { + path: "/project/a/target.ts", + content: `const a = 1; +const b = 2; +const c = 3;`, + }; + const tsconfig: File = { + path: "/project/tsconfig.json", + content: "{}", + }; + const pastedText = `const a = 1; +function e(); +const f = r + s; +const b = 2; +const c = 3;`; + + const host = createServerHost([target, tsconfig]); + const session = new TestSession(host); + openFilesForSession([target], session); + + const hostProject = session.getProjectService().configuredProjects.get(tsconfig.path)!; + hostProject.runWithTemporaryFileUpdate(target.path, pastedText, (_updatedProgram, _originalProgram, _updatedFile) => {}); + + assert.strictEqual(hostProject.getCurrentProgram()?.getSourceFileByPath(target.path as ts.Path)?.getText(), target.content); + baselineTsserverLogs("getPostPasteImportFixes", "Returns the same file unchanged ", session); + }); +}); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index a131733463740..76ade2ea1fd91 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -161,6 +161,7 @@ declare namespace ts { GetApplicableRefactors = "getApplicableRefactors", GetEditsForRefactor = "getEditsForRefactor", GetMoveToRefactoringFileSuggestions = "getMoveToRefactoringFileSuggestions", + GetPostPasteImportFixes = "getPostPasteImportFixes", OrganizeImports = "organizeImports", GetEditsForFileRename = "getEditsForFileRename", ConfigurePlugin = "configurePlugin", @@ -545,6 +546,26 @@ declare namespace ts { files: string[]; }; } + /** + * Request refactorings at a given position post pasting text from some other location. + */ + interface GetPostPasteImportFixesRequest extends Request { + command: CommandTypes.GetPostPasteImportFixes; + arguments: GetPostPasteImportFixesRequestArgs; + } + type GetPostPasteImportFixesRequestArgs = FileRequestArgs & { + copies: { + text: string; + range?: FileSpan; + }[]; + pastes: TextSpan[]; + }; + interface GetPostPasteImportFixesResponse extends Response { + body: PostPasteImportAction; + } + interface PostPasteImportAction { + edits: FileCodeEdits[]; + } /** * A set of one or more available refactoring actions, grouped under a parent refactoring. */ @@ -597,6 +618,10 @@ declare namespace ts { * when calling 'GetEditsForRefactor'. */ isInteractive?: boolean; + /** + * Range of code the refactoring will be applied to. + */ + range?: TextSpan; } interface GetEditsForRefactorRequest extends Request { command: CommandTypes.GetEditsForRefactor; @@ -4089,6 +4114,7 @@ declare namespace ts { private getApplicableRefactors; private getEditsForRefactor; private getMoveToRefactoringFileSuggestions; + private getPostPasteImportFixes; private organizeImports; private getEditsForFileRename; private getCodeFixes; @@ -4097,6 +4123,7 @@ declare namespace ts { private getStartAndEndPosition; private mapCodeAction; private mapCodeFixAction; + private mapPostPasteAction; private mapTextChangesToCodeEdits; private mapTextChangeToCodeEdit; private convertTextChangeToCodeEdit; @@ -10661,6 +10688,19 @@ declare namespace ts { uncommentSelection(fileName: string, textRange: TextRange): TextChange[]; getSupportedCodeFixes(fileName?: string): readonly string[]; dispose(): void; + getPostPasteImportFixes( + targetFile: string, + copies: { + text: string; + range?: { + file: string; + range: TextRange; + }; + }[], + pastes: TextRange[], + preferences: UserPreferences, + formatOptions: FormatCodeSettings, + ): PostPasteImportFixes; } interface JsxClosingTagInfo { readonly newText: string; @@ -10678,6 +10718,9 @@ declare namespace ts { SortAndCombine = "SortAndCombine", RemoveUnused = "RemoveUnused", } + interface PostPasteImportFixes { + edits: readonly FileTextChanges[]; + } interface OrganizeImportsArgs extends CombinedCodeFixScope { /** @deprecated Use `mode` instead */ skipDestructiveCodeActions?: boolean; @@ -10933,6 +10976,10 @@ declare namespace ts { * when calling `getEditsForRefactor`. */ isInteractive?: boolean; + /** + * Range of code the refactoring will be applied to. + */ + range?: TextSpan; } /** * A set of edits to make in response to a refactor action, plus an optional diff --git a/tests/baselines/reference/tsserver/fourslashServer/fixExtractToInnerFunctionDuplicaton.js b/tests/baselines/reference/tsserver/fourslashServer/fixExtractToInnerFunctionDuplicaton.js index aa50b4634e62c..96656bf3bacbb 100644 --- a/tests/baselines/reference/tsserver/fourslashServer/fixExtractToInnerFunctionDuplicaton.js +++ b/tests/baselines/reference/tsserver/fourslashServer/fixExtractToInnerFunctionDuplicaton.js @@ -156,12 +156,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -172,12 +192,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } @@ -246,12 +286,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -262,12 +322,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } @@ -336,12 +416,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -352,12 +452,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } @@ -426,12 +546,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -442,12 +582,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } @@ -516,12 +676,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -532,12 +712,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } @@ -606,12 +806,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to inner function in function 'foo'", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to function in global scope", "name": "function_scope_1", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] }, @@ -622,12 +842,32 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } }, { "description": "Extract to constant in global scope", "name": "constant_scope_1", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 24 + }, + "end": { + "line": 1, + "offset": 41 + } + } } ] } diff --git a/tests/baselines/reference/tsserver/fourslashServer/moveToFile_emptyTargetFile.js b/tests/baselines/reference/tsserver/fourslashServer/moveToFile_emptyTargetFile.js index 0a364963050f6..5414c78b40e24 100644 --- a/tests/baselines/reference/tsserver/fourslashServer/moveToFile_emptyTargetFile.js +++ b/tests/baselines/reference/tsserver/fourslashServer/moveToFile_emptyTargetFile.js @@ -248,7 +248,17 @@ Info seq [hh:mm:ss:mss] response: { "name": "Move to a new file", "description": "Move to a new file", - "kind": "refactor.move.newFile" + "kind": "refactor.move.newFile", + "range": { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 20 + } + } } ] }, @@ -259,7 +269,17 @@ Info seq [hh:mm:ss:mss] response: { "name": "Move to file", "description": "Move to file", - "kind": "refactor.move.file" + "kind": "refactor.move.file", + "range": { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 20 + } + } } ] }, @@ -270,7 +290,17 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to function in module scope", "name": "function_scope_0", - "kind": "refactor.extract.function" + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 1, + "offset": 17 + }, + "end": { + "line": 1, + "offset": 19 + } + } } ] }, @@ -281,7 +311,17 @@ Info seq [hh:mm:ss:mss] response: { "description": "Extract to constant in enclosing scope", "name": "constant_scope_0", - "kind": "refactor.extract.constant" + "kind": "refactor.extract.constant", + "range": { + "start": { + "line": 1, + "offset": 17 + }, + "end": { + "line": 1, + "offset": 19 + } + } } ] } diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports1.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports1.js new file mode 100644 index 0000000000000..c3fd7c5c37059 --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports1.js @@ -0,0 +1,351 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/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 + +//// [/other.ts] +export const t = 1; + +//// [/other2.ts] +export const t2 = 1; + +//// [/other3.ts] +export const t3 = 1; + +//// [/target.ts] +import { t } from "./other"; +import { t3 } from "./other3"; +const a = t + 1; +const b = 10; +const c = 10; + +//// [/tsconfig.json] +{ "files": ["target.ts", "other.ts", "other2.ts", "other3.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/target.ts", + "/other.ts", + "/other2.ts", + "/other3.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other2.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other3.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 (7) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-0 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst b = 10;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + + + 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' + other.ts + Imported via "./other" from file 'target.ts' + Part of 'files' list in tsconfig.json + other3.ts + Imported via "./other3" from file 'target.ts' + Part of 'files' list in tsconfig.json + target.ts + Part of 'files' list in tsconfig.json + other2.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": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (7) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/lib.d.ts: *new* + {"pollingInterval":500} +/lib.decorators.d.ts: *new* + {"pollingInterval":500} +/lib.decorators.legacy.d.ts: *new* + {"pollingInterval":500} +/other.ts: *new* + {"pollingInterval":500} +/other2.ts: *new* + {"pollingInterval":500} +/other3.ts: *new* + {"pollingInterval":500} +/tsconfig.json: *new* + {"pollingInterval":2000} + +Projects:: +/tsconfig.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/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 +/other.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other2.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other3.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "const m = t3 + t2 + 1;" + } + ], + "pastes": [ + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 14 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (7) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-1 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst m = t3 + t2 + 1;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (7) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-2 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst b = 10;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 1 + }, + "newText": "import { t2 } from \"./other2\";\n" + }, + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 14 + }, + "newText": "const m = t3 + t2 + 1;" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/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 +/other.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other2.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other3.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports2.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports2.js new file mode 100644 index 0000000000000..83a2be3d8686b --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_existingImports2.js @@ -0,0 +1,400 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/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 + +//// [/originalFile.ts] +import { t2 } from "./other2"; +import { t3 } from "./other3"; +export const n = 10; +export const m = t3 + t2 + n; + +//// [/other.ts] +export const t = 1; + +//// [/other2.ts] +export const t2 = 1; + +//// [/other3.ts] +export const t3 = 1; + +//// [/target.ts] +import { t } from "./other"; +import { t3 } from "./other3"; +const a = t + 1; +const b = 10; +const c = 10; + +//// [/tsconfig.json] +{ "files": ["target.ts", "originalFile.ts", "other.ts", "other2.ts", "other3.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/target.ts", + "/originalFile.ts", + "/other.ts", + "/other2.ts", + "/other3.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /originalFile.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other2.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other3.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 (8) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-0 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst b = 10;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + /originalFile.ts Text-1 "import { t2 } from \"./other2\";\nimport { t3 } from \"./other3\";\nexport const n = 10;\nexport const m = t3 + t2 + n;" + + + 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' + other.ts + Imported via "./other" from file 'target.ts' + Part of 'files' list in tsconfig.json + other3.ts + Imported via "./other3" from file 'target.ts' + Imported via "./other3" from file 'originalFile.ts' + Part of 'files' list in tsconfig.json + target.ts + Part of 'files' list in tsconfig.json + other2.ts + Imported via "./other2" from file 'originalFile.ts' + Part of 'files' list in tsconfig.json + originalFile.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": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (8) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/lib.d.ts: *new* + {"pollingInterval":500} +/lib.decorators.d.ts: *new* + {"pollingInterval":500} +/lib.decorators.legacy.d.ts: *new* + {"pollingInterval":500} +/originalFile.ts: *new* + {"pollingInterval":500} +/other.ts: *new* + {"pollingInterval":500} +/other2.ts: *new* + {"pollingInterval":500} +/other3.ts: *new* + {"pollingInterval":500} +/tsconfig.json: *new* + {"pollingInterval":2000} + +Projects:: +/tsconfig.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/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 +/originalFile.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other2.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other3.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "const m = t3 + t2 + n;", + "range": { + "file": "originalFile.ts", + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 30 + } + } + } + ], + "pastes": [ + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 14 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (8) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-1 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst m = t3 + t2 + n;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + /originalFile.ts Text-1 "import { t2 } from \"./other2\";\nimport { t3 } from \"./other3\";\nexport const n = 10;\nexport const m = t3 + t2 + n;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] getExportInfoMap: cache miss or empty; calculating new results +Info seq [hh:mm:ss:mss] getExportInfoMap: done in * ms +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (8) + /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 + /other.ts Text-1 "export const t = 1;" + /other3.ts Text-1 "export const t3 = 1;" + /target.ts SVC-1-2 "import { t } from \"./other\";\nimport { t3 } from \"./other3\";\nconst a = t + 1;\nconst b = 10;\nconst c = 10;" + /other2.ts Text-1 "export const t2 = 1;" + /originalFile.ts Text-1 "import { t2 } from \"./other2\";\nimport { t3 } from \"./other3\";\nexport const n = 10;\nexport const m = t3 + t2 + n;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { n } from \"./originalFile\";\n" + }, + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 1 + }, + "newText": "import { t2 } from \"./other2\";\n" + }, + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 14 + }, + "newText": "const m = t3 + t2 + n;" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/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 +/originalFile.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other2.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/other3.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_knownSourceFile.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_knownSourceFile.js new file mode 100644 index 0000000000000..ab094df47d509 --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_knownSourceFile.js @@ -0,0 +1,371 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/file1.ts] +export const b = 2; + +//// [/file2.ts] +import { b } from './file1'; +const a = 1; +const c = a + b; +const t = 9; + +//// [/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 + +//// [/target.ts] +export const tt = 2; +function f(); +const p = 1; + +//// [/tsconfig.json] +{ "files": ["file1.ts", "file2.ts", "target.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/file1.ts", + "/file2.ts", + "/target.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file1.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file2.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 (6) + /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 + /file1.ts Text-1 "export const b = 2;" + /file2.ts Text-1 "import { b } from './file1';\nconst a = 1;\nconst c = a + b;\nconst t = 9;" + /target.ts SVC-1-0 "export const tt = 2;\nfunction f();\nconst p = 1;" + + + 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' + file1.ts + Part of 'files' list in tsconfig.json + Imported via './file1' from file 'file2.ts' + file2.ts + Part of 'files' list in tsconfig.json + 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": "/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (6) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/file1.ts: *new* + {"pollingInterval":500} +/file2.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:: +/file1.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file2.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "const c = a + b;\nconst t = 9;", + "range": { + "file": "file2.ts", + "start": { + "line": 3, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 13 + } + } + } + ], + "pastes": [ + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 14 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (6) + /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 + /file1.ts Text-1 "export const b = 2;" + /file2.ts Text-1 "import { b } from './file1';\nconst a = 1;\nconst c = a + b;\nconst t = 9;" + /target.ts SVC-1-1 "export const tt = 2;\nconst c = a + b;\nconst t = 9;\nconst p = 1;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] getExportInfoMap: cache miss or empty; calculating new results +Info seq [hh:mm:ss:mss] getExportInfoMap: done in * ms +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (6) + /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 + /file1.ts Text-1 "export const b = 2;" + /file2.ts Text-1 "import { b } from './file1';\nconst a = 1;\nconst c = a + b;\nconst t = 9;" + /target.ts SVC-1-2 "export const tt = 2;\nfunction f();\nconst p = 1;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/file2.ts", + "textChanges": [ + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 1 + }, + "newText": "export " + } + ] + }, + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { a } from \"./file2\";\n\n" + }, + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { b } from './file1';\n\n" + }, + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 14 + }, + "newText": "const c = a + b;\nconst t = 9;" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/file1.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file2.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes1.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes1.js new file mode 100644 index 0000000000000..441bd1d77d1be --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes1.js @@ -0,0 +1,353 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/file1.ts] +export const p = 10; +export const q = 12; + +//// [/file3.ts] +export const r = 10; +export const s = 12; + +//// [/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 + +//// [/target.ts] +const a = 1; + +const b = 2; +const c = 3; + +const d = 4; + +//// [/tsconfig.json] +{ "files": ["file1.ts", "target.ts", "file3.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/file1.ts", + "/target.ts", + "/file3.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file1.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file3.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 (6) + /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 + /file1.ts Text-1 "export const p = 10;\nexport const q = 12;" + /target.ts SVC-1-0 "const a = 1;\n\nconst b = 2;\nconst c = 3;\n\nconst d = 4;" + /file3.ts Text-1 "export const r = 10;\nexport const s = 12;" + + + 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' + file1.ts + Part of 'files' list in tsconfig.json + target.ts + Part of 'files' list in tsconfig.json + file3.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": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (6) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/file1.ts: *new* + {"pollingInterval":500} +/file3.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:: +/file1.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file3.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "const g = p + q;\nfunction e();\nconst f = r + s;" + } + ], + "pastes": [ + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 1 + } + }, + { + "start": { + "line": 5, + "offset": 1 + }, + "end": { + "line": 5, + "offset": 1 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (6) + /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 + /file1.ts Text-1 "export const p = 10;\nexport const q = 12;" + /target.ts SVC-1-1 "const a = 1;\nconst g = p + q;\nfunction e();\nconst f = r + s;\nconst b = 2;\nconst c = 3;\n\nconst d = 4;" + /file3.ts Text-1 "export const r = 10;\nexport const s = 12;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (6) + /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 + /file1.ts Text-1 "export const p = 10;\nexport const q = 12;" + /target.ts SVC-1-2 "const a = 1;\n\nconst b = 2;\nconst c = 3;\n\nconst d = 4;" + /file3.ts Text-1 "export const r = 10;\nexport const s = 12;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { p, q } from \"./file1\";\nimport { r, s } from \"./file3\";\n\n" + }, + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 1 + }, + "newText": "const g = p + q;\nfunction e();\nconst f = r + s;" + }, + { + "start": { + "line": 5, + "offset": 1 + }, + "end": { + "line": 5, + "offset": 1 + }, + "newText": "const g = p + q;\nfunction e();\nconst f = r + s;" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/file1.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file3.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes2.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes2.js new file mode 100644 index 0000000000000..45ed3072ffe8e --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_multiplePastes2.js @@ -0,0 +1,381 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/file1.ts] +import { aa, bb } from "./other"; +export const r = 10; +export const s = 12; +export const t = aa + bb + r + s; +const u = 1; + +//// [/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 + +//// [/other.ts] +export const aa = 1; +export const bb = 2; + +//// [/target.ts] +const a = 1; +const b = 2; +const c = 3; + +const d = 4; + +//// [/tsconfig.json] +{ "files": ["file1.ts", "target.ts", "other.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/file1.ts", + "/target.ts", + "/other.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file1.ts 500 undefined WatchType: Closed Script info +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /other.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 (6) + /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 + /other.ts Text-1 "export const aa = 1;\nexport const bb = 2;" + /file1.ts Text-1 "import { aa, bb } from \"./other\";\nexport const r = 10;\nexport const s = 12;\nexport const t = aa + bb + r + s;\nconst u = 1;" + /target.ts SVC-1-0 "const a = 1;\nconst b = 2;\nconst c = 3;\n\nconst d = 4;" + + + 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' + other.ts + Imported via "./other" from file 'file1.ts' + Part of 'files' list in tsconfig.json + file1.ts + Part of 'files' list in tsconfig.json + 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": "/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (6) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/file1.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} +/other.ts: *new* + {"pollingInterval":500} +/tsconfig.json: *new* + {"pollingInterval":2000} + +Projects:: +/tsconfig.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/file1.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/other.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "export const t = aa + bb + r + s;\nconst u = 1;", + "range": { + "file": "file1.ts", + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 5, + "offset": 13 + } + } + } + ], + "pastes": [ + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 13 + } + }, + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 1 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (6) + /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 + /other.ts Text-1 "export const aa = 1;\nexport const bb = 2;" + /file1.ts Text-1 "import { aa, bb } from \"./other\";\nexport const r = 10;\nexport const s = 12;\nexport const t = aa + bb + r + s;\nconst u = 1;" + /target.ts SVC-1-1 "const a = 1;\nexport const t = aa + bb + r + s;\nconst u = 1;\nconst c = 3;\n\nconst d = 4;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] getExportInfoMap: cache miss or empty; calculating new results +Info seq [hh:mm:ss:mss] getExportInfoMap: done in * ms +Info seq [hh:mm:ss:mss] getExportInfoMap: cache hit +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (6) + /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 + /other.ts Text-1 "export const aa = 1;\nexport const bb = 2;" + /file1.ts Text-1 "import { aa, bb } from \"./other\";\nexport const r = 10;\nexport const s = 12;\nexport const t = aa + bb + r + s;\nconst u = 1;" + /target.ts SVC-1-2 "const a = 1;\nconst b = 2;\nconst c = 3;\n\nconst d = 4;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { r, s } from \"./file1\";\n\n" + }, + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { aa, bb } from \"./other\";\n\n" + }, + { + "start": { + "line": 2, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 13 + }, + "newText": "export const t = aa + bb + r + s;\nconst u = 1;" + }, + { + "start": { + "line": 4, + "offset": 1 + }, + "end": { + "line": 4, + "offset": 1 + }, + "newText": "export const t = aa + bb + r + s;\nconst u = 1;" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/file1.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/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 +/other.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_pasteComments.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_pasteComments.js new file mode 100644 index 0000000000000..e4edb335e213e --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_pasteComments.js @@ -0,0 +1,276 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/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 + +//// [/target.ts] +const a = 10; +const b = 10; +const c = 10; + +//// [/tsconfig.json] +{ "files": ["target.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/target.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /target.ts :: Config file name: /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 /target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/target.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +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 (4) + /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 + /target.ts SVC-1-0 "const a = 10;\nconst b = 10;\nconst c = 10;" + + + 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' + 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": "/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/target.ts", + "configFile": "/tsconfig.json", + "diagnostics": [] + } + } +Info seq [hh:mm:ss:mss] Project '/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (4) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/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:: +/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 +/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /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": "/target.ts", + "copies": [ + { + "text": "/**\n* Testing comment line 1\n* line 2\n* line 3\n* line 4\n*/" + } + ], + "pastes": [ + { + "start": { + "line": 2, + "offset": 14 + }, + "end": { + "line": 2, + "offset": 14 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 (4) + /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 + /target.ts SVC-1-1 "const a = 10;\nconst b = 10;/**\n* Testing comment line 1\n* line 2\n* line 3\n* line 4\n*/\nconst c = 10;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 (4) + /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 + /target.ts SVC-1-2 "const a = 10;\nconst b = 10;\nconst c = 10;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/target.ts", + "textChanges": [ + { + "start": { + "line": 2, + "offset": 14 + }, + "end": { + "line": 2, + "offset": 14 + }, + "newText": "/**\n* Testing comment line 1\n* line 2\n* line 3\n* line 4\n*/" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/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 +/target.ts (Open) *changed* + version: SVC-1-2 *changed* + containingProjects: 1 + /tsconfig.json *default* diff --git a/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_unknownSourceFile.js b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_unknownSourceFile.js new file mode 100644 index 0000000000000..2a8374ac6ce0a --- /dev/null +++ b/tests/baselines/reference/tsserver/fourslashServer/postPasteImportFixes_unknownSourceFile.js @@ -0,0 +1,310 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +//// [/file1.ts] +export interface Test1 {} +export interface Test2 {} +export interface Test3 {} +export interface Test4 {} + +//// [/file2.ts] +const a = 10; +const b = 10; +const c = 10; + +//// [/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": ["file1.ts", "file2.ts"] } + + +Info seq [hh:mm:ss:mss] request: + { + "seq": 0, + "type": "request", + "arguments": { + "file": "/file2.ts" + }, + "command": "open" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /file2.ts :: Config file name: /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 /file2.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /tsconfig.json : { + "rootNames": [ + "/file1.ts", + "/file2.ts" + ], + "options": { + "configFilePath": "/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /file1.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 + /file1.ts Text-1 "export interface Test1 {}\nexport interface Test2 {}\nexport interface Test3 {}\nexport interface Test4 {}" + /file2.ts SVC-1-0 "const a = 10;\nconst b = 10;\nconst c = 10;" + + + 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' + file1.ts + Part of 'files' list in tsconfig.json + file2.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": "/file2.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: /file2.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /tsconfig.json +After Request +watchedFiles:: +/file1.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:: +/file1.ts *new* + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file2.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": "/file2.ts", + "copies": [ + { + "text": "interface Testing {\n test1: Test1;\n test2: Test2;\n test3: Test3;\n test4: Test4;\n }" + } + ], + "pastes": [ + { + "start": { + "line": 3, + "offset": 1 + }, + "end": { + "line": 3, + "offset": 1 + } + } + ] + }, + "command": "getPostPasteImportFixes" + } +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 + /file1.ts Text-1 "export interface Test1 {}\nexport interface Test2 {}\nexport interface Test3 {}\nexport interface Test4 {}" + /file2.ts SVC-1-1 "const a = 10;\nconst b = 10;\ninterface Testing {\n test1: Test1;\n test2: Test2;\n test3: Test3;\n test4: Test4;\n }const c = 10;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /tsconfig.json projectStateVersion: 3 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 + /file1.ts Text-1 "export interface Test1 {}\nexport interface Test2 {}\nexport interface Test3 {}\nexport interface Test4 {}" + /file2.ts SVC-1-2 "const a = 10;\nconst b = 10;\nconst c = 10;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "getPostPasteImportFixes", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + }, + "body": { + "edits": [ + { + "fileName": "/file2.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 1, + "offset": 1 + }, + "newText": "import { Test1, Test2, Test3, Test4 } from \"./file1\";\n\n" + }, + { + "start": { + "line": 3, + "offset": 1 + }, + "end": { + "line": 3, + "offset": 1 + }, + "newText": "interface Testing {\n test1: Test1;\n test2: Test2;\n test3: Test3;\n test4: Test4;\n }" + } + ] + } + ] + } + } +After Request +Projects:: +/tsconfig.json (Configured) *changed* + projectStateVersion: 3 *changed* + projectProgramVersion: 1 + +ScriptInfos:: +/file1.ts + version: Text-1 + containingProjects: 1 + /tsconfig.json +/file2.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 diff --git a/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-'move-to-file'-and-'move-to-new-file'-refactors.js b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-'move-to-file'-and-'move-to-new-file'-refactors.js new file mode 100644 index 0000000000000..963e2a025f6b4 --- /dev/null +++ b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-'move-to-file'-and-'move-to-new-file'-refactors.js @@ -0,0 +1,155 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +Before request +//// [/a.ts] +const a = 1; +const b = 1; +function foo() { } + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/a.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found. +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + /a.ts SVC-1-0 "const a = 1;\nconst b = 1;\nfunction foo() { }" + + + a.ts + Root file specified for compilation + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/a/lib/lib.d.ts: *new* + {"pollingInterval":500} + +Projects:: +/dev/null/inferredProject1* (Inferred) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/a.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /dev/null/inferredProject1* *default* + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "configure", + "arguments": { + "preferences": { + "allowTextChangesInNewFiles": true + } + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "seq": 0, + "type": "response", + "command": "configure", + "request_seq": 2, + "success": true, + "performanceData": { + "updateGraphDurationMs": * + } + } +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "getApplicableRefactors", + "arguments": { + "file": "/a.ts", + "startLine": 1, + "startOffset": 3, + "endLine": 2, + "endOffset": 3, + "includeInteractiveActions": true + }, + "seq": 3, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "response": [ + { + "name": "Move to a new file", + "description": "Move to a new file", + "actions": [ + { + "name": "Move to a new file", + "description": "Move to a new file", + "kind": "refactor.move.newFile", + "range": { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 13 + } + } + } + ] + }, + { + "name": "Move to file", + "description": "Move to file", + "actions": [ + { + "name": "Move to file", + "description": "Move to file", + "kind": "refactor.move.file", + "range": { + "start": { + "line": 1, + "offset": 1 + }, + "end": { + "line": 2, + "offset": 13 + } + } + } + ] + } + ], + "responseRequired": true + } +After request diff --git a/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-symbol-refactor.js b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-symbol-refactor.js new file mode 100644 index 0000000000000..ac7ee1c4c179a --- /dev/null +++ b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-symbol-refactor.js @@ -0,0 +1,142 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +Before request +//// [/a.ts] +class Foo { + someMethod(m: number) { + var x = m; + x = x * 3; + var y = 30; + var j = 10; + var z = y + j; + console.log(z); + var q = 10; + return q; + } +} + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/a.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found. +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + /a.ts SVC-1-0 "class Foo {\n someMethod(m: number) {\n var x = m;\n x = x * 3;\n var y = 30;\n var j = 10;\n var z = y + j;\n console.log(z);\n var q = 10;\n return q;\n }\n}" + + + a.ts + Root file specified for compilation + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/a/lib/lib.d.ts: *new* + {"pollingInterval":500} + +Projects:: +/dev/null/inferredProject1* (Inferred) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/a.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /dev/null/inferredProject1* *default* + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "getApplicableRefactors", + "arguments": { + "file": "/a.ts", + "startLine": 3, + "startOffset": 9, + "endLine": 5, + "endOffset": 20 + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "response": [ + { + "name": "Extract Symbol", + "description": "Extract function", + "actions": [ + { + "description": "Extract to inner function in method 'someMethod'", + "name": "function_scope_0", + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 3, + "offset": 9 + }, + "end": { + "line": 5, + "offset": 20 + } + } + }, + { + "description": "Extract to method in class 'Foo'", + "name": "function_scope_1", + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 3, + "offset": 9 + }, + "end": { + "line": 5, + "offset": 20 + } + } + }, + { + "description": "Extract to function in global scope", + "name": "function_scope_2", + "kind": "refactor.extract.function", + "range": { + "start": { + "line": 3, + "offset": 9 + }, + "end": { + "line": 5, + "offset": 20 + } + } + } + ] + } + ], + "responseRequired": true + } +After request diff --git a/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-type-refactor.js b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-type-refactor.js new file mode 100644 index 0000000000000..7fb510f79d0f5 --- /dev/null +++ b/tests/baselines/reference/tsserver/getApplicableRefactors/returns-the-affected-range-of-text-for-extract-type-refactor.js @@ -0,0 +1,101 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +Before request +//// [/a.ts] +type A<B, C, D = B> = Partial<C | string | D> & D | C; + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/a.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: / +Info seq [hh:mm:ss:mss] For info: /a.ts :: No config files found. +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /dev/null/inferredProject1* WatchType: Missing file +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /dev/null/inferredProject1* projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + /a.ts SVC-1-0 "type A<B, C, D = B> = Partial<C | string | D> & D | C;" + + + a.ts + Root file specified for compilation + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Project '/dev/null/inferredProject1*' (Inferred) +Info seq [hh:mm:ss:mss] Files (1) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /a.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /dev/null/inferredProject1* +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/a/lib/lib.d.ts: *new* + {"pollingInterval":500} + +Projects:: +/dev/null/inferredProject1* (Inferred) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/a.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /dev/null/inferredProject1* *default* + +Before request + +Info seq [hh:mm:ss:mss] request: + { + "command": "getApplicableRefactors", + "arguments": { + "file": "/a.ts", + "startLine": 1, + "startOffset": 26, + "endLine": 1, + "endOffset": 38 + }, + "seq": 2, + "type": "request" + } +Info seq [hh:mm:ss:mss] response: + { + "response": [ + { + "name": "Extract type", + "description": "Extract type", + "actions": [ + { + "name": "Extract to type alias", + "description": "Extract to type alias", + "kind": "refactor.extract.type", + "range": { + "start": { + "line": 1, + "offset": 22 + }, + "end": { + "line": 1, + "offset": 46 + } + } + } + ] + } + ], + "responseRequired": true + } +After request diff --git a/tests/baselines/reference/tsserver/getPostPasteImportFixes/Returns-the-same-file-unchanged-.js b/tests/baselines/reference/tsserver/getPostPasteImportFixes/Returns-the-same-file-unchanged-.js new file mode 100644 index 0000000000000..cd4e9f43ba699 --- /dev/null +++ b/tests/baselines/reference/tsserver/getPostPasteImportFixes/Returns-the-same-file-unchanged-.js @@ -0,0 +1,214 @@ +currentDirectory:: / useCaseSensitiveFileNames: false +Info seq [hh:mm:ss:mss] Provided types map file "/typesMap.json" doesn't exist +Before request +//// [/project/a/target.ts] +const a = 1; +const b = 2; +const c = 3; + +//// [/project/tsconfig.json] +{} + + +Info seq [hh:mm:ss:mss] request: + { + "command": "open", + "arguments": { + "file": "/project/a/target.ts" + }, + "seq": 1, + "type": "request" + } +Info seq [hh:mm:ss:mss] Search path: /project/a +Info seq [hh:mm:ss:mss] For info: /project/a/target.ts :: Config file name: /project/tsconfig.json +Info seq [hh:mm:ss:mss] Creating configuration project /project/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /project/tsconfig.json 2000 undefined Project: /project/tsconfig.json WatchType: Config file +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "projectLoadingStart", + "body": { + "projectName": "/project/tsconfig.json", + "reason": "Creating possible configured project for /project/a/target.ts to open" + } + } +Info seq [hh:mm:ss:mss] Config: /project/tsconfig.json : { + "rootNames": [ + "/project/a/target.ts" + ], + "options": { + "configFilePath": "/project/tsconfig.json" + } +} +Info seq [hh:mm:ss:mss] DirectoryWatcher:: Added:: WatchInfo: /project 1 undefined Config: /project/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /project 1 undefined Config: /project/tsconfig.json WatchType: Wild card directory +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /project/tsconfig.json +Info seq [hh:mm:ss:mss] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined Project: /project/tsconfig.json WatchType: Missing file +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /project/tsconfig.json projectStateVersion: 1 projectProgramVersion: 0 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (1) + /project/a/target.ts SVC-1-0 "const a = 1;\nconst b = 2;\nconst c = 3;" + + + a/target.ts + Matched by default include pattern '**/*' + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "projectLoadingFinish", + "body": { + "projectName": "/project/tsconfig.json" + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "telemetry", + "body": { + "telemetryEventName": "projectInfo", + "payload": { + "projectId": "4877b582a3e7d5836ee46b4a0db488dc715db827bacc8ef1fbcd0dbb78ae23d1", + "fileStats": { + "js": 0, + "jsSize": 0, + "jsx": 0, + "jsxSize": 0, + "ts": 1, + "tsSize": 38, + "tsx": 0, + "tsxSize": 0, + "dts": 0, + "dtsSize": 0, + "deferred": 0, + "deferredSize": 0 + }, + "compilerOptions": {}, + "typeAcquisition": { + "enable": false, + "include": false, + "exclude": false + }, + "extends": false, + "files": false, + "include": false, + "exclude": false, + "compileOnSave": false, + "configFileName": "tsconfig.json", + "projectType": "configured", + "languageServiceEnabled": true, + "version": "FakeVersion" + } + } + } +Info seq [hh:mm:ss:mss] event: + { + "seq": 0, + "type": "event", + "event": "configFileDiag", + "body": { + "triggerFile": "/project/a/target.ts", + "configFile": "/project/tsconfig.json", + "diagnostics": [ + { + "text": "File '/a/lib/lib.d.ts' not found.\n The file is in the program because:\n Default library for target 'es5'", + "code": 6053, + "category": "error" + }, + { + "text": "Cannot find global type 'Array'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'Boolean'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'Function'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'IArguments'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'Number'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'Object'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'RegExp'.", + "code": 2318, + "category": "error" + }, + { + "text": "Cannot find global type 'String'.", + "code": 2318, + "category": "error" + } + ] + } + } +Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (1) + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Open files: +Info seq [hh:mm:ss:mss] FileName: /project/a/target.ts ProjectRootPath: undefined +Info seq [hh:mm:ss:mss] Projects: /project/tsconfig.json +Info seq [hh:mm:ss:mss] response: + { + "responseRequired": false + } +After request + +PolledWatches:: +/a/lib/lib.d.ts: *new* + {"pollingInterval":500} + +FsWatches:: +/project/tsconfig.json: *new* + {} + +FsWatchesRecursive:: +/project: *new* + {} + +Projects:: +/project/tsconfig.json (Configured) *new* + projectStateVersion: 1 + projectProgramVersion: 1 + +ScriptInfos:: +/project/a/target.ts (Open) *new* + version: SVC-1-0 + containingProjects: 1 + /project/tsconfig.json *default* + +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /project/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /project/tsconfig.json projectStateVersion: 2 projectProgramVersion: 1 structureChanged: false structureIsReused:: Completely Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (1) + /project/a/target.ts SVC-1-1 "const a = 1;\nfunction e();\nconst f = r + s;\nconst b = 2;\nconst c = 3;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- +Info seq [hh:mm:ss:mss] Starting updateGraphWorker: Project: /project/tsconfig.json +Info seq [hh:mm:ss:mss] Finishing updateGraphWorker: Project: /project/tsconfig.json projectStateVersion: 3 projectProgramVersion: 1 structureChanged: false structureIsReused:: Completely Elapsed:: *ms +Info seq [hh:mm:ss:mss] Project '/project/tsconfig.json' (Configured) +Info seq [hh:mm:ss:mss] Files (1) + /project/a/target.ts SVC-1-2 "const a = 1;\nconst b = 2;\nconst c = 3;" + +Info seq [hh:mm:ss:mss] ----------------------------------------------- \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index ff8fad9abff01..7074be0b2c88c 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -444,6 +444,11 @@ declare namespace FourSlashInterface { toggleMultilineComment(newFileContent: string): void; commentSelection(newFileContent: string): void; uncommentSelection(newFileContent: string): void; + postPasteImportFixes(options: { + newFileContents: { readonly [fileName: string]: string }; + copies: { text: string; copyRange?: { file: string; range: { pos: number, end: number };} }[], + pastes: { pos: number, end: number }[], + }): void; } class edit { caretPosition(): Marker; diff --git a/tests/cases/fourslash/server/postPasteImportFixes_existingImports1.ts b/tests/cases/fourslash/server/postPasteImportFixes_existingImports1.ts new file mode 100644 index 0000000000000..552829ebe6ae3 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_existingImports1.ts @@ -0,0 +1,36 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// import { t } from "./other"; +//// import { t3 } from "./other3"; +//// const a = t + 1; +//// [|const b = 10;|] +//// const c = 10; + +// @Filename: /other.ts +//// export const t = 1; + +// @Filename: /other2.ts +//// export const t2 = 1; + +// @Filename: /other3.ts +//// export const t3 = 1; + +// @Filename: /tsconfig.json +////{ "files": ["target.ts", "other.ts", "other2.ts", "other3.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ text: `const m = t3 + t2 + 1;`}], + pastes: [{ pos: range[0].pos, end: range[0].end }], + newFileContents: { + "/target.ts": +`import { t } from "./other"; +import { t2 } from "./other2"; +import { t3 } from "./other3"; +const a = t + 1; +const m = t3 + t2 + 1; +const c = 10;` + } +}); diff --git a/tests/cases/fourslash/server/postPasteImportFixes_existingImports2.ts b/tests/cases/fourslash/server/postPasteImportFixes_existingImports2.ts new file mode 100644 index 0000000000000..6b991fbdc6904 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_existingImports2.ts @@ -0,0 +1,43 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// import { t } from "./other"; +//// import { t3 } from "./other3"; +//// const a = t + 1; +//// [|const b = 10;|] +//// const c = 10; + +// @Filename: /other.ts +//// export const t = 1; + +// @Filename: /other2.ts +//// export const t2 = 1; + +// @Filename: /other3.ts +//// export const t3 = 1; + +// @Filename: /originalFile.ts +//// import { t2 } from "./other2"; +//// import { t3 } from "./other3"; +//// export const n = 10; +//// [|export const m = t3 + t2 + n;|] + +// @Filename: /tsconfig.json +////{ "files": ["target.ts", "originalFile.ts", "other.ts", "other2.ts", "other3.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ text: `const m = t3 + t2 + n;`, copyRange: { file: "originalFile.ts", range: range[1]}}], + pastes: [range[0]], + newFileContents: { + "/target.ts": +`import { n } from "./originalFile"; +import { t } from "./other"; +import { t2 } from "./other2"; +import { t3 } from "./other3"; +const a = t + 1; +const m = t3 + t2 + n; +const c = 10;` + } +}); diff --git a/tests/cases/fourslash/server/postPasteImportFixes_knownSourceFile.ts b/tests/cases/fourslash/server/postPasteImportFixes_knownSourceFile.ts new file mode 100644 index 0000000000000..a7135d64e53ac --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_knownSourceFile.ts @@ -0,0 +1,47 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// export const tt = 2; +//// [|function f();|] +//// const p = 1; + +// @Filename: /file1.ts +////export const b = 2; + +// @Filename: /file2.ts +////import { b } from './file1'; +////const a = 1; +////[|const c = a + b; +////const t = 9;|] + +// @Filename: /tsconfig.json +////{ "files": ["file1.ts", "file2.ts", "target.ts"] } + +const range = test.ranges(); +const t = range[0]; +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ + text: `const c = a + b; +const t = 9;`, + copyRange: { file: "file2.ts", range: range[1]} + }], + pastes: [range[0]], + newFileContents: { + "/file2.ts": +`import { b } from './file1'; +export const a = 1; +const c = a + b; +const t = 9;`, + + "/target.ts": +`import { a } from "./file2"; + +import { b } from './file1'; + +export const tt = 2; +const c = a + b; +const t = 9; +const p = 1;`, + } +}); diff --git a/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes1.ts b/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes1.ts new file mode 100644 index 0000000000000..ff1c5bf8e5d92 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes1.ts @@ -0,0 +1,47 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// const a = 1; +//// [||] +//// const b = 2; +//// const c = 3; +//// [||] +//// const d = 4; + +// @Filename: /file1.ts +//// export const p = 10; +//// export const q = 12; + +// @Filename: /file3.ts +//// export const r = 10; +//// export const s = 12; + +// @Filename: /tsconfig.json +////{ "files": ["file1.ts", "target.ts", "file3.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ + text: `const g = p + q; +function e(); +const f = r + s;`, + }], + pastes: [range[0], range[1]], + newFileContents: { + "/target.ts": +`import { p, q } from "./file1"; +import { r, s } from "./file3"; + +const a = 1; +const g = p + q; +function e(); +const f = r + s; +const b = 2; +const c = 3; +const g = p + q; +function e(); +const f = r + s; +const d = 4;` + } +}); \ No newline at end of file diff --git a/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes2.ts b/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes2.ts new file mode 100644 index 0000000000000..b7838b907fc80 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_multiplePastes2.ts @@ -0,0 +1,47 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// const a = 1; +//// [|const b = 2;|] +//// const c = 3; +//// [||] +//// const d = 4; + +// @Filename: /file1.ts +//// import { aa, bb } from "./other"; +//// export const r = 10; +//// export const s = 12; +//// [|export const t = aa + bb + r + s; +//// const u = 1;|] + +// @Filename: /other.ts +//// export const aa = 1; +//// export const bb = 2; + +// @Filename: /tsconfig.json +////{ "files": ["file1.ts", "target.ts", "other.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ + text: `export const t = aa + bb + r + s; +const u = 1;`, + copyRange: { file: "file1.ts", range: range[2]} + }], + pastes: [range[0], range[1]], + newFileContents: { + "/target.ts": +`import { r, s } from "./file1"; + +import { aa, bb } from "./other"; + +const a = 1; +export const t = aa + bb + r + s; +const u = 1; +const c = 3; +export const t = aa + bb + r + s; +const u = 1; +const d = 4;` + }, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/server/postPasteImportFixes_pasteComments.ts b/tests/cases/fourslash/server/postPasteImportFixes_pasteComments.ts new file mode 100644 index 0000000000000..71c80fb71acb7 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_pasteComments.ts @@ -0,0 +1,34 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /target.ts +//// const a = 10; +//// const b = 10;[||] +//// const c = 10; + +// @Filename: /tsconfig.json +////{ "files": ["target.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ + text: `/** +* Testing comment line 1 +* line 2 +* line 3 +* line 4 +*/` + }], + pastes: [range[0]], + newFileContents: { + "/target.ts": +`const a = 10; +const b = 10;/** +* Testing comment line 1 +* line 2 +* line 3 +* line 4 +*/ +const c = 10;` + } +}); \ No newline at end of file diff --git a/tests/cases/fourslash/server/postPasteImportFixes_unknownSourceFile.ts b/tests/cases/fourslash/server/postPasteImportFixes_unknownSourceFile.ts new file mode 100644 index 0000000000000..0da558ec4c045 --- /dev/null +++ b/tests/cases/fourslash/server/postPasteImportFixes_unknownSourceFile.ts @@ -0,0 +1,42 @@ +/// <reference path="../fourslash.ts" /> + +// @Filename: /file2.ts +//// const a = 10; +//// const b = 10; +//// [||]const c = 10; + +// @Filename: /file1.ts +//// export interface Test1 {} +//// export interface Test2 {} +//// export interface Test3 {} +//// export interface Test4 {} + +// @Filename: /tsconfig.json +////{ "files": ["file1.ts", "file2.ts"] } + +const range = test.ranges(); +format.setOption("insertSpaceAfterSemicolonInForStatements", true); +verify.postPasteImportFixes({ + copies: [{ + text: `interface Testing { + test1: Test1; + test2: Test2; + test3: Test3; + test4: Test4; + }`, + }], + pastes: [range[0]], + newFileContents: { + "/file2.ts": +`import { Test1, Test2, Test3, Test4 } from "./file1"; + +const a = 10; +const b = 10; +interface Testing { + test1: Test1; + test2: Test2; + test3: Test3; + test4: Test4; + }const c = 10;` + } +}); \ No newline at end of file