forked from DonJayamanne/pythonVSCode
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
fs-paths.ts
158 lines (140 loc) · 5.3 KB
/
fs-paths.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as nodepath from 'path';
import { getSearchPathEnvVarNames } from '../utils/exec';
import { getOSType, OSType } from '../utils/platform';
import { IExecutables, IFileSystemPaths, IFileSystemPathUtils } from './types';
const untildify = require('untildify');
// The parts of node's 'path' module used by FileSystemPaths.
interface INodePath {
sep: string;
join(...filenames: string[]): string;
dirname(filename: string): string;
basename(filename: string, ext?: string): string;
normalize(filename: string): string;
}
export class FileSystemPaths implements IFileSystemPaths {
constructor(
// "true" if targeting a case-insensitive host (like Windows)
private readonly isCaseInsensitive: boolean,
// (effectively) the node "path" module to use
private readonly raw: INodePath,
) {}
// Create a new object using common-case default values.
// We do not use an alternate constructor because defaults in the
// constructor runs counter to our typical approach.
public static withDefaults(
// default: use "isWindows"
isCaseInsensitive?: boolean,
): FileSystemPaths {
if (isCaseInsensitive === undefined) {
isCaseInsensitive = getOSType() === OSType.Windows;
}
return new FileSystemPaths(
isCaseInsensitive,
// Use the actual node "path" module.
nodepath,
);
}
public get sep(): string {
return this.raw.sep;
}
public join(...filenames: string[]): string {
return this.raw.join(...filenames);
}
public dirname(filename: string): string {
return this.raw.dirname(filename);
}
public basename(filename: string, suffix?: string): string {
return this.raw.basename(filename, suffix);
}
public normalize(filename: string): string {
return this.raw.normalize(filename);
}
public normCase(filename: string): string {
filename = this.raw.normalize(filename);
return this.isCaseInsensitive ? filename.toUpperCase() : filename;
}
}
export class Executables {
constructor(
// the $PATH delimiter to use
public readonly delimiter: string,
// the OS type to target
private readonly osType: OSType,
) {}
// Create a new object using common-case default values.
// We do not use an alternate constructor because defaults in the
// constructor runs counter to our typical approach.
public static withDefaults(): Executables {
return new Executables(
// Use node's value.
nodepath.delimiter,
// Use the current OS.
getOSType(),
);
}
public get envVar(): string {
return getSearchPathEnvVarNames(this.osType)[0];
}
}
// The dependencies FileSystemPathUtils has on node's path module.
interface IRawPaths {
relative(relpath: string, rootpath: string): string;
}
export class FileSystemPathUtils implements IFileSystemPathUtils {
constructor(
// the user home directory to use (and expose)
public readonly home: string,
// the low-level FS path operations to use (and expose)
public readonly paths: IFileSystemPaths,
// the low-level OS "executables" to use (and expose)
public readonly executables: IExecutables,
// other low-level FS path operations to use
private readonly raw: IRawPaths,
) {}
// Create a new object using common-case default values.
// We do not use an alternate constructor because defaults in the
// constructor runs counter to our typical approach.
public static withDefaults(
// default: a new FileSystemPaths object (using defaults)
paths?: IFileSystemPaths,
): FileSystemPathUtils {
if (paths === undefined) {
paths = FileSystemPaths.withDefaults();
}
return new FileSystemPathUtils(
// Use the current user's home directory.
untildify('~'),
paths,
Executables.withDefaults(),
// Use the actual node "path" module.
nodepath,
);
}
public arePathsSame(path1: string, path2: string): boolean {
path1 = this.paths.normCase(path1);
path2 = this.paths.normCase(path2);
return path1 === path2;
}
public getDisplayName(filename: string, cwd?: string): string {
if (cwd && filename.startsWith(cwd)) {
return `.${this.paths.sep}${this.raw.relative(cwd, filename)}`;
} else if (filename.startsWith(this.home)) {
return `~${this.paths.sep}${this.raw.relative(this.home, filename)}`;
} else {
return filename;
}
}
}
export function normCasePath(filePath: string): string {
return getOSType() === OSType.Windows ? nodepath.normalize(filePath).toUpperCase() : nodepath.normalize(filePath);
}
/**
* Returns true if given file path exists within the given parent directory, false otherwise.
* @param filePath File path to check for
* @param parentPath The potential parent path to check for
*/
export function isParentPath(filePath: string, parentPath: string): boolean {
return normCasePath(filePath).startsWith(normCasePath(parentPath));
}