src/vs/platform/uriIdentity/common/uriIdentityService.ts
128 LOC · 113 covered · 15 uncovered · 18 ranges · 812 concepts · 6 introducers · 494 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.
/*---------------------------------------------------------------------------------------------
uriIdentityService.ts ×6
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUriIdentityService } from './uriIdentity.js';
import { URI } from '../../../base/common/uri.js';
import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js';
import { IFileService, FileSystemProviderCapabilities, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent } from '../../files/common/files.js';
import { ExtUri, IExtUri, normalizePath } from '../../../base/common/resources.js';
import { Event } from '../../../base/common/event.js';
import { DisposableStore } from '../../../base/common/lifecycle.js';
import { quickSelect } from '../../../base/common/arrays.js';
class Entry {
static _clock = 0;
time: number = Entry._clock++;
constructor(readonly uri: URI) { }
touch() {
return this;
}
export class UriIdentityService implements IUriIdentityService {
declare readonly _serviceBrand: undefined;
readonly extUri: IExtUri;
private readonly _dispooables = new DisposableStore();
private readonly _canonicalUris: Map<string, Entry>;
private readonly _limit = 2 ** 16;
constructor(@IFileService private readonly _fileService: IFileService) {
const schemeIgnoresPathCasingCache = new Map<string, boolean>();
// assume path casing matters unless the file system provider spec'ed the opposite.
// for all other cases path casing matters, e.g for
// * virtual documents
// * in-memory uris
// * all kind of "private" schemes
const ignorePathCasing = (uri: URI): boolean => {
if (ignorePathCasing === undefined) {
// retrieve once and then case per scheme until a change happens
ignorePathCasing = _fileService.hasProvider(uri) && !this._fileService.hasCapability(uri, FileSystemProviderCapabilities.PathCaseSensitive);
schemeIgnoresPathCasingCache.set(uri.scheme, ignorePathCasing);
}
return ignorePathCasing;
};
this._dispooables.add(Event.any<IFileSystemProviderCapabilitiesChangeEvent | IFileSystemProviderRegistrationEvent>(
uriIdentityService.ts ×4
_fileService.onDidChangeFileSystemProviderRegistrations,
_fileService.onDidChangeFileSystemProviderCapabilities
)(e => {
const oldIgnorePathCasingValue = schemeIgnoresPathCasingCache.get(e.scheme);
if (oldIgnorePathCasingValue === undefined) {
return;
}
schemeIgnoresPathCasingCache.delete(e.scheme);
const newIgnorePathCasingValue = ignorePathCasing(URI.from({ scheme: e.scheme }));
if (newIgnorePathCasingValue === newIgnorePathCasingValue) {
return;
}
for (const [key, entry] of this._canonicalUris.entries()) {
if (entry.uri.scheme !== e.scheme) {
continue;
}
this._canonicalUris.delete(key);
}
this.extUri = new ExtUri(ignorePathCasing);
this._canonicalUris = new Map();
}
dispose(): void {
this._canonicalUris.clear();
}
asCanonicalUri(uri: URI): URI {
// (1) normalize URI
if (this._fileService.hasProvider(uri)) {
uri = normalizePath(uri);
}
// (2) find the uri in its canonical form or use this uri to define it
const uriKey = this.extUri.getComparisonKey(uri, true);
const item = this._canonicalUris.get(uriKey);
if (item) {
}
// this uri is first and defines the canonical form
this._canonicalUris.set(uriKey, new Entry(uri));
this._checkTrim();
return uri;
}
private _checkTrim(): void {
return;
}
Entry._clock = 1;
const times = [...this._canonicalUris.values()].map(e => e.time);
const median = quickSelect(
Math.floor(times.length / 2),
times,
(a, b) => a - b);
for (const [key, entry] of this._canonicalUris.entries()) {
// Its important to remove the median value here (<= not <).
// If we have not touched any items since the last trim, the
// median will be 0 and no items will be removed otherwise.
if (entry.time <= median) {
this._canonicalUris.delete(key);
} else {
entry.time = 0;
}
}
registerSingleton(IUriIdentityService, UriIdentityService, InstantiationType.Delayed);