Skip to content

Commit e3e4c5d

Browse files
author
Andy
authored
getEditsForFileRename: For directory rename, preserve casing of suffix (#24975)
1 parent a7af92e commit e3e4c5d

File tree

5 files changed

+36
-19
lines changed

5 files changed

+36
-19
lines changed

src/compiler/core.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,11 +2087,10 @@ namespace ts {
20872087
return startsWith(str, prefix) ? str.substr(prefix.length) : str;
20882088
}
20892089

2090-
export function tryRemovePrefix(str: string, prefix: string): string | undefined {
2091-
return startsWith(str, prefix) ? str.substring(prefix.length) : undefined;
2090+
export function tryRemovePrefix(str: string, prefix: string, getCanonicalFileName: GetCanonicalFileName = identity): string | undefined {
2091+
return startsWith(getCanonicalFileName(str), getCanonicalFileName(prefix)) ? str.substring(prefix.length) : undefined;
20922092
}
20932093

2094-
20952094
function isPatternMatch({ prefix, suffix }: Pattern, candidate: string) {
20962095
return candidate.length >= prefix.length + suffix.length &&
20972096
startsWith(candidate, prefix) &&

src/compiler/utilities.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7490,19 +7490,20 @@ namespace ts {
74907490
return true;
74917491
}
74927492

7493-
export function tryRemoveDirectoryPrefix(path: string, dirPath: string): string | undefined {
7494-
const a = tryRemovePrefix(path, dirPath);
7495-
if (a === undefined) return undefined;
7496-
switch (a.charCodeAt(0)) {
7497-
case CharacterCodes.slash:
7498-
case CharacterCodes.backslash:
7499-
return a.slice(1);
7500-
default:
7501-
return undefined;
7502-
}
7493+
function isDirectorySeparator(charCode: number): boolean {
7494+
return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash;
7495+
}
7496+
7497+
function stripLeadingDirectorySeparator(s: string): string | undefined {
7498+
return isDirectorySeparator(s.charCodeAt(0)) ? s.slice(1) : undefined;
7499+
}
7500+
7501+
export function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined {
7502+
const withoutPrefix = tryRemovePrefix(path, dirPath, getCanonicalFileName);
7503+
return withoutPrefix === undefined ? undefined : stripLeadingDirectorySeparator(withoutPrefix);
75037504
}
75047505

7505-
// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character.
7506+
// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character.
75067507
// It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future
75077508
// proof.
75087509
const reservedCharacterPattern = /[^\w\s\/]/g;

src/services/getEditsForFileRename.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ namespace ts {
1616
function getPathUpdater(oldFileOrDirPath: string, newFileOrDirPath: string, getCanonicalFileName: GetCanonicalFileName): PathUpdater {
1717
const canonicalOldPath = getCanonicalFileName(oldFileOrDirPath);
1818
return path => {
19-
const canonicalPath = getCanonicalFileName(path);
20-
if (canonicalPath === canonicalOldPath) return newFileOrDirPath;
21-
const suffix = tryRemoveDirectoryPrefix(canonicalPath, canonicalOldPath);
19+
if (getCanonicalFileName(path) === canonicalOldPath) return newFileOrDirPath;
20+
const suffix = tryRemoveDirectoryPrefix(path, canonicalOldPath, getCanonicalFileName);
2221
return suffix === undefined ? undefined : newFileOrDirPath + "/" + suffix;
2322
};
2423
}

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ declare namespace ts {
515515
function findBestPatternMatch<T>(values: ReadonlyArray<T>, getPattern: (value: T) => Pattern, candidate: string): T | undefined;
516516
function startsWith(str: string, prefix: string): boolean;
517517
function removePrefix(str: string, prefix: string): string;
518-
function tryRemovePrefix(str: string, prefix: string): string | undefined;
518+
function tryRemovePrefix(str: string, prefix: string, getCanonicalFileName?: GetCanonicalFileName): string | undefined;
519519
function and<T>(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean;
520520
function or<T>(f: (arg: T) => boolean, g: (arg: T) => boolean): (arg: T) => boolean;
521521
function assertTypeIsNever(_: never): void;
@@ -7269,7 +7269,7 @@ declare namespace ts {
72697269
function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean): Comparison;
72707270
function containsPath(parent: string, child: string, ignoreCase?: boolean): boolean;
72717271
function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean): boolean;
7272-
function tryRemoveDirectoryPrefix(path: string, dirPath: string): string | undefined;
7272+
function tryRemoveDirectoryPrefix(path: string, dirPath: string, getCanonicalFileName: GetCanonicalFileName): string | undefined;
72737273
function hasExtension(fileName: string): boolean;
72747274
const commonPackageFolders: ReadonlyArray<string>;
72757275
function getRegularExpressionForWildcard(specs: ReadonlyArray<string> | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @Filename: /a.ts
4+
////import { foo } from "./dir/fOo";
5+
6+
// @Filename: /dir/fOo.ts
7+
////export const foo = 0;
8+
9+
// On a case-insensitive file system (like fourslash uses), there was a bug where we used the canonicalized path suffix.
10+
11+
verify.getEditsForFileRename({
12+
oldPath: "/dir",
13+
newPath: "/newDir",
14+
newFileContents: {
15+
"/a.ts":
16+
`import { foo } from "./newDir/fOo";`,
17+
},
18+
});

0 commit comments

Comments
 (0)