src/vs/platform/files/common/inMemoryFilesystemProvider.ts

358 LOC · 322 covered · 36 uncovered · 74 ranges · 3735 concepts · 18 introducers · 2029 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 > /*--------------------------------------------------------------------------------------------- inMemoryFilesystemProvider.ts ×7
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 { VSBuffer } from '../../../base/common/buffer.js';
7 > import { Emitter, Event } from '../../../base/common/event.js';
8 > import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
9 > import * as resources from '../../../base/common/resources.js';
10 > import { ReadableStreamEvents, newWriteableStream } from '../../../base/common/stream.js';
11 > import { URI } from '../../../base/common/uri.js';
12 > import { FileChangeType, IFileDeleteOptions, IFileOverwriteOptions, FileSystemProviderCapabilities, FileSystemProviderErrorCode, FileType, IFileWriteOptions, IFileChange, IFileSystemProviderWithFileReadWriteCapability, IStat, IWatchOptions, createFileSystemProviderError, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileOpenOptions, IFileSystemProviderWithFileAtomicDeleteCapability, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileAtomicWriteCapability, IFileSystemProviderWithFileReadStreamCapability, isFileOpenForWriteOptions } from './files.js';
13 >
14 > class File implements IStat {
15 >
16 > readonly type: FileType.File;
17 > readonly ctime: number;
18 > mtime: number;
19 > size: number;
20 >
21 > name: string;
22 > data?: Uint8Array;
23 >
24 > constructor(name: string) {
25 > this.type = FileType.File; inMemoryFilesystemProvider.ts ×8
26 > this.ctime = Date.now();
27 > this.mtime = Date.now();
28 > this.size = 0;
29 > this.name = name;
30 > }
32 >
33 > class Directory implements IStat {
34 >
35 > readonly type: FileType.Directory;
36 > readonly ctime: number;
37 > mtime: number;
38 > size: number;
39 >
40 > name: string;
41 > readonly entries: Map<string, File | Directory>;
42 >
43 > constructor(name: string) {
44 > this.type = FileType.Directory; inMemoryFilesystemProvider.ts ×20
45 > this.ctime = Date.now();
46 > this.mtime = Date.now();
47 > this.size = 0;
48 > this.name = name;
49 > this.entries = new Map();
50 > }
52 >
53 > type Entry = File | Directory;
54 >
55 > export class InMemoryFileSystemProvider extends Disposable implements
56 > IFileSystemProviderWithFileReadWriteCapability,
57 > IFileSystemProviderWithOpenReadWriteCloseCapability,
58 > IFileSystemProviderWithFileReadStreamCapability,
59 > IFileSystemProviderWithFileAtomicReadCapability,
60 > IFileSystemProviderWithFileAtomicWriteCapability,
61 > IFileSystemProviderWithFileAtomicDeleteCapability {
63 > private memoryFdCounter = 0;
64 > private readonly fdMemory = new Map<number, { file: File; resource: URI; append: boolean; write: boolean }>();
65 > private _onDidChangeCapabilities = this._register(new Emitter<void>());
66 > readonly onDidChangeCapabilities = this._onDidChangeCapabilities.event;
67 >
68 > private _capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileOpenReadWriteClose | FileSystemProviderCapabilities.FileAppend | FileSystemProviderCapabilities.PathCaseSensitive;
69 > get capabilities(): FileSystemProviderCapabilities { return this._capabilities; }
70 >
71 > setReadOnly(readonly: boolean) {
72 const isReadonly = !!(this._capabilities & FileSystemProviderCapabilities.Readonly);
73 if (readonly !== isReadonly) {
74 this._capabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.FileAppend | FileSystemProviderCapabilities.PathCaseSensitive | (readonly ? FileSystemProviderCapabilities.Readonly : 0);
75 this._onDidChangeCapabilities.fire();
76 }
77 }
79 > root = new Directory('');
80 >
81 > // --- manage file metadata
82 >
83 > async stat(resource: URI): Promise<IStat> {
84 > return this._lookup(resource, false); inMemoryFilesystemProvider.ts ×4
85 > }
87 > async readdir(resource: URI): Promise<[string, FileType][]> {
88 > const entry = this._lookupAsDirectory(resource, false); inMemoryFilesystemProvider.ts ×1
89 > const result: [string, FileType][] = [];
90 > entry.entries.forEach((child, name) => result.push([name, child.type]));
91 > return result;
92 > }
94 > // --- manage file contents
95 >
96 > async readFile(resource: URI): Promise<Uint8Array> {
97 > const data = this._lookupAsFile(resource, false).data; inMemoryFilesystemProvider.ts ×4
98 > if (data) {
100 > }
101 throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
104 > readFileStream(resource: URI): ReadableStreamEvents<Uint8Array> {
105 const data = this._lookupAsFile(resource, false).data;
106
107 const stream = newWriteableStream<Uint8Array>(data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer);
108 stream.end(data);
109
110 return stream;
111 }
113 > async writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void> {
114 > const basename = resources.basename(resource); inMemoryFilesystemProvider.ts ×8
115 > const parent = this._lookupParentDirectory(resource);
116 > let entry = parent.entries.get(basename);
117 > if (entry instanceof Directory) {
118 throw createFileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory);
119 }
120 > if (!entry && !opts.create) { inMemoryFilesystemProvider.ts ×8
121 throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
122 }
123 > if (entry && opts.create && !opts.overwrite) { inMemoryFilesystemProvider.ts ×8
124 throw createFileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
125 }
126 > if (!entry) { inMemoryFilesystemProvider.ts ×8
127 > entry = new File(basename);
128 > parent.entries.set(basename, entry);
129 > this._fireSoon({ type: FileChangeType.ADDED, resource });
130 > }
131 > entry.mtime = Date.now();
132 >
133 > if (opts.append) {
134 > entry.size += content.byteLength; ahpJsonlLogger.ts ×15
135 > const oldData = entry.data ?? new Uint8Array(0);
136 > const newData = new Uint8Array(oldData.byteLength + content.byteLength);
137 > newData.set(oldData, 0);
138 > newData.set(content, oldData.byteLength);
139 > entry.data = newData;
141 > entry.size = content.byteLength; inMemoryFilesystemProvider.ts ×1
142 > entry.data = content;
143 > }
145 > this._fireSoon({ type: FileChangeType.UPDATED, resource });
146 > }
148 > // file open/read/write/close
149 > open(resource: URI, opts: IFileOpenOptions): Promise<number> {
150 > let file = this._lookup(resource, true); fileService.ts ×15
151 > const write = isFileOpenForWriteOptions(opts);
152 > const append = write && !!opts.append;
153 >
154 > if (!file) {
155 > if (!write) {
156 throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
157 }
158 > // Create the file if opening for write fileService.ts ×15
159 > const basename = resources.basename(resource);
160 > const parent = this._lookupParentDirectory(resource);
161 > file = new File(basename);
162 > file.data = new Uint8Array(0);
163 > parent.entries.set(basename, file);
164 > this._fireSoon({ type: FileChangeType.ADDED, resource });
165 > } else if (file instanceof Directory) {
166 throw createFileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory);
167 }
169 > if (!file.data) {
170 file.data = new Uint8Array(0);
171 }
173 > const fd = this.memoryFdCounter++;
174 > this.fdMemory.set(fd, { file, resource, write, append });
175 > return Promise.resolve(fd);
176 > }
178 > close(fd: number): Promise<void> {
179 > const fdData = this.fdMemory.get(fd); fileService.ts ×15
180 > if (fdData?.write) {
181 > // Update file metadata on close
182 > fdData.file.mtime = Date.now();
183 > fdData.file.size = fdData.file.data?.byteLength ?? 0;
184 > this._fireSoon({ type: FileChangeType.UPDATED, resource: fdData.resource });
185 > }
186 > this.fdMemory.delete(fd);
187 > return Promise.resolve();
188 > }
190 > read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
191 > const fdData = this.fdMemory.get(fd); fileService.ts ×15
192 > if (!fdData) {
193 throw createFileSystemProviderError(`No file with that descriptor open`, FileSystemProviderErrorCode.Unavailable);
194 }
196 > if (!fdData.file.data) {
197 return Promise.resolve(0);
198 }
200 > const toWrite = VSBuffer.wrap(fdData.file.data).slice(pos, pos + length);
201 > data.set(toWrite.buffer, offset);
202 > return Promise.resolve(toWrite.byteLength);
203 > }
205 > write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
206 > const fdData = this.fdMemory.get(fd); fileService.ts ×15
207 > if (!fdData) {
208 throw createFileSystemProviderError(`No file with that descriptor open`, FileSystemProviderErrorCode.Unavailable);
209 }
211 > const toWrite = VSBuffer.wrap(data).slice(offset, offset + length);
212 > fdData.file.data ??= new Uint8Array(0);
213 >
214 > // In append mode, always write at the end
215 > const writePos = fdData.append ? fdData.file.data.byteLength : pos;
216 >
217 > // Grow the buffer if needed
218 > const endPos = writePos + toWrite.byteLength;
219 > if (endPos > fdData.file.data.byteLength) {
220 > const newData = new Uint8Array(endPos);
221 > newData.set(fdData.file.data, 0);
222 > fdData.file.data = newData;
223 > }
224 >
225 > fdData.file.data.set(toWrite.buffer, writePos);
226 > return Promise.resolve(toWrite.byteLength);
227 > }
229 > // --- manage files/folders
230 >
231 > async rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void> {
232 > if (!opts.overwrite && this._lookup(to, true)) { extensionDownloader.ts ×10
233 throw createFileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
234 }
236 > const entry = this._lookup(from, false);
237 > const oldParent = this._lookupParentDirectory(from);
238 >
239 > const newParent = this._lookupParentDirectory(to);
240 > const newName = resources.basename(to);
241 >
242 > oldParent.entries.delete(entry.name);
243 > entry.name = newName;
244 > newParent.entries.set(newName, entry);
245 >
246 > this._fireSoon(
247 > { type: FileChangeType.DELETED, resource: from },
248 > { type: FileChangeType.ADDED, resource: to }
249 > );
250 > }
252 > async delete(resource: URI, opts: IFileDeleteOptions): Promise<void> {
253 > const dirname = resources.dirname(resource); inMemoryFilesystemProvider.ts ×1
254 > const basename = resources.basename(resource);
255 > const parent = this._lookupAsDirectory(dirname, false);
256 > if (parent.entries.delete(basename)) {
257 > parent.mtime = Date.now();
258 > parent.size -= 1;
259 > this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED });
260 > }
261 > }
263 > async mkdir(resource: URI): Promise<void> {
264 > if (this._lookup(resource, true)) { inMemoryFilesystemProvider.ts ×3
265 > throw createFileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists); fileService.ts ×2
266 > }
268 > const basename = resources.basename(resource);
269 > const dirname = resources.dirname(resource);
270 > const parent = this._lookupAsDirectory(dirname, false);
271 >
272 > const entry = new Directory(basename);
273 > parent.entries.set(entry.name, entry);
274 > parent.mtime = Date.now();
275 > parent.size += 1;
276 > this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { type: FileChangeType.ADDED, resource });
277 > }
279 > // --- lookup
280 >
281 > private _lookup(uri: URI, silent: false): Entry;
282 > private _lookup(uri: URI, silent: boolean): Entry | undefined;
283 > private _lookup(uri: URI, silent: boolean): Entry | undefined {
284 > const parts = uri.path.split('/'); inMemoryFilesystemProvider.ts ×4
285 > let entry: Entry = this.root;
286 > for (const part of parts) {
287 > if (!part) {
288 > continue;
289 > }
290 > let child: Entry | undefined;
291 > if (entry instanceof Directory) {
292 > child = entry.entries.get(part);
293 > }
294 > if (!child) {
295 > if (!silent) {
296 > throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
297 > } else {
298 > return undefined; inMemoryFilesystemProvider.ts ×3
299 > }
301 > entry = child; inMemoryFilesystemProvider.ts ×6
302 > }
303 > return entry;
306 > private _lookupAsDirectory(uri: URI, silent: boolean): Directory {
307 > const entry = this._lookup(uri, silent); inMemoryFilesystemProvider.ts ×6
308 > if (entry instanceof Directory) {
309 > return entry;
310 > }
311 throw createFileSystemProviderError('file not a directory', FileSystemProviderErrorCode.FileNotADirectory);
314 > private _lookupAsFile(uri: URI, silent: boolean): File {
315 > const entry = this._lookup(uri, silent); inMemoryFilesystemProvider.ts ×4
316 > if (entry instanceof File) {
317 > return entry; inMemoryFilesystemProvider.ts ×2
318 > }
319 throw createFileSystemProviderError('file is a directory', FileSystemProviderErrorCode.FileIsADirectory);
322 > private _lookupParentDirectory(uri: URI): Directory {
323 > const dirname = resources.dirname(uri); inMemoryFilesystemProvider.ts ×8
324 > return this._lookupAsDirectory(dirname, false);
325 > }
327 > // --- manage file events
328 >
329 > private readonly _onDidChangeFile = this._register(new Emitter<readonly IFileChange[]>());
330 > readonly onDidChangeFile: Event<readonly IFileChange[]> = this._onDidChangeFile.event;
331 >
332 > private _bufferedChanges: IFileChange[] = [];
333 > private _fireSoonHandle?: Timeout; inMemoryFilesystemProvider.ts ×7
334 >
335 > watch(resource: URI, opts: IWatchOptions): IDisposable {
336 > // ignore, fires for all changes... fileService.ts ×3
337 > return Disposable.None;
338 > }
340 > private _fireSoon(...changes: IFileChange[]): void {
341 > this._bufferedChanges.push(...changes); inMemoryFilesystemProvider.ts ×6
342 >
343 > if (this._fireSoonHandle) {
344 > clearTimeout(this._fireSoonHandle); inMemoryFilesystemProvider.ts ×1
345 > }
347 > this._fireSoonHandle = setTimeout(() => {
348 > this._onDidChangeFile.fire(this._bufferedChanges); inMemoryFilesystemProvider.ts ×1
349 > this._bufferedChanges.length = 0;
351 > }
353 > override dispose(): void {
354 > super.dispose(); inMemoryFilesystemProvider.ts ×20
355 >
356 > this.fdMemory.clear();
357 > }