diff --git a/src/compiler/moduleSpecifiers.ts b/src/compiler/moduleSpecifiers.ts index 3ab03ecbbfcb8..349cabd6da615 100644 --- a/src/compiler/moduleSpecifiers.ts +++ b/src/compiler/moduleSpecifiers.ts @@ -2,13 +2,23 @@ /* @internal */ namespace ts.moduleSpecifiers { export interface ModuleSpecifierPreferences { - importModuleSpecifierPreference?: "relative" | "non-relative"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } - // Note: fromSourceFile is just for usesJsExtensionOnImports - export function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences: ModuleSpecifierPreferences = {}) { - const info = getInfo(compilerOptions, fromSourceFile, fromSourceFileName, host); - return getGlobalModuleSpecifier(toFileName, info, host, compilerOptions) || + // Note: importingSourceFile is just for usesJsExtensionOnImports + export function getModuleSpecifier( + compilerOptions: CompilerOptions, + importingSourceFile: SourceFile, + importingSourceFileName: string, + toFileName: string, + host: ModuleSpecifierResolutionHost, + files: ReadonlyArray, + preferences: ModuleSpecifierPreferences = {}, + ): string { + const info = getInfo(compilerOptions, importingSourceFile, importingSourceFileName, host); + const modulePaths = getAllModulePaths(files, toFileName, info.getCanonicalFileName, host); + return firstDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)) || + getGlobalModuleSpecifier(toFileName, info, host, compilerOptions) || first(getLocalModuleSpecifiers(toFileName, info, compilerOptions, preferences)); } @@ -28,7 +38,7 @@ namespace ts.moduleSpecifiers { if (!files) { return Debug.fail("Files list must be present to resolve symlinks in specifier resolution"); } - const modulePaths = getAllModulePaths(files, getSourceFileOfNode(moduleSymbol.valueDeclaration), info.getCanonicalFileName, host); + const modulePaths = getAllModulePaths(files, getSourceFileOfNode(moduleSymbol.valueDeclaration).fileName, info.getCanonicalFileName, host); const global = mapDefined(modulePaths, moduleFileName => getGlobalModuleSpecifier(moduleFileName, info, host, compilerOptions)); return global.length ? global.map(g => [g]) : modulePaths.map(moduleFileName => @@ -177,14 +187,14 @@ namespace ts.moduleSpecifiers { } /** - * Looks for a existing imports that use symlinks to this module. + * Looks for existing imports that use symlinks to this module. * Only if no symlink is available, the real path will be used. */ - function getAllModulePaths(files: ReadonlyArray, { fileName }: SourceFile, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost): ReadonlyArray { + function getAllModulePaths(files: ReadonlyArray, importedFileName: string, getCanonicalFileName: (file: string) => string, host: ModuleSpecifierResolutionHost): ReadonlyArray { const symlinks = mapDefined(files, sf => sf.resolvedModules && firstDefinedIterator(sf.resolvedModules.values(), res => - res && res.resolvedFileName === fileName ? res.originalPath : undefined)); - return symlinks.length === 0 ? getAllModulePathsUsingIndirectSymlinks(files, getNormalizedAbsolutePath(fileName, host.getCurrentDirectory ? host.getCurrentDirectory() : ""), getCanonicalFileName, host) : symlinks; + res && res.resolvedFileName === importedFileName ? res.originalPath : undefined)); + return symlinks.length === 0 ? getAllModulePathsUsingIndirectSymlinks(files, getNormalizedAbsolutePath(importedFileName, host.getCurrentDirectory ? host.getCurrentDirectory() : ""), getCanonicalFileName, host) : symlinks; } function getRelativePathNParents(relativePath: string): number { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index d2b5368b62337..1785af730e83a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -391,8 +391,11 @@ namespace FourSlash { } private getFileContent(fileName: string): string { - const script = this.languageServiceAdapterHost.getScriptInfo(fileName)!; - return script.content; + return ts.Debug.assertDefined(this.tryGetFileContent(fileName)); + } + private tryGetFileContent(fileName: string): string | undefined { + const script = this.languageServiceAdapterHost.getScriptInfo(fileName); + return script && script.content; } // Entry points from fourslash.ts @@ -1962,7 +1965,7 @@ Actual: ${stringify(fullActual)}`); * @returns The number of characters added to the file as a result of the edits. * May be negative. */ - private applyEdits(fileName: string, edits: ts.TextChange[], isFormattingEdit: boolean): number { + private applyEdits(fileName: string, edits: ReadonlyArray, isFormattingEdit: boolean): number { // We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track // of the incremental offset from each edit to the next. We assume these edit ranges don't overlap @@ -3156,30 +3159,34 @@ Actual: ${stringify(fullActual)}`); assert(action.name === "Move to a new file" && action.description === "Move to a new file"); const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactor.name, action.name, options.preferences || ts.defaultPreferences)!; - this.testNewFileContents(editInfo.edits, options.newFileContents); + this.testNewFileContents(editInfo.edits, options.newFileContents, "move to new file"); } - private testNewFileContents(edits: ReadonlyArray, newFileContents: { [fileName: string]: string }): void { - for (const edit of edits) { - const newContent = newFileContents[edit.fileName]; + private testNewFileContents(edits: ReadonlyArray, newFileContents: { [fileName: string]: string }, description: string): void { + for (const { fileName, textChanges } of edits) { + const newContent = newFileContents[fileName]; if (newContent === undefined) { - this.raiseError(`There was an edit in ${edit.fileName} but new content was not specified.`); + this.raiseError(`${description} - There was an edit in ${fileName} but new content was not specified.`); } - if (this.testData.files.some(f => f.fileName === edit.fileName)) { - this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false); - this.openFile(edit.fileName); - this.verifyCurrentFileContent(newContent); + + const fileContent = this.tryGetFileContent(fileName); + if (fileContent !== undefined) { + const actualNewContent = ts.textChanges.applyChanges(fileContent, textChanges); + assert.equal(actualNewContent, newContent, `new content for ${fileName}`); } else { - assert(edit.textChanges.length === 1); - const change = ts.first(edit.textChanges); + // Creates a new file. + assert(textChanges.length === 1); + const change = ts.first(textChanges); assert.deepEqual(change.span, ts.createTextSpan(0, 0)); - assert.equal(change.newText, newContent, `Content for ${edit.fileName}`); + assert.equal(change.newText, newContent, `${description} - Content for ${fileName}`); } } for (const fileName in newFileContents) { - assert(edits.some(e => e.fileName === fileName)); + if (!edits.some(e => e.fileName === fileName)) { + ts.Debug.fail(`${description} - Asserted new contents of ${fileName} but there were no edits`); + } } } @@ -3314,7 +3321,7 @@ Actual: ${stringify(fullActual)}`); eq(item.replacementSpan, options && options.replacementSpan && ts.createTextSpanFromRange(options.replacementSpan), "replacementSpan"); } - private findFile(indexOrName: string | number) { + private findFile(indexOrName: string | number): FourSlashFile { if (typeof indexOrName === "number") { const index = indexOrName; if (index >= this.testData.files.length) { @@ -3325,32 +3332,39 @@ Actual: ${stringify(fullActual)}`); } } else if (ts.isString(indexOrName)) { - let name = ts.normalizePath(indexOrName); - - // names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName - name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name; - - const availableNames: string[] = []; - const result = ts.forEach(this.testData.files, file => { - const fn = ts.normalizePath(file.fileName); - if (fn) { - if (fn === name) { - return file; - } - availableNames.push(fn); - } - }); - - if (!result) { - throw new Error(`No test file named "${name}" exists. Available file names are: ${availableNames.join(", ")}`); + const { file, availableNames } = this.tryFindFileWorker(indexOrName); + if (!file) { + throw new Error(`No test file named "${indexOrName}" exists. Available file names are: ${availableNames.join(", ")}`); } - return result; + return file; } else { return ts.Debug.assertNever(indexOrName); } } + private tryFindFileWorker(name: string): { readonly file: FourSlashFile | undefined; readonly availableNames: ReadonlyArray; } { + name = ts.normalizePath(name); + // names are stored in the compiler with this relative path, this allows people to use goTo.file on just the fileName + name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name; + + const availableNames: string[] = []; + const file = ts.forEach(this.testData.files, file => { + const fn = ts.normalizePath(file.fileName); + if (fn) { + if (fn === name) { + return file; + } + availableNames.push(fn); + } + }); + return { file, availableNames }; + } + + private hasFile(name: string): boolean { + return this.tryFindFileWorker(name).file !== undefined; + } + private getLineColStringAtPosition(position: number) { const pos = this.languageServiceAdapterHost.positionToLineAndCharacter(this.activeFile.fileName, position); return `line ${(pos.line + 1)}, col ${pos.character}`; @@ -3388,9 +3402,20 @@ Actual: ${stringify(fullActual)}`); return !!a && !!b && a.start === b.start && a.length === b.length; } - public getEditsForFileRename(options: FourSlashInterface.GetEditsForFileRenameOptions): void { - const changes = this.languageService.getEditsForFileRename(options.oldPath, options.newPath, this.formatCodeSettings, ts.defaultPreferences); - this.testNewFileContents(changes, options.newFileContents); + public getEditsForFileRename({ oldPath, newPath, newFileContents }: FourSlashInterface.GetEditsForFileRenameOptions): void { + const test = (fileContents: { readonly [fileName: string]: string }, description: string): void => { + const changes = this.languageService.getEditsForFileRename(oldPath, newPath, this.formatCodeSettings, ts.defaultPreferences); + this.testNewFileContents(changes, fileContents, description); + }; + + ts.Debug.assert(!this.hasFile(newPath), "initially, newPath should not exist"); + + test(newFileContents, "with file not yet moved"); + + this.languageServiceAdapterHost.renameFileOrDirectory(oldPath, newPath); + this.languageService.cleanupSemanticCache(); + const pathUpdater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false)); + test(renameKeys(newFileContents, key => pathUpdater(key) || key), "with file moved"); } private getApplicableRefactors(positionOrRange: number | ts.TextRange, preferences = ts.defaultPreferences): ReadonlyArray { @@ -3398,6 +3423,14 @@ Actual: ${stringify(fullActual)}`); } } + function renameKeys(obj: { readonly [key: string]: T }, renameKey: (key: string) => string): { readonly [key: string]: T } { + const res: { [key: string]: T } = {}; + for (const key in obj) { + res[renameKey(key)] = obj[key]; + } + return res; + } + export function runFourSlashTest(basePath: string, testType: FourSlashTestType, fileName: string) { const content = Harness.IO.readFile(fileName)!; runFourSlashTestContent(basePath, testType, content, fileName); diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index bb476ee699ae3..e6b16efe6c7ca 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -151,6 +151,21 @@ namespace Harness.LanguageService { this.scriptInfos.set(vpath.resolve(this.vfs.cwd(), fileName), new ScriptInfo(fileName, content, isRootFile)); } + public renameFileOrDirectory(oldPath: string, newPath: string): void { + this.vfs.mkdirpSync(ts.getDirectoryPath(newPath)); + this.vfs.renameSync(oldPath, newPath); + + const updater = ts.getPathUpdater(oldPath, newPath, ts.createGetCanonicalFileName(/*useCaseSensitiveFileNames*/ false)); + this.scriptInfos.forEach((scriptInfo, key) => { + const newFileName = updater(key); + if (newFileName !== undefined) { + this.scriptInfos.delete(key); + this.scriptInfos.set(newFileName, scriptInfo); + scriptInfo.fileName = newFileName; + } + }); + } + public editScript(fileName: string, start: number, end: number, newText: string) { const script = this.getScriptInfo(fileName); if (script) { diff --git a/src/server/session.ts b/src/server/session.ts index 550575ca2e8db..2ad7c602d8647 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -129,13 +129,8 @@ namespace ts.server { project: Project; } - function allEditsBeforePos(edits: TextChange[], pos: number) { - for (const edit of edits) { - if (textSpanEnd(edit.span) >= pos) { - return false; - } - } - return true; + function allEditsBeforePos(edits: ReadonlyArray, pos: number): boolean { + return edits.every(edit => textSpanEnd(edit.span) < pos); } // CommandNames used to be exposed before TS 2.4 as a namespace diff --git a/src/services/getEditsForFileRename.ts b/src/services/getEditsForFileRename.ts index c7fd5313022c7..43a0589536ca3 100644 --- a/src/services/getEditsForFileRename.ts +++ b/src/services/getEditsForFileRename.ts @@ -13,7 +13,8 @@ namespace ts { /** If 'path' refers to an old directory, returns path in the new directory. */ type PathUpdater = (path: string) => string | undefined; - function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater { + // exported for tests + export function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater { const canonicalOldPath = getCanonicalFileName(oldFileOrDirPath); return path => { if (getCanonicalFileName(path) === canonicalOldPath) return newFileOrDirPath; @@ -101,7 +102,8 @@ namespace ts { getCanonicalFileName: GetCanonicalFileName, preferences: UserPreferences, ): void { - for (const sourceFile of program.getSourceFiles()) { + const allFiles = program.getSourceFiles(); + for (const sourceFile of allFiles) { const newFromOld = oldToNew(sourceFile.fileName); const newImportFromPath = newFromOld !== undefined ? newFromOld : sourceFile.fileName; const newImportFromDirectory = getDirectoryPath(newImportFromPath); @@ -120,15 +122,19 @@ namespace ts { return newAbsolute === undefined ? undefined : ensurePathIsNonModuleName(getRelativePathFromDirectory(newImportFromDirectory, newAbsolute, getCanonicalFileName)); }, importLiteral => { + const importedModuleSymbol = program.getTypeChecker().getSymbolAtLocation(importLiteral); + // No need to update if it's an ambient module^M + if (importedModuleSymbol && importedModuleSymbol.declarations.some(d => isAmbientModule(d))) return undefined; + const toImport = oldFromNew !== undefined // If we're at the new location (file was already renamed), need to redo module resolution starting from the old location. // TODO:GH#18217 ? getSourceFileToImportFromResolved(resolveModuleName(importLiteral.text, oldImportFromPath, program.getCompilerOptions(), host as ModuleResolutionHost), oldToNew, program) - : getSourceFileToImport(importLiteral, sourceFile, program, host, oldToNew); + : getSourceFileToImport(importedModuleSymbol, importLiteral, sourceFile, program, host, oldToNew); // Need an update if the imported file moved, or the importing file moved and was using a relative path. return toImport !== undefined && (toImport.updated || (importingSourceFileMoved && pathIsRelative(importLiteral.text))) - ? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, preferences) + ? moduleSpecifiers.getModuleSpecifier(program.getCompilerOptions(), sourceFile, newImportFromPath, toImport.newFileName, host, allFiles, preferences) : undefined; }); } @@ -146,11 +152,17 @@ namespace ts { /** True if the imported file was renamed. */ readonly updated: boolean; } - function getSourceFileToImport(importLiteral: StringLiteralLike, importingSourceFile: SourceFile, program: Program, host: LanguageServiceHost, oldToNew: PathUpdater): ToImport | undefined { - const symbol = program.getTypeChecker().getSymbolAtLocation(importLiteral); - if (symbol) { - if (symbol.declarations.some(d => isAmbientModule(d))) return undefined; // No need to update if it's an ambient module - const oldFileName = find(symbol.declarations, isSourceFile)!.fileName; + function getSourceFileToImport( + importedModuleSymbol: Symbol | undefined, + importLiteral: StringLiteralLike, + importingSourceFile: SourceFile, + program: Program, + host: LanguageServiceHost, + oldToNew: PathUpdater, + ): ToImport | undefined { + if (importedModuleSymbol) { + // `find` should succeed because we checked for ambient modules before calling this function. + const oldFileName = find(importedModuleSymbol.declarations, isSourceFile)!.fileName; const newFileName = oldToNew(oldFileName); return newFileName === undefined ? { newFileName: oldFileName, updated: false } : { newFileName, updated: true }; } diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 3001f5cf44e3d..4f8de83969b57 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -803,10 +803,10 @@ namespace ts.textChanges { } } - export function applyChanges(text: string, changes: TextChange[]): string { + export function applyChanges(text: string, changes: ReadonlyArray): string { for (let i = changes.length - 1; i >= 0; i--) { - const change = changes[i]; - text = `${text.substring(0, change.span.start)}${change.newText}${text.substring(textSpanEnd(change.span))}`; + const { span, newText } = changes[i]; + text = `${text.substring(0, span.start)}${newText}${text.substring(textSpanEnd(span))}`; } return text; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 489cf717c13b1..1beea4399c593 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9254,9 +9254,9 @@ declare namespace ts { } declare namespace ts.moduleSpecifiers { interface ModuleSpecifierPreferences { - importModuleSpecifierPreference?: "relative" | "non-relative"; + readonly importModuleSpecifierPreference?: "relative" | "non-relative"; } - function getModuleSpecifier(compilerOptions: CompilerOptions, fromSourceFile: SourceFile, fromSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, preferences?: ModuleSpecifierPreferences): string; + function getModuleSpecifier(compilerOptions: CompilerOptions, importingSourceFile: SourceFile, importingSourceFileName: string, toFileName: string, host: ModuleSpecifierResolutionHost, files: ReadonlyArray, preferences?: ModuleSpecifierPreferences): string; function getModuleSpecifiers(moduleSymbol: Symbol, compilerOptions: CompilerOptions, importingSourceFile: SourceFile, host: ModuleSpecifierResolutionHost, files: ReadonlyArray, preferences: ModuleSpecifierPreferences): ReadonlyArray>; } declare namespace ts { @@ -11107,6 +11107,9 @@ declare namespace ts.FindAllReferences.Core { } declare namespace ts { function getEditsForFileRename(program: Program, oldFileOrDirPath: string, newFileOrDirPath: string, host: LanguageServiceHost, formatContext: formatting.FormatContext, preferences: UserPreferences): ReadonlyArray; + /** If 'path' refers to an old directory, returns path in the new directory. */ + type PathUpdater = (path: string) => string | undefined; + function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater; } declare namespace ts.GoToDefinition { function getDefinitionAtPosition(program: Program, sourceFile: SourceFile, position: number): DefinitionInfo[] | undefined; @@ -11533,7 +11536,7 @@ declare namespace ts.textChanges { createNewFile(oldFile: SourceFile, fileName: string, statements: ReadonlyArray): void; } type ValidateNonFormattedText = (node: Node, text: string) => void; - function applyChanges(text: string, changes: TextChange[]): string; + function applyChanges(text: string, changes: ReadonlyArray): string; function isValidLocationToAddComment(sourceFile: SourceFile, position: number): boolean; } declare namespace ts { diff --git a/tests/cases/fourslash/getEditsForFileRename.ts b/tests/cases/fourslash/getEditsForFileRename.ts index 6cd7f9bb046bf..5276f6f918dae 100644 --- a/tests/cases/fourslash/getEditsForFileRename.ts +++ b/tests/cases/fourslash/getEditsForFileRename.ts @@ -1,7 +1,5 @@ /// -// See also `getEditsForFileRename_oldFileStillPresent.ts` - // @Filename: /a.ts /////// ////import old from "./src/old"; @@ -18,8 +16,8 @@ // Don't update an unrelated import ////import { x } from "././src/./foo/./a"; -// @Filename: /src/new.ts -//// +// @Filename: /src/old.ts +////export default 0; // @Filename: /tsconfig.json ////{ "files": ["a.ts", "src/a.ts", "src/foo/a.ts", "src/old.ts"] } diff --git a/tests/cases/fourslash/getEditsForFileRename_ambientModule.ts b/tests/cases/fourslash/getEditsForFileRename_ambientModule.ts new file mode 100644 index 0000000000000..9c0baedfab63c --- /dev/null +++ b/tests/cases/fourslash/getEditsForFileRename_ambientModule.ts @@ -0,0 +1,23 @@ +/// + +// @Filename: /tsconfig.json +////{} + +// @Filename: /sub/types.d.ts +// @Symlink: /node_modules/sub/types.d.ts +////declare module "sub" { +//// declare export const abc: number +////} + +// @Filename: /sub/package.json +// @Symlink: /node_modules/sub/package.json +////{ "types": "types.d.ts" } + +// @Filename: /a.ts +////import { abc } from "sub"; + +verify.getEditsForFileRename({ + oldPath: "/a.ts", + newPath: "/b.ts", + newFileContents: {}, +}); diff --git a/tests/cases/fourslash/getEditsForFileRename_amd.ts b/tests/cases/fourslash/getEditsForFileRename_amd.ts index 22bf01e823cdf..5ba006dcdbcde 100644 --- a/tests/cases/fourslash/getEditsForFileRename_amd.ts +++ b/tests/cases/fourslash/getEditsForFileRename_amd.ts @@ -6,7 +6,7 @@ ////import { x } from "old"; // @Filename: /src/old.ts -//// +////export const x = 0; verify.getEditsForFileRename({ oldPath: "/src/old.ts", diff --git a/tests/cases/fourslash/getEditsForFileRename_directory.ts b/tests/cases/fourslash/getEditsForFileRename_directory.ts index 8089d6fa13822..29ecf6fddfa6f 100644 --- a/tests/cases/fourslash/getEditsForFileRename_directory.ts +++ b/tests/cases/fourslash/getEditsForFileRename_directory.ts @@ -18,15 +18,15 @@ ////import old2 from "../old/file"; ////export default 0; -// @Filename: /src/new/index.ts +// @Filename: /src/old/index.ts ////import a from "../../a"; ////import a2 from "../b"; ////import a3 from "../foo/c"; ////import f from "./file"; ////export default 0; -// @Filename: /src/new/file.ts -//// +// @Filename: /src/old/file.ts +////export default 0; // @Filename: /tsconfig.json ////{ "files": ["a.ts", "src/b.ts", "src/foo/c.ts", "src/old/index.ts", "src/old/file.ts"] } diff --git a/tests/cases/fourslash/getEditsForFileRename_directory_down.ts b/tests/cases/fourslash/getEditsForFileRename_directory_down.ts index c4f42e3cc52e3..1febdcfd5e97e 100644 --- a/tests/cases/fourslash/getEditsForFileRename_directory_down.ts +++ b/tests/cases/fourslash/getEditsForFileRename_directory_down.ts @@ -18,15 +18,15 @@ ////import old2 from "../old/file"; ////export default 0; -// @Filename: /src/newDir/new/index.ts +// @Filename: /src/old/index.ts ////import a from "../../a"; ////import a2 from "../b"; ////import a3 from "../foo/c"; ////import f from "./file"; ////export default 0; -// @Filename: /src/newDir/new/file.ts -//// +// @Filename: /src/old/file.ts +////export default 0; // @Filename: /tsconfig.json ////{ "files": ["a.ts", "src/b.ts", "src/foo/c.ts", "src/old/index.ts", "src/old/file.ts"] } @@ -53,7 +53,7 @@ import old from "../newDir/new"; import old2 from "../newDir/new/file"; export default 0;`, - "/src/newDir/new/index.ts": + "/src/old/index.ts": `import a from "../../../a"; import a2 from "../../b"; import a3 from "../../foo/c"; diff --git a/tests/cases/fourslash/getEditsForFileRename_directory_up.ts b/tests/cases/fourslash/getEditsForFileRename_directory_up.ts index c63cfe888f1a0..f85c07891335e 100644 --- a/tests/cases/fourslash/getEditsForFileRename_directory_up.ts +++ b/tests/cases/fourslash/getEditsForFileRename_directory_up.ts @@ -18,15 +18,15 @@ ////import old2 from "../old/file"; ////export default 0; -// @Filename: /newDir/new/index.ts +// @Filename: /src/old/index.ts ////import a from "../../a"; ////import a2 from "../b"; ////import a3 from "../foo/c"; ////import f from "./file"; ////export default 0; -// @Filename: /newDir/new/file.ts -//// +// @Filename: /src/old/file.ts +////export default 0; // @Filename: /tsconfig.json ////{ "files": ["a.ts", "src/b.ts", "src/foo/c.ts", "src/old/index.ts", "src/old/file.ts"] } @@ -53,7 +53,7 @@ import old from "../../newDir/new"; import old2 from "../../newDir/new/file"; export default 0;`, - "/newDir/new/index.ts": + "/src/old/index.ts": `import a from "../../a"; import a2 from "../../src/b"; import a3 from "../../src/foo/c"; diff --git a/tests/cases/fourslash/getEditsForFileRename_oldFileStillPresent.ts b/tests/cases/fourslash/getEditsForFileRename_oldFileStillPresent.ts deleted file mode 100644 index 34084d9d9dffc..0000000000000 --- a/tests/cases/fourslash/getEditsForFileRename_oldFileStillPresent.ts +++ /dev/null @@ -1,32 +0,0 @@ -/// - -// Same test as `getEditsForFileRename.ts`, but with the old file not yet renamed. - -// @Filename: /src/old.ts -////stuff - -// @Filename: /a.ts -/////// -////import old from "./src/old"; - -// @Filename: /src/a.ts -/////// -////import old from "./old"; - -// @Filename: /src/foo/a.ts -/////// -////import old from "../old"; - -// @Filename: /tsconfig.json -////{ "files": ["a.ts", "src/a.ts", "src/foo/a.ts", "src/old.ts"] } - -verify.getEditsForFileRename({ - oldPath: "/src/old.ts", - newPath: "/src/new.ts", - newFileContents: { - "/a.ts": '/// \nimport old from "./src/new";', - "/src/a.ts": '/// \nimport old from "./new";', - "/src/foo/a.ts": '/// \nimport old from "../new";', - "/tsconfig.json": '{ "files": ["a.ts", "src/a.ts", "src/foo/a.ts", "src/new.ts"] }', - }, -}); diff --git a/tests/cases/fourslash/getEditsForFileRename_shortenRelativePaths.ts b/tests/cases/fourslash/getEditsForFileRename_shortenRelativePaths.ts index d5a62f219d59f..256fa468ed3b0 100644 --- a/tests/cases/fourslash/getEditsForFileRename_shortenRelativePaths.ts +++ b/tests/cases/fourslash/getEditsForFileRename_shortenRelativePaths.ts @@ -3,14 +3,14 @@ // @Filename: /src/foo/x.ts //// -// @Filename: /src/foo/new.ts +// @Filename: /src/old.ts ////import { x } from "./foo/x"; verify.getEditsForFileRename({ oldPath: "/src/old.ts", newPath: "/src/foo/new.ts", newFileContents: { - "/src/foo/new.ts": + "/src/old.ts": `import { x } from "./x";`, }, }); diff --git a/tests/cases/fourslash/getEditsForFileRename_subDir.ts b/tests/cases/fourslash/getEditsForFileRename_subDir.ts index ae75f190b9d4d..9ea204e693bd7 100644 --- a/tests/cases/fourslash/getEditsForFileRename_subDir.ts +++ b/tests/cases/fourslash/getEditsForFileRename_subDir.ts @@ -3,14 +3,14 @@ // @Filename: /src/foo/a.ts //// -// @Filename: /src/dir/new.ts +// @Filename: /src/old.ts ////import a from "./foo/a"; verify.getEditsForFileRename({ oldPath: "/src/old.ts", newPath: "/src/dir/new.ts", newFileContents: { - "/src/dir/new.ts": + "/src/old.ts": `import a from "../foo/a";`, }, }); diff --git a/tests/cases/fourslash/getEditsForFileRename_symlink.ts b/tests/cases/fourslash/getEditsForFileRename_symlink.ts new file mode 100644 index 0000000000000..562049054719b --- /dev/null +++ b/tests/cases/fourslash/getEditsForFileRename_symlink.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: /foo.ts +// @Symlink: /node_modules/foo/index.ts +////export const x = 0; + +// @Filename: /user.ts +////import { x } from 'foo'; + +verify.noErrors(); + +verify.getEditsForFileRename({ + oldPath: "/user.ts", + newPath: "/luser.ts", + // no change + newFileContents: {}, +}); diff --git a/tests/cases/fourslash/getEditsForFileRename_tsconfig.ts b/tests/cases/fourslash/getEditsForFileRename_tsconfig.ts index 62a4d835f5f4d..f64023eeb40b3 100644 --- a/tests/cases/fourslash/getEditsForFileRename_tsconfig.ts +++ b/tests/cases/fourslash/getEditsForFileRename_tsconfig.ts @@ -16,6 +16,9 @@ //// "exclude": ["old"], ////} +// @Filename: /src/old/someFile.ts +//// + verify.getEditsForFileRename({ oldPath: "/src/old", newPath: "/src/new", diff --git a/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_add.ts b/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_add.ts index 5b1ef6791c0cc..e9474036a5cb1 100644 --- a/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_add.ts +++ b/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_add.ts @@ -5,6 +5,9 @@ //// "include": ["dir"], ////} +// @Filename: /src/dir/a.ts +//// + verify.getEditsForFileRename({ oldPath: "/src/dir/a.ts", newPath: "/src/newDir/b.ts", diff --git a/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_noChange.ts b/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_noChange.ts index dc2200e17e881..5be4eaa18d8a8 100644 --- a/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_noChange.ts +++ b/tests/cases/fourslash/getEditsForFileRename_tsconfig_include_noChange.ts @@ -5,6 +5,9 @@ //// "include": ["dir"], ////} +// @Filename: /src/dir/a.ts +//// + verify.getEditsForFileRename({ oldPath: "/src/dir/a.ts", newPath: "/src/dir/b.ts",