src/vs/base/common/resources.ts

444 LOC · 426 covered · 18 uncovered · 83 ranges · 13812 concepts · 40 introducers · 7273 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- resources.ts ×23
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CharCode } from './charCode.js';
7 > import * as extpath from './extpath.js';
8 > import { Schemas } from './network.js';
9 > import * as paths from './path.js';
10 > import { isLinux, isWindows } from './platform.js';
11 > import { compare as strCompare, equalsIgnoreCase } from './strings.js';
12 > import { URI, uriToFsPath } from './uri.js';
13 >
14 > export function originalFSPath(uri: URI): string {
15 > return uriToFsPath(uri, true); resources.ts ×1
16 > }
18 > //#region IExtUri
19 >
20 > export interface IExtUri {
21 >
22 > // --- identity
23 >
24 > /**
25 > * Compares two uris.
26 > *
27 > * @param uri1 Uri
28 > * @param uri2 Uri
29 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
30 > */
31 > compare(uri1: URI, uri2: URI, ignoreFragment?: boolean): number;
32 >
33 > /**
34 > * Tests whether two uris are equal
35 > *
36 > * @param uri1 Uri
37 > * @param uri2 Uri
38 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
39 > */
40 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment?: boolean): boolean;
41 >
42 > /**
43 > * Tests whether a `candidate` URI is a parent or equal of a given `base` URI.
44 > *
45 > * @param base A uri which is "longer" or at least same length as `parentCandidate`
46 > * @param parentCandidate A uri which is "shorter" or up to same length as `base`
47 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
48 > */
49 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment?: boolean): boolean;
50 >
51 > /**
52 > * Creates a key from a resource URI to be used to resource comparison and for resource maps.
53 > * @see {@link ResourceMap}
54 > * @param uri Uri
55 > * @param ignoreFragment Ignore the fragment (defaults to `false`)
56 > */
57 > getComparisonKey(uri: URI, ignoreFragment?: boolean): string;
58 >
59 > /**
60 > * Whether the casing of the path-component of the uri should be ignored.
61 > */
62 > ignorePathCasing(uri: URI): boolean;
63 >
64 > // --- path math
65 >
66 > basenameOrAuthority(resource: URI): string;
67 >
68 > /**
69 > * Returns the basename of the path component of an uri.
70 > * @param resource
71 > */
72 > basename(resource: URI): string;
73 >
74 > /**
75 > * Returns the extension of the path component of an uri.
76 > * @param resource
77 > */
78 > extname(resource: URI): string;
79 > /**
80 > * Return a URI representing the directory of a URI path.
81 > *
82 > * @param resource The input URI.
83 > * @returns The URI representing the directory of the input URI.
84 > */
85 > dirname(resource: URI): URI;
86 > /**
87 > * Join a URI path with path fragments and normalizes the resulting path.
88 > *
89 > * @param resource The input URI.
90 > * @param pathFragment The path fragment to add to the URI path.
91 > * @returns The resulting URI.
92 > */
93 > joinPath(resource: URI, ...pathFragment: string[]): URI;
94 > /**
95 > * Normalizes the path part of a URI: Resolves `.` and `..` elements with directory names.
96 > *
97 > * @param resource The URI to normalize the path.
98 > * @returns The URI with the normalized path.
99 > */
100 > normalizePath(resource: URI): URI;
101 > /**
102 > *
103 > * @param from
104 > * @param to
105 > */
106 > relativePath(from: URI, to: URI): string | undefined;
107 > /**
108 > * Resolves an absolute or relative path against a base URI.
109 > * The path can be relative or absolute posix or a Windows path
110 > */
111 > resolvePath(base: URI, path: string): URI;
112 >
113 > // --- misc
114 >
115 > /**
116 > * Returns true if the URI path is absolute.
117 > */
118 > isAbsolutePath(resource: URI): boolean;
119 > /**
120 > * Tests whether the two authorities are the same
121 > */
122 > isEqualAuthority(a1: string, a2: string): boolean;
123 > /**
124 > * Returns true if the URI path has a trailing path separator
125 > */
126 > hasTrailingPathSeparator(resource: URI, sep?: string): boolean;
127 > /**
128 > * Removes a trailing path separator, if there's one.
129 > * Important: Doesn't remove the first slash, it would make the URI invalid
130 > */
131 > removeTrailingPathSeparator(resource: URI, sep?: string): URI;
132 > /**
133 > * Adds a trailing path separator to the URI if there isn't one already.
134 > * For example, c:\ would be unchanged, but c:\users would become c:\users\
135 > */
136 > addTrailingPathSeparator(resource: URI, sep?: string): URI;
137 > }
138 >
139 > export class ExtUri implements IExtUri {
140 >
141 > constructor(private _ignorePathCasing: (uri: URI) => boolean) { }
142 >
143 > compare(uri1: URI, uri2: URI, ignoreFragment: boolean = false): number {
144 > if (uri1 === uri2) { resources.ts ×1
145 > return 0;
146 > }
147 > return strCompare(this.getComparisonKey(uri1, ignoreFragment), this.getComparisonKey(uri2, ignoreFragment));
148 > }
150 > isEqual(uri1: URI | undefined, uri2: URI | undefined, ignoreFragment: boolean = false): boolean {
151 > if (uri1 === uri2) { resources.ts ×3
152 > return true; resources.ts ×1
153 > }
154 > if (!uri1 || !uri2) { resources.ts ×3
155 > return false; resources.ts ×1
156 > }
157 > return this.getComparisonKey(uri1, ignoreFragment) === this.getComparisonKey(uri2, ignoreFragment); resources.ts ×1
160 > getComparisonKey(uri: URI, ignoreFragment: boolean = false): string {
161 > return uri.with({ resources.ts ×1
162 > path: this._ignorePathCasing(uri) ? uri.path.toLowerCase() : undefined,
163 > fragment: ignoreFragment ? null : undefined
164 > }).toString();
165 > }
167 > ignorePathCasing(uri: URI): boolean {
168 return this._ignorePathCasing(uri);
169 }
171 > isEqualOrParent(base: URI, parentCandidate: URI, ignoreFragment: boolean = false): boolean {
172 > if (base.scheme === parentCandidate.scheme) { resources.ts ×2
173 > if (base.scheme === Schemas.file) { resources.ts ×2
174 > return extpath.isEqualOrParent(originalFSPath(base), originalFSPath(parentCandidate), this._ignorePathCasing(base)) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment); resources.ts ×1
175 > }
176 > if (isEqualAuthority(base.authority, parentCandidate.authority)) { resources.ts ×1
177 > return extpath.isEqualOrParent(base.path, parentCandidate.path, this._ignorePathCasing(base), true) && base.query === parentCandidate.query && (ignoreFragment || base.fragment === parentCandidate.fragment);
178 > }
180 > return false; resources.ts ×1
183 > // --- path math
184 >
185 > joinPath(resource: URI, ...pathFragment: string[]): URI {
186 > return URI.joinPath(resource, ...pathFragment); resources.ts ×1
187 > }
189 > basenameOrAuthority(resource: URI): string {
190 > return basename(resource) || resource.authority; resources.ts ×1
191 > }
193 > basename(resource: URI, suffix?: string): string {
194 > return paths.posix.basename(resource.path, suffix); resources.ts ×1
195 > }
197 > extname(resource: URI): string {
198 > return paths.posix.extname(resource.path); resources.ts ×1
199 > }
201 > dirname(resource: URI): URI {
202 > if (resource.path.length === 0) { resources.ts ×4
203 > return resource; resources.ts ×1
204 > }
205 > let dirname; resources.ts ×4
206 > if (resource.scheme === Schemas.file) {
207 > dirname = URI.file(paths.dirname(originalFSPath(resource))).path; resources.ts ×1
208 > } else { resources.ts ×4
209 > dirname = paths.posix.dirname(resource.path); resources.ts ×2
210 > if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) {
211 console.error(`dirname("${resource.toString})) resulted in a relative path`);
212 dirname = '/'; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character
213 }
215 > return resource.with({ resources.ts ×4
216 > path: dirname
217 > });
218 > }
220 > normalizePath(resource: URI): URI {
221 > if (!resource.path.length) { resources.ts ×4
222 > return resource; resources.ts ×1
223 > }
224 > let normalizedPath: string; resources.ts ×4
225 > if (resource.scheme === Schemas.file) {
226 > normalizedPath = URI.file(paths.normalize(originalFSPath(resource))).path; resources.ts ×1
227 > } else { resources.ts ×4
228 > normalizedPath = paths.posix.normalize(resource.path); resources.ts ×1
229 > }
230 > return resource.with({ resources.ts ×4
231 > path: normalizedPath
232 > });
233 > }
235 > relativePath(from: URI, to: URI): string | undefined {
236 > if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { resources.ts ×4
237 > return undefined; path.ts ×2
238 > }
239 > if (from.scheme === Schemas.file) { resources.ts ×4
240 > const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); resources.ts ×1
241 > return isWindows ? extpath.toSlashes(relativePath) : relativePath;
242 > }
243 > let fromPath = from.path || '/'; resources.ts ×2
244 > const toPath = to.path || '/'; resources.ts ×4
245 > if (this._ignorePathCasing(from)) {
246 > // make casing of fromPath match toPath path.ts ×2
247 > let i = 0;
248 > for (const len = Math.min(fromPath.length, toPath.length); i < len; i++) {
249 > if (fromPath.charCodeAt(i) !== toPath.charCodeAt(i)) {
250 > if (fromPath.charAt(i).toLowerCase() !== toPath.charAt(i).toLowerCase()) {
251 > break;
252 > }
253 > }
254 > }
255 > fromPath = toPath.substr(0, i) + fromPath.substr(i);
256 > }
257 > return paths.posix.relative(fromPath, toPath); resources.ts ×2
260 > resolvePath(base: URI, path: string): URI {
261 > if (base.scheme === Schemas.file) { resources.ts ×2
262 > const newURI = URI.file(paths.resolve(originalFSPath(base), path));
263 > return base.with({
264 > authority: newURI.authority,
265 > path: newURI.path
266 > });
267 > }
268 > path = extpath.toPosixPath(path); // we allow path to be a windows path extpath.ts ×1
269 > return base.with({
270 > path: paths.posix.resolve(base.path, path)
271 > });
274 > // --- misc
275 >
276 > isAbsolutePath(resource: URI): boolean {
277 > return !!resource.path && resource.path[0] === '/'; resources.ts ×1
278 > }
280 > isEqualAuthority(a1: string | undefined, a2: string | undefined) {
281 > return a1 === a2 || (a1 !== undefined && a2 !== undefined && equalsIgnoreCase(a1, a2)); resources.ts ×1
282 > }
284 > hasTrailingPathSeparator(resource: URI, sep: string = paths.sep): boolean {
285 > if (resource.scheme === Schemas.file) { resources.ts ×4
286 > const fsp = originalFSPath(resource);
287 > return fsp.length > extpath.getRoot(fsp).length && fsp[fsp.length - 1] === sep;
288 > } else {
289 > const p = resource.path; resources.ts ×1
290 > return (p.length > 1 && p.charCodeAt(p.length - 1) === CharCode.Slash) && !(/^[a-zA-Z]:(\/$|\\$)/.test(resource.fsPath)); // ignore the slash at offset 0
291 > }
294 > removeTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
295 > // Make sure that the path isn't a drive letter. A trailing separator there is not removable. resources.ts ×4
296 > if (hasTrailingPathSeparator(resource, sep)) {
297 > return resource.with({ path: resource.path.substr(0, resource.path.length - 1) }); resources.ts ×1
298 > }
299 > return resource; resources.ts ×4
300 > }
302 > addTrailingPathSeparator(resource: URI, sep: string = paths.sep): URI {
303 > let isRootSep: boolean = false; resources.ts ×1
304 > if (resource.scheme === Schemas.file) {
305 > const fsp = originalFSPath(resource);
306 > isRootSep = ((fsp !== undefined) && (fsp.length === extpath.getRoot(fsp).length) && (fsp[fsp.length - 1] === sep));
307 > } else {
308 > sep = '/';
309 > const p = resource.path;
310 > isRootSep = p.length === 1 && p.charCodeAt(p.length - 1) === CharCode.Slash;
311 > }
312 > if (!isRootSep && !hasTrailingPathSeparator(resource, sep)) {
313 > return resource.with({ path: resource.path + '/' });
314 > }
315 > return resource;
316 > }
318 >
319 >
320 > /**
321 > * Unbiased utility that takes uris "as they are". This means it can be interchanged with
322 > * uri#toString() usages. The following is true
323 > * ```
324 > * assertEqual(aUri.toString() === bUri.toString(), exturi.isEqual(aUri, bUri))
325 > * ```
326 > */
327 > export const extUri = new ExtUri(() => false);
328 >
329 > /**
330 > * BIASED utility that _mostly_ ignored the case of urs paths. ONLY use this util if you
331 > * understand what you are doing.
332 > *
333 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
334 > *
335 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
336 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
337 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
338 > * casing matters.
339 > */
340 > export const extUriBiasedIgnorePathCase = new ExtUri(uri => {
341 > // A file scheme resource is in the same platform as code, so ignore case for non linux platforms resources.ts ×1
342 > // Resource can be from another platform. Lowering the case as an hack. Should come from File system provider
343 > return uri.scheme === Schemas.file ? !isLinux : true;
344 > });
346 >
347 > /**
348 > * BIASED utility that always ignores the casing of uris paths. ONLY use this util if you
349 > * understand what you are doing.
350 > *
351 > * This utility is INCOMPATIBLE with `uri.toString()`-usages and both CANNOT be used interchanged.
352 > *
353 > * When dealing with uris from files or documents, `extUri` (the unbiased friend)is sufficient
354 > * because those uris come from a "trustworthy source". When creating unknown uris it's always
355 > * better to use `IUriIdentityService` which exposes an `IExtUri`-instance which knows when path
356 > * casing matters.
357 > */
358 > export const extUriIgnorePathCase = new ExtUri(_ => true);
359 >
360 > export const isEqual = extUri.isEqual.bind(extUri);
361 > export const isEqualOrParent = extUri.isEqualOrParent.bind(extUri);
362 > export const getComparisonKey = extUri.getComparisonKey.bind(extUri);
363 > export const basenameOrAuthority = extUri.basenameOrAuthority.bind(extUri);
364 > export const basename = extUri.basename.bind(extUri);
365 > export const extname = extUri.extname.bind(extUri);
366 > export const dirname = extUri.dirname.bind(extUri);
367 > export const joinPath = extUri.joinPath.bind(extUri);
368 > export const normalizePath = extUri.normalizePath.bind(extUri);
369 > export const relativePath = extUri.relativePath.bind(extUri);
370 > export const resolvePath = extUri.resolvePath.bind(extUri);
371 > export const isAbsolutePath = extUri.isAbsolutePath.bind(extUri);
372 > export const isEqualAuthority = extUri.isEqualAuthority.bind(extUri);
373 > export const hasTrailingPathSeparator = extUri.hasTrailingPathSeparator.bind(extUri);
374 > export const removeTrailingPathSeparator = extUri.removeTrailingPathSeparator.bind(extUri);
375 > export const addTrailingPathSeparator = extUri.addTrailingPathSeparator.bind(extUri);
376 >
377 > //#endregion
378 >
379 > export function distinctParents<T>(items: T[], resourceAccessor: (item: T) => URI): T[] {
380 > const distinctParents: T[] = []; resources.ts ×1
381 > for (let i = 0; i < items.length; i++) {
382 > const candidateResource = resourceAccessor(items[i]);
383 > if (items.some((otherItem, index) => {
384 > if (index === i) {
385 > return false;
386 > }
387 >
388 > return isEqualOrParent(candidateResource, resourceAccessor(otherItem));
389 > })) {
390 > continue;
391 > }
392 >
393 > distinctParents.push(items[i]);
394 > }
395 >
396 > return distinctParents;
397 > }
399 > /**
400 > * Data URI related helpers.
401 > */
402 > export namespace DataUri {
403 >
404 > export const META_DATA_LABEL = 'label';
405 > export const META_DATA_DESCRIPTION = 'description';
406 > export const META_DATA_SIZE = 'size';
407 > export const META_DATA_MIME = 'mime';
408 >
409 > export function parseMetaData(dataUri: URI): Map<string, string> {
410 > const metadata = new Map<string, string>(); resources.ts ×2
411 >
412 > // Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
413 > // the metadata is: size:2313;label:SomeLabel;description:SomeDescription
414 > const meta = dataUri.path.substring(dataUri.path.indexOf(';') + 1, dataUri.path.lastIndexOf(';'));
415 > meta.split(';').forEach(property => {
416 > const [key, value] = property.split(':');
417 > if (key && value) {
418 > metadata.set(key, value);
419 > }
420 > });
421 >
422 > // Given a URI of: data:image/png;size:2313;label:SomeLabel;description:SomeDescription;base64,77+9UE5...
423 > // the mime is: image/png
424 > const mime = dataUri.path.substring(0, dataUri.path.indexOf(';'));
425 > if (mime) {
426 metadata.set(META_DATA_MIME, mime);
427 }
429 > return metadata;
430 > }
432 >
433 > export function toLocalResource(resource: URI, authority: string | undefined, localScheme: string): URI {
434 if (authority) {
435 let path = resource.path;
436 if (path && path[0] !== paths.posix.sep) {
437 path = paths.posix.sep + path;
438 }
439
440 return resource.with({ scheme: localScheme, authority, path });
441 }
442
443 return resource.with({ scheme: localScheme });
444 }