src/vs/base/common/uri.ts

754 LOC · 729 covered · 25 uncovered · 202 ranges · 20961 concepts · 88 introducers · 12741 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 > /*--------------------------------------------------------------------------------------------- map.ts ×97
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 { MarshalledId } from './marshallingIds.js';
8 > import * as paths from './path.js';
9 > import { isWindows } from './platform.js';
10 >
11 > const _schemePattern = /^\w[\w\d+.-]*$/;
12 > const _singleSlashStart = /^\//;
13 > const _doubleSlashStart = /^\/\//;
14 >
15 > function _validateUri(ret: URI, _strict?: boolean): void { uri.ts ×9
16 >
17 > // scheme, must be set
18 > if (!ret.scheme && _strict) {
19 > throw new Error(`[UriError]: Scheme is missing: {scheme: "", authority: "${ret.authority}", path: "${ret.path}", query: "${ret.query}", fragment: "${ret.fragment}"}`); uri.ts ×1
20 > }
21 > uri.ts ×2
22 > // scheme, https://tools.ietf.org/html/rfc3986#section-3.1
23 > // ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
24 > if (ret.scheme && !_schemePattern.test(ret.scheme)) { uri.ts ×9
25 > const matches = [...ret.scheme.matchAll(/[^\w\d+.-]/gu)]; uri.ts ×2
26 > const detail = matches.length > 0
27 > ? ` Found '${matches[0][0]}' at index ${matches[0].index} (${matches.length} total)`
28 : '';
29 > throw new Error(`[UriError]: Scheme contains illegal characters.${detail} (len:${ret.scheme.length})`); uri.ts ×2
30 > }
31 > uri.ts ×2
32 > // path, http://tools.ietf.org/html/rfc3986#section-3.3
33 > // If a URI contains an authority component, then the path component
34 > // must either be empty or begin with a slash ("/") character. If a URI
35 > // does not contain an authority component, then the path cannot begin
36 > // with two slash characters ("//").
37 > if (ret.path) {
38 > if (ret.authority) { uri.ts ×3
39 > if (!_singleSlashStart.test(ret.path)) { uri.ts ×1
40 > throw new Error('[UriError]: If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character'); uri.ts ×1
41 > }
42 > } else { uri.ts ×3
43 > if (_doubleSlashStart.test(ret.path)) { uri.ts ×2
44 > throw new Error('[UriError]: If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//")'); uri.ts ×1
45 > }
46 > } uri.ts ×2
47 > } uri.ts ×3
48 > } uri.ts ×9
50 > // for a while we allowed uris *without* schemes and this is the migration
51 > // for them, e.g. an uri without scheme and without strict-mode warns and falls
52 > // back to the file-scheme. that should cause the least carnage and still be a
53 > // clear warning
54 > function _schemeFix(scheme: string, _strict: boolean): string { uri.ts ×9
55 > if (!scheme && !_strict) {
56 > return 'file'; uri.ts ×1
57 > }
58 > return scheme; uri.ts ×1
59 > }
61 > // implements a bit of https://tools.ietf.org/html/rfc3986#section-5
62 > function _referenceResolution(scheme: string, path: string): string { uri.ts ×9
63 >
64 > // the slash-character is our 'default base' as we don't
65 > // support constructing URIs relative to other URIs. This
66 > // also means that we alter and potentially break paths.
67 > // see https://tools.ietf.org/html/rfc3986#section-5.1.4
68 > switch (scheme) {
69 > case 'https':
70 > case 'http':
71 > case 'file':
72 > if (!path) { uri.ts ×3
73 > path = _slash; uri.ts ×1
74 > } else if (path[0] !== _slash) { uri.ts ×3
75 > path = _slash + path; uri.ts ×1
76 > }
77 > break; uri.ts ×3
78 > } uri.ts ×9
79 > return path;
80 > }
82 > const _empty = '';
83 > const _slash = '/';
84 > const _regexp = /^(([^:/?#]+?):)?(\/\/([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/;
85 >
86 > /**
87 > * Uniform Resource Identifier (URI) http://tools.ietf.org/html/rfc3986.
88 > * This class is a simple parser which creates the basic component parts
89 > * (http://tools.ietf.org/html/rfc3986#section-3) with minimal validation
90 > * and encoding.
91 > *
92 > * ```txt
93 > * foo://example.com:8042/over/there?name=ferret#nose
94 > * \_/ \______________/\_________/ \_________/ \__/
95 > * | | | | |
96 > * scheme authority path query fragment
97 > * | _____________________|__
98 > * / \ / \
99 > * urn:example:animal:ferret:nose
100 > * ```
101 > */
102 > export class URI implements UriComponents {
103 >
104 > static isUri(thing: unknown): thing is URI {
105 > if (thing instanceof URI) { uri.ts ×3
106 > return true; uri.ts ×1
107 > }
108 > if (!thing || typeof thing !== 'object') { uri.ts ×3
109 > return false; uri.ts ×1
110 > }
111 > return typeof (<URI>thing).authority === 'string' uri.ts ×1
112 > && typeof (<URI>thing).fragment === 'string' uri.ts ×1
113 > && typeof (<URI>thing).path === 'string'
114 > && typeof (<URI>thing).query === 'string'
115 > && typeof (<URI>thing).scheme === 'string'
116 > && typeof (<URI>thing).fsPath === 'string'
117 > && typeof (<URI>thing).with === 'function'
118 > && typeof (<URI>thing).toString === 'function';
119 > } uri.ts ×3
120 > map.ts ×97
121 > /**
122 > * scheme is the 'http' part of 'http://www.example.com/some/path?query#fragment'.
123 > * The part before the first colon.
124 > */
125 > readonly scheme: string;
126 >
127 > /**
128 > * authority is the 'www.example.com' part of 'http://www.example.com/some/path?query#fragment'.
129 > * The part between the first double slashes and the next slash.
130 > */
131 > readonly authority: string;
132 >
133 > /**
134 > * path is the '/some/path' part of 'http://www.example.com/some/path?query#fragment'.
135 > */
136 > readonly path: string;
137 >
138 > /**
139 > * query is the 'query' part of 'http://www.example.com/some/path?query#fragment'.
140 > */
141 > readonly query: string;
142 >
143 > /**
144 > * fragment is the 'fragment' part of 'http://www.example.com/some/path?query#fragment'.
145 > */
146 > readonly fragment: string;
147 >
148 > /**
149 > * @internal
150 > */
151 > protected constructor(scheme: string, authority?: string, path?: string, query?: string, fragment?: string, _strict?: boolean);
152 >
153 > /**
154 > * @internal
155 > */
156 > protected constructor(components: UriComponents);
157 >
158 > /**
159 > * @internal
160 > */
161 > protected constructor(schemeOrData: string | UriComponents, authority?: string, path?: string, query?: string, fragment?: string, _strict: boolean = false) {
162 > uri.ts ×9
163 > if (typeof schemeOrData === 'object') {
164 > this.scheme = schemeOrData.scheme || _empty; uri.ts ×2
165 > this.authority = schemeOrData.authority || _empty;
166 > this.path = schemeOrData.path || _empty;
167 > this.query = schemeOrData.query || _empty;
168 > this.fragment = schemeOrData.fragment || _empty;
169 > // no validation because it's this URI
170 > // that creates uri components.
171 > // _validateUri(this);
172 > } else { uri.ts ×9
173 > this.scheme = _schemeFix(schemeOrData, _strict);
174 > this.authority = authority || _empty;
175 > this.path = _referenceResolution(this.scheme, path || _empty);
176 > this.query = query || _empty;
177 > this.fragment = fragment || _empty;
178 >
179 > _validateUri(this, _strict);
180 > }
181 > }
182 > map.ts ×97
183 > // ---- filesystem path -----------------------
184 >
185 > /**
186 > * Returns a string representing the corresponding file system path of this URI.
187 > * Will handle UNC paths, normalizes windows drive letters to lower-case, and uses the
188 > * platform specific path separator.
189 > *
190 > * * Will *not* validate the path for invalid characters and semantics.
191 > * * Will *not* look at the scheme of this URI.
192 > * * The result shall *not* be used for display purposes but for accessing a file on disk.
193 > *
194 > *
195 > * The *difference* to `URI#path` is the use of the platform specific separator and the handling
196 > * of UNC paths. See the below sample of a file-uri with an authority (UNC path).
197 > *
198 > * ```ts
199 > const u = URI.parse('file://server/c$/folder/file.txt')
200 > u.authority === 'server'
201 > u.path === '/shares/c$/file.txt'
202 > u.fsPath === '\\server\c$\folder\file.txt'
203 > ```
204 > *
205 > * Using `URI#path` to read a file (using fs-apis) would not be enough because parts of the path,
206 > * namely the server name, would be missing. Therefore `URI#fsPath` exists - it's sugar to ease working
207 > * with URIs that represent files on disk (`file` scheme).
208 > */
209 > get fsPath(): string {
210 // if (this.scheme !== 'file') {
211 // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`);
212 // }
213 return uriToFsPath(this, false);
214 }
215 > map.ts ×97
216 > // ---- modify to new -------------------------
217 >
218 > with(change: { scheme?: string; authority?: string | null; path?: string | null; query?: string | null; fragment?: string | null }): URI {
219 > uri.ts ×14
220 > if (!change) {
221 > return this; uri.ts ×1
222 > }
223 > uri.ts ×14
224 > let { scheme, authority, path, query, fragment } = change;
225 > if (scheme === undefined) {
226 > scheme = this.scheme; uri.ts ×2
227 > } else if (scheme === null) { uri.ts ×14
228 scheme = _empty;
229 }
230 > if (authority === undefined) { uri.ts ×14
231 > authority = this.authority; uri.ts ×1
232 > } else if (authority === null) { uri.ts ×14
233 > authority = _empty; uri.ts ×1
234 > }
235 > if (path === undefined) { uri.ts ×14
236 > path = this.path; uri.ts ×1
237 > } else if (path === null) { uri.ts ×14
238 > path = _empty; uri.ts ×1
239 > }
240 > if (query === undefined) { uri.ts ×14
241 > query = this.query; uri.ts ×1
242 > } else if (query === null) { uri.ts ×14
243 > query = _empty; uri.ts ×1
244 > }
245 > if (fragment === undefined) { uri.ts ×14
246 > fragment = this.fragment; uri.ts ×1
247 > } else if (fragment === null) { uri.ts ×14
248 > fragment = _empty; uri.ts ×1
249 > }
250 > uri.ts ×14
251 > if (scheme === this.scheme
252 > && authority === this.authority uri.ts ×2
253 > && path === this.path
254 > && query === this.query uri.ts ×1
255 > && fragment === this.fragment) { uri.ts ×14
256 > uri.ts ×1
257 > return this;
258 > }
259 > uri.ts ×1
260 > return new Uri(scheme, authority, path, query, fragment);
261 > } uri.ts ×14
262 > map.ts ×97
263 > // ---- parse & validate ------------------------
264 >
265 > /**
266 > * Creates a new URI from a string, e.g. `http://www.example.com/some/path`,
267 > * `file:///usr/home`, or `scheme:with/path`.
268 > *
269 > * @param value A string which represents an URI (see `URI#toString`).
270 > */
271 > static parse(value: string, _strict: boolean = false): URI {
272 > const match = _regexp.exec(value); uri.ts ×3
273 > if (!match) {
274 return new Uri(_empty, _empty, _empty, _empty, _empty);
275 }
276 > return new Uri( uri.ts ×3
277 > match[2] || _empty,
278 > percentDecode(match[4] || _empty),
279 > percentDecode(match[5] || _empty),
280 > percentDecode(match[7] || _empty),
281 > percentDecode(match[9] || _empty),
282 > _strict
283 > );
284 > }
285 > map.ts ×97
286 > /**
287 > * Creates a new URI from a file system path, e.g. `c:\my\files`,
288 > * `/usr/home`, or `\\server\share\some\path`.
289 > *
290 > * The *difference* between `URI#parse` and `URI#file` is that the latter treats the argument
291 > * as path, not as stringified-uri. E.g. `URI.file(path)` is **not the same as**
292 > * `URI.parse('file://' + path)` because the path might contain characters that are
293 > * interpreted (# and ?). See the following sample:
294 > * ```ts
295 > const good = URI.file('/coding/c#/project1');
296 > good.scheme === 'file';
297 > good.path === '/coding/c#/project1';
298 > good.fragment === '';
299 > const bad = URI.parse('file://' + '/coding/c#/project1');
300 > bad.scheme === 'file';
301 > bad.path === '/coding/c'; // path is now broken
302 > bad.fragment === '/project1';
303 > ```
304 > *
305 > * @param path A file system path (see `URI#fsPath`)
306 > */
307 > static file(path: string): URI {
308 > uri.ts ×3
309 > let authority = _empty;
310 >
311 > // normalize to fwd-slashes on windows,
312 > // on other systems bwd-slashes are valid
313 > // filename character, eg /f\oo/ba\r.txt
314 > if (isWindows) {
315 path = path.replace(/\\/g, _slash);
316 }
317 > uri.ts ×3
318 > // check for authority as used in UNC shares
319 > // or use the path as given
320 > if (path[0] === _slash && path[1] === _slash) {
321 > const idx = path.indexOf(_slash, 2); uri.ts ×2
322 > if (idx === -1) {
323 authority = path.substring(2);
324 path = _slash;
325 > } else { uri.ts ×2
326 > authority = path.substring(2, idx);
327 > path = path.substring(idx) || _slash;
328 > }
329 > }
330 > uri.ts ×3
331 > return new Uri('file', authority, path, _empty, _empty);
332 > }
333 > map.ts ×97
334 > /**
335 > * Creates new URI from uri components.
336 > *
337 > * Unless `strict` is `true` the scheme is defaults to be `file`. This function performs
338 > * validation and should be used for untrusted uri components retrieved from storage,
339 > * user input, command arguments etc
340 > */
341 > static from(components: UriComponents, strict?: boolean): URI {
342 > const result = new Uri( uri.ts ×1
343 > components.scheme,
344 > components.authority,
345 > components.path,
346 > components.query,
347 > components.fragment,
348 > strict
349 > );
350 > return result;
351 > }
352 > map.ts ×97
353 > /**
354 > * Join a URI path with path fragments and normalizes the resulting path.
355 > *
356 > * @param uri The input URI.
357 > * @param pathFragment The path fragment to add to the URI path.
358 > * @returns The resulting URI.
359 > */
360 > static joinPath(uri: URI, ...pathFragment: string[]): URI {
361 > if (!uri.path) { uri.ts ×3
362 > throw new Error(`[UriError]: cannot call joinPath on URI without path: ${uri.toString()}`); uri.ts ×1
363 > }
364 > let newPath: string; uri.ts ×3
365 > if (isWindows && uri.scheme === 'file') {
366 newPath = URI.file(paths.win32.join(uriToFsPath(uri, true), ...pathFragment)).path;
367 > } else { uri.ts ×3
368 > newPath = paths.posix.join(uri.path, ...pathFragment);
369 > }
370 > return uri.with({ path: newPath });
371 > }
372 > map.ts ×97
373 > // ---- printing/externalize ---------------------------
374 >
375 > /**
376 > * Creates a string representation for this URI. It's guaranteed that calling
377 > * `URI.parse` with the result of this function creates an URI which is equal
378 > * to this URI.
379 > *
380 > * * The result shall *not* be used for display purposes but for externalization or transport.
381 > * * The result will be encoded using the percentage encoding and encoding happens mostly
382 > * ignore the scheme-specific encoding rules.
383 > *
384 > * @param skipEncoding Do not encode the result, default is `false`
385 > */
386 > toString(skipEncoding: boolean = false): string {
387 return _asFormatted(this, skipEncoding);
388 }
389 > map.ts ×97
390 > toJSON(): UriComponents {
391 return this;
392 }
393 > map.ts ×97
394 > /**
395 > * A helper function to revive URIs.
396 > *
397 > * **Note** that this function should only be used when receiving URI#toJSON generated data
398 > * and that it doesn't do any validation. Use {@link URI.from} when received "untrusted"
399 > * uri components such as command arguments or data from storage.
400 > *
401 > * @param data The URI components or URI to revive.
402 > * @returns The revived URI or undefined or null.
403 > */
404 > static revive(data: UriComponents | URI): URI;
405 > static revive(data: UriComponents | URI | undefined): URI | undefined;
406 > static revive(data: UriComponents | URI | null): URI | null;
407 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null;
408 > static revive(data: UriComponents | URI | undefined | null): URI | undefined | null {
409 > if (!data) { uri.ts ×3
410 > return data; extHostTerminalService.ts ×31
411 > } else if (data instanceof URI) { uri.ts ×3
412 > return data; uri.ts ×1
413 > } else { uri.ts ×1
414 > const result = new Uri(data); uri.ts ×2
415 > result._formatted = (<UriState>data).external ?? null;
416 > result._fsPath = (<UriState>data)._sep === _pathSepMarker ? (<UriState>data).fsPath ?? null : null;
417 > return result;
418 > }
419 > } uri.ts ×3
420 > map.ts ×97
421 > [Symbol.for('debug.description')]() {
422 > return `URI(${this.toString()})`; uri.ts ×1
423 > }
424 > } map.ts ×97
425 >
426 > export interface UriComponents {
427 > scheme: string;
428 > authority?: string;
429 > path?: string;
430 > query?: string;
431 > fragment?: string;
432 > }
433 >
434 > export function isUriComponents(thing: unknown): thing is UriComponents {
435 > if (!thing || typeof thing !== 'object') { uri.ts ×3
436 > return false; uri.ts ×1
437 > }
438 > return typeof (<UriComponents>thing).scheme === 'string' uri.ts ×3
439 > && (typeof (<UriComponents>thing).authority === 'string' || typeof (<UriComponents>thing).authority === 'undefined') uri.ts ×1
440 > && (typeof (<UriComponents>thing).path === 'string' || typeof (<UriComponents>thing).path === 'undefined')
441 > && (typeof (<UriComponents>thing).query === 'string' || typeof (<UriComponents>thing).query === 'undefined')
442 > && (typeof (<UriComponents>thing).fragment === 'string' || typeof (<UriComponents>thing).fragment === 'undefined');
443 > } uri.ts ×3
444 > map.ts ×97
445 > interface UriState extends UriComponents {
446 > $mid: MarshalledId.Uri;
447 > external?: string;
448 > fsPath?: string;
449 > _sep?: 1;
450 > }
451 >
452 > const _pathSepMarker = isWindows ? 1 : undefined;
453 >
454 > // This class exists so that URI is compatible with vscode.Uri (API).
455 > class Uri extends URI { uri.ts ×9
456 >
457 > _formatted: string | null = null;
458 > _fsPath: string | null = null;
459 > map.ts ×97
460 > override get fsPath(): string {
461 > if (!this._fsPath) { uri.ts ×1
462 > this._fsPath = uriToFsPath(this, false);
463 > }
464 > return this._fsPath;
465 > }
466 > map.ts ×97
467 > override toString(skipEncoding: boolean = false): string {
468 > if (!skipEncoding) { uri.ts ×10
469 > if (!this._formatted) { uri.ts ×8
470 > this._formatted = _asFormatted(this, false);
471 > }
472 > return this._formatted;
473 > } else { uri.ts ×10
474 > // we don't cache that uri.ts ×5
475 > return _asFormatted(this, true);
476 > }
477 > } uri.ts ×10
478 > map.ts ×97
479 > override toJSON(): UriComponents {
480 > // eslint-disable-next-line local/code-no-dangerous-type-assertions uri.ts ×6
481 > const res = <UriState>{
482 > $mid: MarshalledId.Uri
483 > };
484 > // cached state
485 > if (this._fsPath) {
486 res.fsPath = this._fsPath;
487 res._sep = _pathSepMarker;
488 }
489 > if (this._formatted) { uri.ts ×6
490 > res.external = this._formatted; uri.ts ×1
491 > }
492 > //--- uri components uri.ts ×6
493 > if (this.path) {
494 > res.path = this.path;
495 > }
496 > // TODO
497 > // this isn't correct and can violate the UriComponents contract but
498 > // this is part of the vscode.Uri API and we shouldn't change how that
499 > // works anymore
500 > if (this.scheme) {
501 > res.scheme = this.scheme;
502 > }
503 > if (this.authority) {
504 > res.authority = this.authority; uri.ts ×1
505 > }
506 > if (this.query) { uri.ts ×6
507 > res.query = this.query; uri.ts ×1
508 > }
509 > if (this.fragment) { uri.ts ×6
510 > res.fragment = this.fragment; uri.ts ×1
511 > }
512 > return res; uri.ts ×6
513 > }
514 > } map.ts ×97
515 >
516 > // reserved characters: https://tools.ietf.org/html/rfc3986#section-2.2
517 > const encodeTable: { [ch: number]: string } = {
518 > [CharCode.Colon]: '%3A', // gen-delims
519 > [CharCode.Slash]: '%2F',
520 > [CharCode.QuestionMark]: '%3F',
521 > [CharCode.Hash]: '%23',
522 > [CharCode.OpenSquareBracket]: '%5B',
523 > [CharCode.CloseSquareBracket]: '%5D',
524 > [CharCode.AtSign]: '%40',
525 >
526 > [CharCode.ExclamationMark]: '%21', // sub-delims
527 > [CharCode.DollarSign]: '%24',
528 > [CharCode.Ampersand]: '%26',
529 > [CharCode.SingleQuote]: '%27',
530 > [CharCode.OpenParen]: '%28',
531 > [CharCode.CloseParen]: '%29',
532 > [CharCode.Asterisk]: '%2A',
533 > [CharCode.Plus]: '%2B',
534 > [CharCode.Comma]: '%2C',
535 > [CharCode.Semicolon]: '%3B',
536 > [CharCode.Equals]: '%3D',
537 >
538 > [CharCode.Space]: '%20',
539 > };
540 >
541 > function encodeURIComponentFast(uriComponent: string, isPath: boolean, isAuthority: boolean): string { uri.ts ×8
542 > let res: string | undefined = undefined;
543 > let nativeEncodePos = -1;
544 >
545 > for (let pos = 0; pos < uriComponent.length; pos++) {
546 > const code = uriComponent.charCodeAt(pos);
547 >
548 > // unreserved characters: https://tools.ietf.org/html/rfc3986#section-2.3
549 > if (
550 > (code >= CharCode.a && code <= CharCode.z)
551 > || (code >= CharCode.A && code <= CharCode.Z) uri.ts ×1
552 > || (code >= CharCode.Digit0 && code <= CharCode.Digit9)
553 > || code === CharCode.Dash uri.ts ×1
554 > || code === CharCode.Period uri.ts ×1
555 > || code === CharCode.Underline
556 > || code === CharCode.Tilde
557 > || (isPath && code === CharCode.Slash)
558 > || (isAuthority && code === CharCode.OpenSquareBracket) uri.ts ×5
559 > || (isAuthority && code === CharCode.CloseSquareBracket)
560 > || (isAuthority && code === CharCode.Colon)
561 > ) { uri.ts ×8
562 > // check if we are delaying native encode
563 > if (nativeEncodePos !== -1) {
564 > res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); uri.ts ×1
565 > nativeEncodePos = -1;
566 > }
567 > // check if we write into a new string (by default we try to return the param) uri.ts ×8
568 > if (res !== undefined) {
569 > res += uriComponent.charAt(pos); uri.ts ×5
570 > }
571 > uri.ts ×8
572 > } else {
573 > // encoding needed, we need to allocate a new string uri.ts ×5
574 > if (res === undefined) {
575 > res = uriComponent.substr(0, pos);
576 > }
577 >
578 > // check with default table first
579 > const escaped = encodeTable[code];
580 > if (escaped !== undefined) {
581 > uri.ts ×2
582 > // check if we are delaying native encode
583 > if (nativeEncodePos !== -1) {
584 > res += encodeURIComponent(uriComponent.substring(nativeEncodePos, pos)); uri.ts ×1
585 > nativeEncodePos = -1;
586 > }
587 > uri.ts ×2
588 > // append escaped variant to result
589 > res += escaped;
590 >
591 > } else if (nativeEncodePos === -1) { uri.ts ×5
592 > // use native encode only when needed uri.ts ×1
593 > nativeEncodePos = pos;
594 > }
595 > } uri.ts ×5
596 > } uri.ts ×8
597 >
598 > if (nativeEncodePos !== -1) {
599 > res += encodeURIComponent(uriComponent.substring(nativeEncodePos)); uri.ts ×1
600 > }
601 > uri.ts ×8
602 > return res !== undefined ? res : uriComponent;
603 > }
604 > map.ts ×97
605 > function encodeURIComponentMinimal(path: string): string { uri.ts ×5
606 > let res: string | undefined = undefined;
607 > for (let pos = 0; pos < path.length; pos++) {
608 > const code = path.charCodeAt(pos);
609 > if (code === CharCode.Hash || code === CharCode.QuestionMark) {
610 > if (res === undefined) { uri.ts ×2
611 > res = path.substr(0, pos);
612 > }
613 > res += encodeTable[code];
614 > } else { uri.ts ×5
615 > if (res !== undefined) {
616 > res += path[pos]; uri.ts ×2
617 > }
618 > } uri.ts ×5
619 > }
620 > return res !== undefined ? res : path;
621 > }
622 > map.ts ×97
623 > /**
624 > * Compute `fsPath` for the given uri
625 > */
626 > export function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string {
627 > uri.ts ×6
628 > let value: string;
629 > if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') {
630 > // unc path: file://shares/c$/far/boo uri.ts ×1
631 > value = `//${uri.authority}${uri.path}`;
632 > } else if (
633 > uri.path.charCodeAt(0) === CharCode.Slash uri.ts ×6
634 > && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)
635 > && uri.path.charCodeAt(2) === CharCode.Colon uri.ts ×1
636 > ) { uri.ts ×6
637 > if (!keepDriveLetterCasing) { uri.ts ×1
638 > // windows drive letter: file:///c:/far/boo
639 > value = uri.path[1].toLowerCase() + uri.path.substr(2);
640 > } else {
641 > value = uri.path.substr(1); labels.ts ×12
642 > }
643 > } else { uri.ts ×6
644 > // other path uri.ts ×1
645 > value = uri.path;
646 > }
647 > if (isWindows) { uri.ts ×6
648 value = value.replace(/\//g, '\\');
649 }
650 > return value; uri.ts ×6
651 > }
652 > map.ts ×97
653 > /**
654 > * Create the external version of a uri
655 > */
656 > function _asFormatted(uri: URI, skipEncoding: boolean): string { uri.ts ×10
657 >
658 > const encoder = !skipEncoding
659 > ? encodeURIComponentFast uri.ts ×8
660 > : encodeURIComponentMinimal; uri.ts ×5
661 > uri.ts ×10
662 > let res = '';
663 > let { scheme, authority, path, query, fragment } = uri;
664 > if (scheme) {
665 > res += scheme;
666 > res += ':';
667 > }
668 > if (authority || scheme === 'file') {
669 > res += _slash; uri.ts ×1
670 > res += _slash;
671 > }
672 > if (authority) { uri.ts ×10
673 > let idx = authority.indexOf('@'); uri.ts ×4
674 > if (idx !== -1) {
675 > // <user>@<auth> uri.ts ×2
676 > const userinfo = authority.substr(0, idx);
677 > authority = authority.substr(idx + 1);
678 > idx = userinfo.lastIndexOf(':');
679 > if (idx === -1) {
680 > res += encoder(userinfo, false, false);
681 > } else {
682 > // <user>:<pass>@<auth> uri.ts ×1
683 > res += encoder(userinfo.substr(0, idx), false, false);
684 > res += ':';
685 > res += encoder(userinfo.substr(idx + 1), false, true);
686 > }
687 > res += '@'; uri.ts ×2
688 > }
689 > authority = authority.toLowerCase(); uri.ts ×4
690 > idx = authority.lastIndexOf(':');
691 > if (idx === -1) {
692 > res += encoder(authority, false, true); uri.ts ×1
693 > } else { uri.ts ×4
694 > // <auth>:<port> uri.ts ×1
695 > res += encoder(authority.substr(0, idx), false, true);
696 > res += authority.substr(idx);
697 > }
698 > } uri.ts ×4
699 > if (path) { uri.ts ×10
700 > // lower-case windows drive letters in /C:/fff or C:/fff uri.ts ×3
701 > if (path.length >= 3 && path.charCodeAt(0) === CharCode.Slash && path.charCodeAt(2) === CharCode.Colon) {
702 > const code = path.charCodeAt(1); uri.ts ×1
703 > if (code >= CharCode.A && code <= CharCode.Z) {
704 > path = `/${String.fromCharCode(code + 32)}:${path.substr(3)}`; // "/c:".length === 3 uri.ts ×1
705 > }
706 > } else if (path.length >= 2 && path.charCodeAt(1) === CharCode.Colon) { uri.ts ×3
707 > const code = path.charCodeAt(0); uri.ts ×1
708 > if (code >= CharCode.A && code <= CharCode.Z) {
709 > path = `${String.fromCharCode(code + 32)}:${path.substr(2)}`; // "/c:".length === 3
710 > }
711 > }
712 > // encode the rest of the path uri.ts ×3
713 > res += encoder(path, true, false);
714 > }
715 > if (query) { uri.ts ×10
716 > res += '?'; uri.ts ×1
717 > res += encoder(query, false, false);
718 > }
719 > if (fragment) { uri.ts ×10
720 > res += '#'; uri.ts ×1
721 > res += !skipEncoding ? encodeURIComponentFast(fragment, false, false) : fragment;
722 > }
723 > return res; uri.ts ×10
724 > }
725 > map.ts ×97
726 > // --- decode
727 >
728 > function decodeURIComponentGraceful(str: string): string { uri.ts ×3
729 > try {
730 > return decodeURIComponent(str);
731 > } catch {
732 > if (str.length > 3) { uri.ts ×2
733 return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3));
734 > } else { uri.ts ×2
735 > return str;
736 > }
737 > }
738 > } uri.ts ×3
739 > map.ts ×97
740 > const _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
741 >
742 > function percentDecode(str: string): string { uri.ts ×3
743 > if (!str.match(_rEncodedAsHex)) {
744 > return str;
745 > }
746 > return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match)); uri.ts ×3
747 > }
748 > map.ts ×97
749 > /**
750 > * Mapped-type that replaces all occurrences of URI with UriComponents
751 > */
752 > export type UriDto<T> = { [K in keyof T]: T[K] extends URI
753 > ? UriComponents
754 > : UriDto<T[K]> };