fileService.ts ×56

Frontier kind: Code frontier

unlabeled · c_cbcfb59f2262

1515 tests · 11125 LOC · 44 files · introduces 0 tests · 181 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
56 ranges181 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1679 ranges11125 lines · 44 files · Browse complete extent
All tests (intent)
1515 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

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 native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 181 introduced LOC across 56 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/files/common/fileService.ts 181 introduced LOC · 56 ranges

Open complete file

34
35 constructor(@ILogService private readonly logService: ILogService) {
36 > super(); fileService.ts
37 > }
38 >
39 > //#region File System Provider
40 >
41 > private readonly _onDidChangeFileSystemProviderRegistrations = this._register(new Emitter<IFileSystemProviderRegistrationEvent>());
42 > readonly onDidChangeFileSystemProviderRegistrations = this._onDidChangeFileSystemProviderRegistrations.event;
43 >
44 > private readonly _onWillActivateFileSystemProvider = this._register(new Emitter<IFileSystemProviderActivationEvent>());
45 > readonly onWillActivateFileSystemProvider = this._onWillActivateFileSystemProvider.event;
46 >
47 > private readonly _onDidChangeFileSystemProviderCapabilities = this._register(new Emitter<IFileSystemProviderCapabilitiesChangeEvent>());
48 > readonly onDidChangeFileSystemProviderCapabilities = this._onDidChangeFileSystemProviderCapabilities.event;
49 >
50 > private readonly provider = new Map<string, IFileSystemProvider>();
51 >
52 > registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable {
53 if (this.provider.has(scheme)) {
54 throw new Error(`A filesystem provider for the scheme '${scheme}' is already registered.`);
87 });
88 }
90 > getProvider(scheme: string): IFileSystemProvider | undefined {
91 return this.provider.get(scheme);
92 }
94 > async activateProvider(scheme: string): Promise<void> {
95
96 // Emit an event that we are about to activate a provider with the given scheme.
112 await Promises.settled(joiners);
113 }
115 > async canHandleResource(resource: URI): Promise<boolean> {
116
117 // Await activation of potentially extension contributed providers
120 return this.hasProvider(resource);
121 }
123 > hasProvider(resource: URI): boolean {
124 return this.provider.has(resource.scheme);
125 }
127 > hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean {
128 const provider = this.provider.get(resource.scheme);
129
130 return !!(provider && (provider.capabilities & capability));
131 }
133 > listCapabilities(): Iterable<{ scheme: string; capabilities: FileSystemProviderCapabilities }> {
134 return Iterable.map(this.provider, ([scheme, provider]) => ({ scheme, capabilities: provider.capabilities }));
135 }
137 > protected async withProvider(resource: URI): Promise<IFileSystemProvider> {
138
139 // Assert path is absolute
156 return provider;
157 }
159 > private async withReadProvider(resource: URI): Promise<IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability> {
160 const provider = await this.withProvider(resource);
161
166 throw new Error(`Filesystem provider for scheme '${resource.scheme}' neither has FileReadWrite, FileReadStream nor FileOpenReadWriteClose capability which is needed for the read operation.`);
167 }
169 > private async withWriteProvider(resource: URI): Promise<IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability> {
170 const provider = await this.withProvider(resource);
171
176 throw new Error(`Filesystem provider for scheme '${resource.scheme}' neither has FileReadWrite nor FileOpenReadWriteClose capability which is needed for the write operation.`);
177 }
179 > //#endregion
180 >
181 > //#region Operation events
182 >
183 > private readonly _onDidRunOperation = this._register(new Emitter<FileOperationEvent>());
184 > readonly onDidRunOperation = this._onDidRunOperation.event;
185 >
186 > //#endregion
187 >
188 > //#region File Metadata Resolving
189 >
190 > async resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
191 > async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
192 > async resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
193 try {
194 return await this.doResolveFile(resource, options);
204 }
205 }
207 > private async doResolveFile(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
208 > private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
209 > private async doResolveFile(resource: URI, options?: IResolveFileOptions): Promise<IFileStat> {
210 const provider = await this.withProvider(resource);
211 const isPathCaseSensitive = this.isPathCaseSensitive(provider);
243 });
244 }
246 > private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat>;
247 > private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, siblings: number | undefined, resolveMetadata: true, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStatWithMetadata>;
248 > private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat> {
249 const { providerExtUri } = this.getExtUri(provider);
250
296 return fileStat;
297 }
299 > async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]>;
300 > async resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IFileStatResultWithMetadata[]>;
301 > async resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]> {
302 return Promises.settled(toResolve.map(async entry => {
303 try {
310 }));
311 }
313 > async stat(resource: URI): Promise<IFileStatWithPartialMetadata> {
314 const provider = await this.withProvider(resource);
315
318 return this.toFileStat(provider, resource, stat, undefined, true, () => false /* Do not resolve any children */);
319 }
321 > async realpath(resource: URI): Promise<URI | undefined> {
322 const provider = await this.withProvider(resource);
323
330 return undefined;
331 }
333 > async exists(resource: URI): Promise<boolean> {
334 const provider = await this.withProvider(resource);
335
342 }
343 }
345 > //#endregion
346 >
347 > //#region File Reading/Writing
348 >
349 > async canCreateFile(resource: URI, options?: ICreateFileOptions): Promise<Error | true> {
350 try {
351 await this.doValidateCreateFile(resource, options);
356 return true;
357 }
359 > private async doValidateCreateFile(resource: URI, options?: ICreateFileOptions): Promise<void> {
360
361 // validate overwrite
364 }
365 }
367 > async createFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream = VSBuffer.fromString(''), options?: ICreateFileOptions): Promise<IFileStatWithMetadata> {
368
369 // validate
378 return fileStat;
379 }
381 > async writeFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {
382 const provider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(resource), resource);
383 const { providerExtUri } = this.getExtUri(provider);
431 return this.resolve(resource, { resolveMetadata: true });
432 }
434 >
435 > private async peekBufferForWriting(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise<VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream> {
436 let peekResult: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream;
437 if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) {
452 return peekResult;
453 }
455 > private async validateWriteFile(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<{ stat: IStat | undefined; buffer: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream | undefined }> {
456
457 // Validate unlock support
537 return { stat, buffer };
538 }
540 > async readFile(resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise<IFileContent> {
541 const provider = await this.withReadProvider(resource);
542
547 return this.doReadFile(provider, resource, options, token);
548 }
550 > private async doReadFileAtomic(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability, resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise<IFileContent> {
551 return new Promise<IFileContent>((resolve, reject) => {
552 this.writeQueue.queueFor(resource, async () => {
560 });
561 }
563 > private async doReadFile(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability, resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise<IFileContent> {
564 const stream = await this.doReadFileStream(provider, resource, {
565 ...options,
577 };
578 }
580 > async readFileStream(resource: URI, options?: IReadFileStreamOptions, token?: CancellationToken): Promise<IFileStreamContent> {
581 const provider = await this.withReadProvider(resource);
582
583 return this.doReadFileStream(provider, resource, options, token);
584 }
586 > private async doReadFileStream(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability, resource: URI, options?: IReadFileOptions & IReadFileStreamOptions & { preferUnbuffered?: boolean }, token?: CancellationToken): Promise<IFileStreamContent> {
587
588 // install a cancellation token that gets cancelled
661 }
662 }
664 > private restoreReadError(error: Error, resource: URI, options?: IReadFileStreamOptions): FileOperationError {
665 const message = localize('err.read', "Unable to read file '{0}' ({1})", this.resourceForError(resource), ensureFileSystemProviderError(error).toString());
666
675 return new FileOperationError(message, toFileOperationResult(error), options);
676 }
678 > private readFileStreamed(provider: IFileSystemProviderWithFileReadStreamCapability, resource: URI, token: CancellationToken, options: IReadFileStreamOptions = Object.create(null)): VSBufferReadableStream {
679 const fileStream = provider.readFileStream(resource, options, token);
680
684 }, data => VSBuffer.concat(data));
685 }
687 > private readFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, token: CancellationToken, options: IReadFileStreamOptions = Object.create(null)): VSBufferReadableStream {
688 const stream = newWriteableBufferStream();
689
696 return stream;
697 }
699 > private readFileUnbuffered(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithFileAtomicReadCapability, resource: URI, options?: IReadFileOptions & IReadFileStreamOptions): VSBufferReadableStream {
700 const stream = newWriteableStream<VSBuffer>(data => VSBuffer.concat(data));
701
734 return stream;
735 }
737 > private async validateReadFile(resource: URI, options?: IReadFileStreamOptions): Promise<IFileStatWithMetadata> {
738 const stat = await this.resolve(resource, { resolveMetadata: true });
739
753 return stat;
754 }
756 > private validateReadFileLimits(resource: URI, size: number, options?: IReadFileStreamOptions): void {
757 if (typeof options?.limits?.size === 'number' && size > options.limits.size) {
758 throw new TooLargeFileOperationError(localize('fileTooLargeError', "Unable to read file '{0}' that is too large to open", this.resourceForError(resource)), FileOperationResult.FILE_TOO_LARGE, size, options);
759 }
760 }
762 > //#endregion
763 >
764 > //#region Move/Copy/Delete/Create Folder
765 >
766 > async canMove(source: URI, target: URI, overwrite?: boolean): Promise<Error | true> {
767 return this.doCanMoveCopy(source, target, 'move', overwrite);
768 }
770 > async canCopy(source: URI, target: URI, overwrite?: boolean): Promise<Error | true> {
771 return this.doCanMoveCopy(source, target, 'copy', overwrite);
772 }
774 > private async doCanMoveCopy(source: URI, target: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise<Error | true> {
775 if (source.toString() !== target.toString()) {
776 try {
786 return true;
787 }
789 > async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
790 const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source), source);
791 const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
800 return fileStat;
801 }
803 > async copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
804 const sourceProvider = await this.withReadProvider(source);
805 const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
814 return fileStat;
815 }
817 > private async doMoveCopy(sourceProvider: IFileSystemProvider, source: URI, targetProvider: IFileSystemProvider, target: URI, mode: 'move' | 'copy', overwrite: boolean): Promise<'move' | 'copy'> {
818 if (source.toString() === target.toString()) {
819 return mode; // simulate node.js behaviour here and do a no-op if paths match
872 }
873 }
875 > private async doCopyFile(sourceProvider: IFileSystemProvider, source: URI, targetProvider: IFileSystemProvider, target: URI): Promise<void> {
876
877 // copy: source (buffered) => target (buffered)
895 }
896 }
898 > private async doCopyFolder(sourceProvider: IFileSystemProvider, sourceFolder: IFileStat, targetProvider: IFileSystemProvider, targetFolder: URI): Promise<void> {
899
900 // create folder in target
913 }
914 }
916 > private async doValidateMoveCopy(sourceProvider: IFileSystemProvider, source: URI, targetProvider: IFileSystemProvider, target: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise<{ exists: boolean; isSameResourceWithDifferentPathCase: boolean }> {
917 let isSameResourceWithDifferentPathCase = false;
918
954 return { exists, isSameResourceWithDifferentPathCase };
955 }
957 > private getExtUri(provider: IFileSystemProvider): { providerExtUri: IExtUri; isPathCaseSensitive: boolean } {
958 const isPathCaseSensitive = this.isPathCaseSensitive(provider);
959
963 };
964 }
966 > private isPathCaseSensitive(provider: IFileSystemProvider): boolean {
967 return !!(provider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive);
968 }
970 > async createFolder(resource: URI): Promise<IFileStatWithMetadata> {
971 const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource), resource);
972
980 return fileStat;
981 }
983 > private async mkdirp(provider: IFileSystemProvider, directory: URI): Promise<void> {
984 const directoriesToCreate: string[] = [];
985
1030 }
1031 }
1033 > async canDelete(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<Error | true> {
1034 try {
1035 await this.doValidateDelete(resource, options);
1040 return true;
1041 }
1043 > private async doValidateDelete(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<IFileSystemProvider> {
1044 const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource), resource);
1045
1085 return provider;
1086 }
1088 > async del(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<void> {
1089 const provider = await this.doValidateDelete(resource, options);
1090
1107 this._onDidRunOperation.fire(new FileOperationEvent(resource, FileOperation.DELETE));
1108 }
1110 > //#endregion
1111 >
1112 > //#region Clone File
1113 >
1114 > async cloneFile(source: URI, target: URI): Promise<void> {
1115 const sourceProvider = await this.withProvider(source);
1116 const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
1142 return this.writeQueue.queueFor(source, () => this.doCopyFile(sourceProvider, source, targetProvider, target), this.getExtUri(sourceProvider).providerExtUri);
1143 }
1145 > //#endregion
1146 >
1147 > //#region File Watching
1148 >
1149 > private readonly internalOnDidFilesChange = this._register(new Emitter<FileChangesEvent>());
1150 >
1151 > private readonly _onDidUncorrelatedFilesChange = this._register(new Emitter<FileChangesEvent>());
1152 > readonly onDidFilesChange = this._onDidUncorrelatedFilesChange.event; // global `onDidFilesChange` skips correlated events
1153 >
1154 > private readonly _onDidWatchError = this._register(new Emitter<Error>());
1155 > readonly onDidWatchError = this._onDidWatchError.event;
1156 >
1157 > private readonly activeWatchers = new Map<number /* watch request hash */, { disposable: IDisposable; count: number }>();
1158 >
1159 > private static WATCHER_CORRELATION_IDS = 0;
1160 >
1161 > createWatcher(resource: URI, options: IWatchOptionsWithoutCorrelation & { recursive: false }): IFileSystemWatcher {
1162 return this.watch(resource, {
1163 ...options,
1168 });
1169 }
1171 > watch(resource: URI, options: IWatchOptionsWithCorrelation): IFileSystemWatcher;
1172 > watch(resource: URI, options?: IWatchOptionsWithoutCorrelation): IDisposable;
1173 > watch(resource: URI, options: IWatchOptions = { recursive: false, excludes: [] }): IFileSystemWatcher | IDisposable {
1174 const disposables = new DisposableStore();
1175
1215 return disposables;
1216 }
1218 > private async doWatch(resource: URI, options: IWatchOptions): Promise<IDisposable> {
1219 const provider = await this.withProvider(resource);
1220
1248 });
1249 }
1251 > override dispose(): void {
1252 super.dispose();
1253
1258 this.activeWatchers.clear();
1259 }
1261 > //#endregion
1262 >
1263 > //#region Helpers
1264 >
1265 > private readonly writeQueue = this._register(new ResourceQueue());
1266
1267 private async doWriteBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, options: IWriteFileOptions | undefined, readableOrStreamOrBufferedStream: VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream): Promise<void> {