Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/files/common/files.ts
5243 views
1
/*---------------------------------------------------------------------------------------------
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, VSBufferReadable, VSBufferReadableStream } from '../../../base/common/buffer.js';
7
import { CancellationToken } from '../../../base/common/cancellation.js';
8
import { Event } from '../../../base/common/event.js';
9
import { IExpression, IRelativePattern } from '../../../base/common/glob.js';
10
import { IDisposable } from '../../../base/common/lifecycle.js';
11
import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
12
import { sep } from '../../../base/common/path.js';
13
import { ReadableStreamEvents } from '../../../base/common/stream.js';
14
import { startsWithIgnoreCase } from '../../../base/common/strings.js';
15
import { isNumber } from '../../../base/common/types.js';
16
import { URI } from '../../../base/common/uri.js';
17
import { localize } from '../../../nls.js';
18
import { createDecorator } from '../../instantiation/common/instantiation.js';
19
import { isWeb } from '../../../base/common/platform.js';
20
import { Schemas } from '../../../base/common/network.js';
21
import { IMarkdownString } from '../../../base/common/htmlContent.js';
22
import { Lazy } from '../../../base/common/lazy.js';
23
24
//#region file service & providers
25
26
export const IFileService = createDecorator<IFileService>('fileService');
27
28
export interface IFileService {
29
30
readonly _serviceBrand: undefined;
31
32
/**
33
* An event that is fired when a file system provider is added or removed
34
*/
35
readonly onDidChangeFileSystemProviderRegistrations: Event<IFileSystemProviderRegistrationEvent>;
36
37
/**
38
* An event that is fired when a registered file system provider changes its capabilities.
39
*/
40
readonly onDidChangeFileSystemProviderCapabilities: Event<IFileSystemProviderCapabilitiesChangeEvent>;
41
42
/**
43
* An event that is fired when a file system provider is about to be activated. Listeners
44
* can join this event with a long running promise to help in the activation process.
45
*/
46
readonly onWillActivateFileSystemProvider: Event<IFileSystemProviderActivationEvent>;
47
48
/**
49
* Registers a file system provider for a certain scheme.
50
*/
51
registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable;
52
53
/**
54
* Returns a file system provider for a certain scheme.
55
*/
56
getProvider(scheme: string): IFileSystemProvider | undefined;
57
58
/**
59
* Tries to activate a provider with the given scheme.
60
*/
61
activateProvider(scheme: string): Promise<void>;
62
63
/**
64
* Checks if this file service can handle the given resource by
65
* first activating any extension that wants to be activated
66
* on the provided resource scheme to include extensions that
67
* contribute file system providers for the given resource.
68
*/
69
canHandleResource(resource: URI): Promise<boolean>;
70
71
/**
72
* Checks if the file service has a registered provider for the
73
* provided resource.
74
*
75
* Note: this does NOT account for contributed providers from
76
* extensions that have not been activated yet. To include those,
77
* consider to call `await fileService.canHandleResource(resource)`.
78
*/
79
hasProvider(resource: URI): boolean;
80
81
/**
82
* Checks if the provider for the provided resource has the provided file system capability.
83
*/
84
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean;
85
86
/**
87
* List the schemes and capabilities for registered file system providers
88
*/
89
listCapabilities(): Iterable<{ scheme: string; capabilities: FileSystemProviderCapabilities }>;
90
91
/**
92
* Allows to listen for file changes. The event will fire for every file within the opened workspace
93
* (if any) as well as all files that have been watched explicitly using the #watch() API.
94
*/
95
readonly onDidFilesChange: Event<FileChangesEvent>;
96
97
/**
98
* An event that is fired upon successful completion of a certain file operation.
99
*/
100
readonly onDidRunOperation: Event<FileOperationEvent>;
101
102
/**
103
* Resolve the properties of a file/folder identified by the resource. For a folder, children
104
* information is resolved as well depending on the provided options. Use `stat()` method if
105
* you do not need children information.
106
*
107
* If the optional parameter "resolveTo" is specified in options, the stat service is asked
108
* to provide a stat object that should contain the full graph of folders up to all of the
109
* target resources.
110
*
111
* If the optional parameter "resolveSingleChildDescendants" is specified in options,
112
* the stat service is asked to automatically resolve child folders that only
113
* contain a single element.
114
*
115
* If the optional parameter "resolveMetadata" is specified in options,
116
* the stat will contain metadata information such as size, mtime and etag.
117
*/
118
resolve(resource: URI, options: IResolveMetadataFileOptions): Promise<IFileStatWithMetadata>;
119
resolve(resource: URI, options?: IResolveFileOptions): Promise<IFileStat>;
120
121
/**
122
* Same as `resolve()` but supports resolving multiple resources in parallel.
123
*
124
* If one of the resolve targets fails to resolve returns a fake `IFileStat` instead of
125
* making the whole call fail.
126
*/
127
resolveAll(toResolve: { resource: URI; options: IResolveMetadataFileOptions }[]): Promise<IFileStatResult[]>;
128
resolveAll(toResolve: { resource: URI; options?: IResolveFileOptions }[]): Promise<IFileStatResult[]>;
129
130
/**
131
* Same as `resolve()` but without resolving the children of a folder if the
132
* resource is pointing to a folder.
133
*/
134
stat(resource: URI): Promise<IFileStatWithPartialMetadata>;
135
136
/**
137
* Attempts to resolve the real path of the provided resource. The real path can be
138
* different from the resource path for example when it is a symlink.
139
*
140
* Will return `undefined` if the real path cannot be resolved.
141
*/
142
realpath(resource: URI): Promise<URI | undefined>;
143
144
/**
145
* Finds out if a file/folder identified by the resource exists.
146
*/
147
exists(resource: URI): Promise<boolean>;
148
149
/**
150
* Read the contents of the provided resource unbuffered.
151
*/
152
readFile(resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise<IFileContent>;
153
154
/**
155
* Read the contents of the provided resource buffered as stream.
156
*/
157
readFileStream(resource: URI, options?: IReadFileStreamOptions, token?: CancellationToken): Promise<IFileStreamContent>;
158
159
/**
160
* Updates the content replacing its previous value.
161
* If `options.append` is true, appends content to the end of the file instead.
162
*
163
* Emits a `FileOperation.WRITE` file operation event when successful.
164
*/
165
writeFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
166
167
/**
168
* Moves the file/folder to a new path identified by the resource.
169
*
170
* The optional parameter overwrite can be set to replace an existing file at the location.
171
*
172
* Emits a `FileOperation.MOVE` file operation event when successful.
173
*/
174
move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
175
176
/**
177
* Find out if a move operation is possible given the arguments. No changes on disk will
178
* be performed. Returns an Error if the operation cannot be done.
179
*/
180
canMove(source: URI, target: URI, overwrite?: boolean): Promise<Error | true>;
181
182
/**
183
* Copies the file/folder to a path identified by the resource. A folder is copied
184
* recursively.
185
*
186
* Emits a `FileOperation.COPY` file operation event when successful.
187
*/
188
copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
189
190
/**
191
* Find out if a copy operation is possible given the arguments. No changes on disk will
192
* be performed. Returns an Error if the operation cannot be done.
193
*/
194
canCopy(source: URI, target: URI, overwrite?: boolean): Promise<Error | true>;
195
196
/**
197
* Clones a file to a path identified by the resource. Folders are not supported.
198
*
199
* If the target path exists, it will be overwritten.
200
*/
201
cloneFile(source: URI, target: URI): Promise<void>;
202
203
/**
204
* Creates a new file with the given path and optional contents. The returned promise
205
* will have the stat model object as a result.
206
*
207
* The optional parameter content can be used as value to fill into the new file.
208
*
209
* Emits a `FileOperation.CREATE` file operation event when successful.
210
*/
211
createFile(resource: URI, bufferOrReadableOrStream?: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: ICreateFileOptions): Promise<IFileStatWithMetadata>;
212
213
/**
214
* Find out if a file create operation is possible given the arguments. No changes on disk will
215
* be performed. Returns an Error if the operation cannot be done.
216
*/
217
canCreateFile(resource: URI, options?: ICreateFileOptions): Promise<Error | true>;
218
219
/**
220
* Creates a new folder with the given path. The returned promise
221
* will have the stat model object as a result.
222
*
223
* Emits a `FileOperation.CREATE` file operation event when successful.
224
*/
225
createFolder(resource: URI): Promise<IFileStatWithMetadata>;
226
227
/**
228
* Deletes the provided file. The optional useTrash parameter allows to
229
* move the file to trash. The optional recursive parameter allows to delete
230
* non-empty folders recursively.
231
*
232
* Emits a `FileOperation.DELETE` file operation event when successful.
233
*/
234
del(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<void>;
235
236
/**
237
* Find out if a delete operation is possible given the arguments. No changes on disk will
238
* be performed. Returns an Error if the operation cannot be done.
239
*/
240
canDelete(resource: URI, options?: Partial<IFileDeleteOptions>): Promise<Error | true>;
241
242
/**
243
* An event that signals an error when watching for file changes.
244
*/
245
readonly onDidWatchError: Event<Error>;
246
247
/**
248
* Allows to start a watcher that reports file/folder change events on the provided resource.
249
*
250
* The watcher runs correlated and thus, file events will be reported on the returned
251
* `IFileSystemWatcher` and not on the generic `IFileService.onDidFilesChange` event.
252
*
253
* Note: only non-recursive file watching supports event correlation for now.
254
*/
255
createWatcher(resource: URI, options: IWatchOptionsWithoutCorrelation & { recursive: false }): IFileSystemWatcher;
256
257
/**
258
* Allows to start a watcher that reports file/folder change events on the provided resource.
259
*
260
* The watcher runs uncorrelated and thus will report all events from `IFileService.onDidFilesChange`.
261
* This means, most listeners in the application will receive your events. It is encouraged to
262
* use correlated watchers (via `IWatchOptionsWithCorrelation`) to limit events to your listener.
263
*/
264
watch(resource: URI, options?: IWatchOptionsWithoutCorrelation): IDisposable;
265
266
/**
267
* Frees up any resources occupied by this service.
268
*/
269
dispose(): void;
270
}
271
272
export interface IFileOverwriteOptions {
273
274
/**
275
* Set to `true` to overwrite a file if it exists. Will
276
* throw an error otherwise if the file does exist.
277
*/
278
readonly overwrite: boolean;
279
}
280
281
export interface IFileUnlockOptions {
282
283
/**
284
* Set to `true` to try to remove any write locks the file might
285
* have. A file that is write locked will throw an error for any
286
* attempt to write to unless `unlock: true` is provided.
287
*/
288
readonly unlock: boolean;
289
}
290
291
export interface IFileAtomicReadOptions {
292
293
/**
294
* The optional `atomic` flag can be used to make sure
295
* the `readFile` method is not running in parallel with
296
* any `write` operations in the same process.
297
*
298
* Typically you should not need to use this flag but if
299
* for example you are quickly reading a file right after
300
* a file event occurred and the file changes a lot, there
301
* is a chance that a read returns an empty or partial file
302
* because a pending write has not finished yet.
303
*
304
* Note: this does not prevent the file from being written
305
* to from a different process. If you need such atomic
306
* operations, you better use a real database as storage.
307
*/
308
readonly atomic: boolean;
309
}
310
311
export interface IFileAtomicOptions {
312
313
/**
314
* The postfix is used to create a temporary file based
315
* on the original resource. The resulting temporary
316
* file will be in the same folder as the resource and
317
* have `postfix` appended to the resource name.
318
*
319
* Example: given a file resource `file:///some/path/foo.txt`
320
* and a postfix `.vsctmp`, the temporary file will be
321
* created as `file:///some/path/foo.txt.vsctmp`.
322
*/
323
readonly postfix: string;
324
}
325
326
export interface IFileAtomicWriteOptions {
327
328
/**
329
* The optional `atomic` flag can be used to make sure
330
* the `writeFile` method updates the target file atomically
331
* by first writing to a temporary file in the same folder
332
* and then renaming it over the target.
333
*/
334
readonly atomic: IFileAtomicOptions | false;
335
}
336
337
export interface IFileAtomicDeleteOptions {
338
339
/**
340
* The optional `atomic` flag can be used to make sure
341
* the `delete` method deletes the target atomically by
342
* first renaming it to a temporary resource in the same
343
* folder and then deleting it.
344
*/
345
readonly atomic: IFileAtomicOptions | false;
346
}
347
348
export interface IFileReadLimits {
349
350
/**
351
* If the file exceeds the given size, an error of kind
352
* `FILE_TOO_LARGE` will be thrown.
353
*/
354
size?: number;
355
}
356
357
export interface IFileReadStreamOptions {
358
359
/**
360
* Is an integer specifying where to begin reading from in the file. If position is undefined,
361
* data will be read from the current file position.
362
*/
363
readonly position?: number;
364
365
/**
366
* Is an integer specifying how many bytes to read from the file. By default, all bytes
367
* will be read.
368
*/
369
readonly length?: number;
370
371
/**
372
* If provided, the size of the file will be checked against the limits
373
* and an error will be thrown if any limit is exceeded.
374
*/
375
readonly limits?: IFileReadLimits;
376
}
377
378
export interface IFileWriteOptions extends IFileOverwriteOptions, IFileUnlockOptions, IFileAtomicWriteOptions {
379
380
/**
381
* Set to `true` to create a file when it does not exist. Will
382
* throw an error otherwise if the file does not exist.
383
*/
384
readonly create: boolean;
385
386
/**
387
* Set to `true` to append content to the end of the file. Implies `create: true`,
388
* and set only when the corresponding `FileAppend` capability is defined.
389
*/
390
readonly append?: boolean;
391
}
392
393
export type IFileOpenOptions = IFileOpenForReadOptions | IFileOpenForWriteOptions;
394
395
export function isFileOpenForWriteOptions(options: IFileOpenOptions): options is IFileOpenForWriteOptions {
396
return options.create === true;
397
}
398
399
export interface IFileOpenForReadOptions {
400
401
/**
402
* A hint that the file should be opened for reading only.
403
*/
404
readonly create: false;
405
}
406
407
export interface IFileOpenForWriteOptions extends IFileUnlockOptions {
408
409
/**
410
* A hint that the file should be opened for reading and writing.
411
*/
412
readonly create: true;
413
414
/**
415
* Open the file in append mode. This will write data to the
416
* end of the file.
417
*/
418
readonly append?: boolean;
419
}
420
421
export interface IFileDeleteOptions {
422
423
/**
424
* Set to `true` to recursively delete any children of the file. This
425
* only applies to folders and can lead to an error unless provided
426
* if the folder is not empty.
427
*/
428
readonly recursive: boolean;
429
430
/**
431
* Set to `true` to attempt to move the file to trash
432
* instead of deleting it permanently from disk.
433
*
434
* This option maybe not be supported on all providers.
435
*/
436
readonly useTrash: boolean;
437
438
/**
439
* The optional `atomic` flag can be used to make sure
440
* the `delete` method deletes the target atomically by
441
* first renaming it to a temporary resource in the same
442
* folder and then deleting it.
443
*
444
* This option maybe not be supported on all providers.
445
*/
446
readonly atomic: IFileAtomicOptions | false;
447
}
448
449
export enum FileType {
450
451
/**
452
* File is unknown (neither file, directory nor symbolic link).
453
*/
454
Unknown = 0,
455
456
/**
457
* File is a normal file.
458
*/
459
File = 1,
460
461
/**
462
* File is a directory.
463
*/
464
Directory = 2,
465
466
/**
467
* File is a symbolic link.
468
*
469
* Note: even when the file is a symbolic link, you can test for
470
* `FileType.File` and `FileType.Directory` to know the type of
471
* the target the link points to.
472
*/
473
SymbolicLink = 64
474
}
475
476
export enum FilePermission {
477
478
/**
479
* File is readonly. Components like editors should not
480
* offer to edit the contents.
481
*/
482
Readonly = 1,
483
484
/**
485
* File is locked. Components like editors should offer
486
* to edit the contents and ask the user upon saving to
487
* remove the lock.
488
*/
489
Locked = 2,
490
491
/**
492
* File is executable. Relevant for Unix-like systems where
493
* the executable bit determines if a file can be run.
494
*/
495
Executable = 4
496
}
497
498
export interface IStat {
499
500
/**
501
* The file type.
502
*/
503
readonly type: FileType;
504
505
/**
506
* The last modification date represented as millis from unix epoch.
507
*/
508
readonly mtime: number;
509
510
/**
511
* The creation date represented as millis from unix epoch.
512
*/
513
readonly ctime: number;
514
515
/**
516
* The size of the file in bytes.
517
*/
518
readonly size: number;
519
520
/**
521
* The file permissions.
522
*/
523
readonly permissions?: FilePermission;
524
}
525
526
export interface IWatchOptionsWithoutCorrelation {
527
528
/**
529
* Set to `true` to watch for changes recursively in a folder
530
* and all of its children.
531
*/
532
recursive: boolean;
533
534
/**
535
* A set of glob patterns or paths to exclude from watching.
536
* Paths can be relative or absolute and when relative are
537
* resolved against the watched folder. Glob patterns are
538
* always matched relative to the watched folder.
539
*/
540
excludes: string[];
541
542
/**
543
* An optional set of glob patterns or paths to include for
544
* watching. If not provided, all paths are considered for
545
* events.
546
* Paths can be relative or absolute and when relative are
547
* resolved against the watched folder. Glob patterns are
548
* always matched relative to the watched folder.
549
*/
550
includes?: Array<string | IRelativePattern>;
551
552
/**
553
* If provided, allows to filter the events that the watcher should consider
554
* for emitting. If not provided, all events are emitted.
555
*
556
* For example, to emit added and updated events, set to:
557
* `FileChangeFilter.ADDED | FileChangeFilter.UPDATED`.
558
*/
559
filter?: FileChangeFilter;
560
}
561
562
export interface IWatchOptions extends IWatchOptionsWithoutCorrelation {
563
564
/**
565
* If provided, file change events from the watcher that
566
* are a result of this watch request will carry the same
567
* id.
568
*/
569
readonly correlationId?: number;
570
}
571
572
export const enum FileChangeFilter {
573
UPDATED = 1 << 1,
574
ADDED = 1 << 2,
575
DELETED = 1 << 3
576
}
577
578
export interface IWatchOptionsWithCorrelation extends IWatchOptions {
579
readonly correlationId: number;
580
}
581
582
export interface IFileSystemWatcher extends IDisposable {
583
584
/**
585
* An event which fires on file/folder change only for changes
586
* that correlate to the watch request with matching correlation
587
* identifier.
588
*/
589
readonly onDidChange: Event<FileChangesEvent>;
590
}
591
592
export function isFileSystemWatcher(thing: unknown): thing is IFileSystemWatcher {
593
const candidate = thing as IFileSystemWatcher | undefined;
594
595
return !!candidate && typeof candidate.onDidChange === 'function';
596
}
597
598
export const enum FileSystemProviderCapabilities {
599
600
/**
601
* No capabilities.
602
*/
603
None = 0,
604
605
/**
606
* Provider supports unbuffered read/write.
607
*/
608
FileReadWrite = 1 << 1,
609
610
/**
611
* Provider supports open/read/write/close low level file operations.
612
*/
613
FileOpenReadWriteClose = 1 << 2,
614
615
/**
616
* Provider supports stream based reading.
617
*/
618
FileReadStream = 1 << 4,
619
620
/**
621
* Provider supports copy operation.
622
*/
623
FileFolderCopy = 1 << 3,
624
625
/**
626
* Provider is path case sensitive.
627
*/
628
PathCaseSensitive = 1 << 10,
629
630
/**
631
* All files of the provider are readonly.
632
*/
633
Readonly = 1 << 11,
634
635
/**
636
* Provider supports to delete via trash.
637
*/
638
Trash = 1 << 12,
639
640
/**
641
* Provider support to unlock files for writing.
642
*/
643
FileWriteUnlock = 1 << 13,
644
645
/**
646
* Provider support to read files atomically. This implies the
647
* provider provides the `FileReadWrite` capability too.
648
*/
649
FileAtomicRead = 1 << 14,
650
651
/**
652
* Provider support to write files atomically. This implies the
653
* provider provides the `FileReadWrite` capability too.
654
*/
655
FileAtomicWrite = 1 << 15,
656
657
/**
658
* Provider support to delete atomically.
659
*/
660
FileAtomicDelete = 1 << 16,
661
662
/**
663
* Provider support to clone files atomically.
664
*/
665
FileClone = 1 << 17,
666
667
/**
668
* Provider support to resolve real paths.
669
*/
670
FileRealpath = 1 << 18,
671
672
/**
673
* Provider support to append to files.
674
*/
675
FileAppend = 1 << 19
676
}
677
678
export interface IFileSystemProvider {
679
680
readonly capabilities: FileSystemProviderCapabilities;
681
readonly onDidChangeCapabilities: Event<void>;
682
683
readonly onDidChangeFile: Event<readonly IFileChange[]>;
684
readonly onDidWatchError?: Event<string>;
685
watch(resource: URI, opts: IWatchOptions): IDisposable;
686
687
stat(resource: URI): Promise<IStat>;
688
mkdir(resource: URI): Promise<void>;
689
readdir(resource: URI): Promise<[string, FileType][]>;
690
delete(resource: URI, opts: IFileDeleteOptions): Promise<void>;
691
692
rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
693
copy?(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
694
695
readFile?(resource: URI): Promise<Uint8Array>;
696
writeFile?(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void>;
697
698
readFileStream?(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array>;
699
700
open?(resource: URI, opts: IFileOpenOptions): Promise<number>;
701
close?(fd: number): Promise<void>;
702
read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
703
write?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
704
705
cloneFile?(from: URI, to: URI): Promise<void>;
706
}
707
708
export interface IFileSystemProviderWithFileReadWriteCapability extends IFileSystemProvider {
709
readFile(resource: URI): Promise<Uint8Array>;
710
writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void>;
711
}
712
713
export function hasReadWriteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadWriteCapability {
714
return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadWrite);
715
}
716
717
export function hasFileAppendCapability(provider: IFileSystemProvider): boolean {
718
return !!(provider.capabilities & FileSystemProviderCapabilities.FileAppend);
719
}
720
721
export interface IFileSystemProviderWithFileFolderCopyCapability extends IFileSystemProvider {
722
copy(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void>;
723
}
724
725
export function hasFileFolderCopyCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileFolderCopyCapability {
726
return !!(provider.capabilities & FileSystemProviderCapabilities.FileFolderCopy);
727
}
728
729
export interface IFileSystemProviderWithFileCloneCapability extends IFileSystemProvider {
730
cloneFile(from: URI, to: URI): Promise<void>;
731
}
732
733
export function hasFileCloneCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileCloneCapability {
734
return !!(provider.capabilities & FileSystemProviderCapabilities.FileClone);
735
}
736
737
export interface IFileSystemProviderWithFileRealpathCapability extends IFileSystemProvider {
738
realpath(resource: URI): Promise<string>;
739
}
740
741
export function hasFileRealpathCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileRealpathCapability {
742
return !!(provider.capabilities & FileSystemProviderCapabilities.FileRealpath);
743
}
744
745
export interface IFileSystemProviderWithOpenReadWriteCloseCapability extends IFileSystemProvider {
746
open(resource: URI, opts: IFileOpenOptions): Promise<number>;
747
close(fd: number): Promise<void>;
748
read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
749
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
750
}
751
752
export function hasOpenReadWriteCloseCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithOpenReadWriteCloseCapability {
753
return !!(provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose);
754
}
755
756
export interface IFileSystemProviderWithFileReadStreamCapability extends IFileSystemProvider {
757
readFileStream(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array>;
758
}
759
760
export function hasFileReadStreamCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadStreamCapability {
761
return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadStream);
762
}
763
764
export interface IFileSystemProviderWithFileAtomicReadCapability extends IFileSystemProvider {
765
readFile(resource: URI, opts?: IFileAtomicReadOptions): Promise<Uint8Array>;
766
enforceAtomicReadFile?(resource: URI): boolean;
767
}
768
769
export function hasFileAtomicReadCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicReadCapability {
770
if (!hasReadWriteCapability(provider)) {
771
return false; // we require the `FileReadWrite` capability too
772
}
773
774
return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicRead);
775
}
776
777
export interface IFileSystemProviderWithFileAtomicWriteCapability extends IFileSystemProvider {
778
writeFile(resource: URI, contents: Uint8Array, opts?: IFileAtomicWriteOptions): Promise<void>;
779
enforceAtomicWriteFile?(resource: URI): IFileAtomicOptions | false;
780
}
781
782
export function hasFileAtomicWriteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicWriteCapability {
783
if (!hasReadWriteCapability(provider)) {
784
return false; // we require the `FileReadWrite` capability too
785
}
786
787
return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicWrite);
788
}
789
790
export interface IFileSystemProviderWithFileAtomicDeleteCapability extends IFileSystemProvider {
791
delete(resource: URI, opts: IFileAtomicDeleteOptions): Promise<void>;
792
enforceAtomicDelete?(resource: URI): IFileAtomicOptions | false;
793
}
794
795
export function hasFileAtomicDeleteCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileAtomicDeleteCapability {
796
return !!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicDelete);
797
}
798
799
export interface IFileSystemProviderWithReadonlyCapability extends IFileSystemProvider {
800
801
readonly capabilities: FileSystemProviderCapabilities.Readonly & FileSystemProviderCapabilities;
802
803
/**
804
* An optional message to show in the UI to explain why the file system is readonly.
805
*/
806
readonly readOnlyMessage?: IMarkdownString;
807
}
808
809
export function hasReadonlyCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithReadonlyCapability {
810
return !!(provider.capabilities & FileSystemProviderCapabilities.Readonly);
811
}
812
813
export enum FileSystemProviderErrorCode {
814
FileExists = 'EntryExists',
815
FileNotFound = 'EntryNotFound',
816
FileNotADirectory = 'EntryNotADirectory',
817
FileIsADirectory = 'EntryIsADirectory',
818
FileExceedsStorageQuota = 'EntryExceedsStorageQuota',
819
FileTooLarge = 'EntryTooLarge',
820
FileWriteLocked = 'EntryWriteLocked',
821
NoPermissions = 'NoPermissions',
822
Unavailable = 'Unavailable',
823
Unknown = 'Unknown'
824
}
825
826
export interface IFileSystemProviderError extends Error {
827
readonly name: string;
828
readonly code: FileSystemProviderErrorCode;
829
}
830
831
export class FileSystemProviderError extends Error implements IFileSystemProviderError {
832
833
static create(error: Error | string, code: FileSystemProviderErrorCode): FileSystemProviderError {
834
const providerError = new FileSystemProviderError(error.toString(), code);
835
markAsFileSystemProviderError(providerError, code);
836
837
return providerError;
838
}
839
840
private constructor(message: string, readonly code: FileSystemProviderErrorCode) {
841
super(message);
842
}
843
}
844
845
export function createFileSystemProviderError(error: Error | string, code: FileSystemProviderErrorCode): FileSystemProviderError {
846
return FileSystemProviderError.create(error, code);
847
}
848
849
export function ensureFileSystemProviderError(error?: Error): Error {
850
if (!error) {
851
return createFileSystemProviderError(localize('unknownError', "Unknown Error"), FileSystemProviderErrorCode.Unknown); // https://github.com/microsoft/vscode/issues/72798
852
}
853
854
return error;
855
}
856
857
export function markAsFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): Error {
858
error.name = code ? `${code} (FileSystemError)` : `FileSystemError`;
859
860
return error;
861
}
862
863
export function toFileSystemProviderErrorCode(error: Error | undefined | null): FileSystemProviderErrorCode {
864
865
// Guard against abuse
866
if (!error) {
867
return FileSystemProviderErrorCode.Unknown;
868
}
869
870
// FileSystemProviderError comes with the code
871
if (error instanceof FileSystemProviderError) {
872
return error.code;
873
}
874
875
// Any other error, check for name match by assuming that the error
876
// went through the markAsFileSystemProviderError() method
877
const match = /^(.+) \(FileSystemError\)$/.exec(error.name);
878
if (!match) {
879
return FileSystemProviderErrorCode.Unknown;
880
}
881
882
switch (match[1]) {
883
case FileSystemProviderErrorCode.FileExists: return FileSystemProviderErrorCode.FileExists;
884
case FileSystemProviderErrorCode.FileIsADirectory: return FileSystemProviderErrorCode.FileIsADirectory;
885
case FileSystemProviderErrorCode.FileNotADirectory: return FileSystemProviderErrorCode.FileNotADirectory;
886
case FileSystemProviderErrorCode.FileNotFound: return FileSystemProviderErrorCode.FileNotFound;
887
case FileSystemProviderErrorCode.FileTooLarge: return FileSystemProviderErrorCode.FileTooLarge;
888
case FileSystemProviderErrorCode.FileWriteLocked: return FileSystemProviderErrorCode.FileWriteLocked;
889
case FileSystemProviderErrorCode.NoPermissions: return FileSystemProviderErrorCode.NoPermissions;
890
case FileSystemProviderErrorCode.Unavailable: return FileSystemProviderErrorCode.Unavailable;
891
}
892
893
return FileSystemProviderErrorCode.Unknown;
894
}
895
896
export function toFileOperationResult(error: Error): FileOperationResult {
897
898
// FileSystemProviderError comes with the result already
899
if (error instanceof FileOperationError) {
900
return error.fileOperationResult;
901
}
902
903
// Otherwise try to find from code
904
switch (toFileSystemProviderErrorCode(error)) {
905
case FileSystemProviderErrorCode.FileNotFound:
906
return FileOperationResult.FILE_NOT_FOUND;
907
case FileSystemProviderErrorCode.FileIsADirectory:
908
return FileOperationResult.FILE_IS_DIRECTORY;
909
case FileSystemProviderErrorCode.FileNotADirectory:
910
return FileOperationResult.FILE_NOT_DIRECTORY;
911
case FileSystemProviderErrorCode.FileWriteLocked:
912
return FileOperationResult.FILE_WRITE_LOCKED;
913
case FileSystemProviderErrorCode.NoPermissions:
914
return FileOperationResult.FILE_PERMISSION_DENIED;
915
case FileSystemProviderErrorCode.FileExists:
916
return FileOperationResult.FILE_MOVE_CONFLICT;
917
case FileSystemProviderErrorCode.FileTooLarge:
918
return FileOperationResult.FILE_TOO_LARGE;
919
default:
920
return FileOperationResult.FILE_OTHER_ERROR;
921
}
922
}
923
924
export interface IFileSystemProviderRegistrationEvent {
925
readonly added: boolean;
926
readonly scheme: string;
927
readonly provider?: IFileSystemProvider;
928
}
929
930
export interface IFileSystemProviderCapabilitiesChangeEvent {
931
readonly provider: IFileSystemProvider;
932
readonly scheme: string;
933
}
934
935
export interface IFileSystemProviderActivationEvent {
936
readonly scheme: string;
937
join(promise: Promise<void>): void;
938
}
939
940
export const enum FileOperation {
941
CREATE,
942
DELETE,
943
MOVE,
944
COPY,
945
WRITE
946
}
947
948
export interface IFileOperationEvent {
949
950
readonly resource: URI;
951
readonly operation: FileOperation;
952
953
isOperation(operation: FileOperation.DELETE | FileOperation.WRITE): boolean;
954
isOperation(operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY): this is IFileOperationEventWithMetadata;
955
}
956
957
export interface IFileOperationEventWithMetadata extends IFileOperationEvent {
958
readonly target: IFileStatWithMetadata;
959
}
960
961
export class FileOperationEvent implements IFileOperationEvent {
962
963
constructor(resource: URI, operation: FileOperation.DELETE | FileOperation.WRITE);
964
constructor(resource: URI, operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY, target: IFileStatWithMetadata);
965
constructor(readonly resource: URI, readonly operation: FileOperation, readonly target?: IFileStatWithMetadata) { }
966
967
isOperation(operation: FileOperation.DELETE | FileOperation.WRITE): boolean;
968
isOperation(operation: FileOperation.CREATE | FileOperation.MOVE | FileOperation.COPY): this is IFileOperationEventWithMetadata;
969
isOperation(operation: FileOperation): boolean {
970
return this.operation === operation;
971
}
972
}
973
974
/**
975
* Possible changes that can occur to a file.
976
*/
977
export const enum FileChangeType {
978
UPDATED,
979
ADDED,
980
DELETED
981
}
982
983
/**
984
* Identifies a single change in a file.
985
*/
986
export interface IFileChange {
987
988
/**
989
* The type of change that occurred to the file.
990
*/
991
type: FileChangeType;
992
993
/**
994
* The unified resource identifier of the file that changed.
995
*/
996
readonly resource: URI;
997
998
/**
999
* If provided when starting the file watcher, the correlation
1000
* identifier will match the original file watching request as
1001
* a way to identify the original component that is interested
1002
* in the change.
1003
*/
1004
readonly cId?: number;
1005
}
1006
1007
export class FileChangesEvent {
1008
1009
private static readonly MIXED_CORRELATION = null;
1010
1011
private readonly correlationId: number | undefined | typeof FileChangesEvent.MIXED_CORRELATION = undefined;
1012
1013
constructor(changes: readonly IFileChange[], private readonly ignorePathCasing: boolean) {
1014
for (const change of changes) {
1015
1016
// Split by type
1017
switch (change.type) {
1018
case FileChangeType.ADDED:
1019
this.rawAdded.push(change.resource);
1020
break;
1021
case FileChangeType.UPDATED:
1022
this.rawUpdated.push(change.resource);
1023
break;
1024
case FileChangeType.DELETED:
1025
this.rawDeleted.push(change.resource);
1026
break;
1027
}
1028
1029
// Figure out events correlation
1030
if (this.correlationId !== FileChangesEvent.MIXED_CORRELATION) {
1031
if (typeof change.cId === 'number') {
1032
if (this.correlationId === undefined) {
1033
this.correlationId = change.cId; // correlation not yet set, just take it
1034
} else if (this.correlationId !== change.cId) {
1035
this.correlationId = FileChangesEvent.MIXED_CORRELATION; // correlation mismatch, we have mixed correlation
1036
}
1037
} else {
1038
if (this.correlationId !== undefined) {
1039
this.correlationId = FileChangesEvent.MIXED_CORRELATION; // correlation mismatch, we have mixed correlation
1040
}
1041
}
1042
}
1043
}
1044
}
1045
1046
private readonly added = new Lazy(() => {
1047
const added = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing);
1048
added.fill(this.rawAdded.map(resource => [resource, true]));
1049
1050
return added;
1051
});
1052
1053
private readonly updated = new Lazy(() => {
1054
const updated = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing);
1055
updated.fill(this.rawUpdated.map(resource => [resource, true]));
1056
1057
return updated;
1058
});
1059
1060
private readonly deleted = new Lazy(() => {
1061
const deleted = TernarySearchTree.forUris<boolean>(() => this.ignorePathCasing);
1062
deleted.fill(this.rawDeleted.map(resource => [resource, true]));
1063
1064
return deleted;
1065
});
1066
1067
/**
1068
* Find out if the file change events match the provided resource.
1069
*
1070
* Note: when passing `FileChangeType.DELETED`, we consider a match
1071
* also when the parent of the resource got deleted.
1072
*/
1073
contains(resource: URI, ...types: FileChangeType[]): boolean {
1074
return this.doContains(resource, { includeChildren: false }, ...types);
1075
}
1076
1077
/**
1078
* Find out if the file change events either match the provided
1079
* resource, or contain a child of this resource.
1080
*/
1081
affects(resource: URI, ...types: FileChangeType[]): boolean {
1082
return this.doContains(resource, { includeChildren: true }, ...types);
1083
}
1084
1085
private doContains(resource: URI, options: { includeChildren: boolean }, ...types: FileChangeType[]): boolean {
1086
if (!resource) {
1087
return false;
1088
}
1089
1090
const hasTypesFilter = types.length > 0;
1091
1092
// Added
1093
if (!hasTypesFilter || types.includes(FileChangeType.ADDED)) {
1094
if (this.added.value.get(resource)) {
1095
return true;
1096
}
1097
1098
if (options.includeChildren && this.added.value.findSuperstr(resource)) {
1099
return true;
1100
}
1101
}
1102
1103
// Updated
1104
if (!hasTypesFilter || types.includes(FileChangeType.UPDATED)) {
1105
if (this.updated.value.get(resource)) {
1106
return true;
1107
}
1108
1109
if (options.includeChildren && this.updated.value.findSuperstr(resource)) {
1110
return true;
1111
}
1112
}
1113
1114
// Deleted
1115
if (!hasTypesFilter || types.includes(FileChangeType.DELETED)) {
1116
if (this.deleted.value.findSubstr(resource) /* deleted also considers parent folders */) {
1117
return true;
1118
}
1119
1120
if (options.includeChildren && this.deleted.value.findSuperstr(resource)) {
1121
return true;
1122
}
1123
}
1124
1125
return false;
1126
}
1127
1128
/**
1129
* Returns if this event contains added files.
1130
*/
1131
gotAdded(): boolean {
1132
return this.rawAdded.length > 0;
1133
}
1134
1135
/**
1136
* Returns if this event contains deleted files.
1137
*/
1138
gotDeleted(): boolean {
1139
return this.rawDeleted.length > 0;
1140
}
1141
1142
/**
1143
* Returns if this event contains updated files.
1144
*/
1145
gotUpdated(): boolean {
1146
return this.rawUpdated.length > 0;
1147
}
1148
1149
/**
1150
* Returns if this event contains changes that correlate to the
1151
* provided `correlationId`.
1152
*
1153
* File change event correlation is an advanced watch feature that
1154
* allows to identify from which watch request the events originate
1155
* from. This correlation allows to route events specifically
1156
* only to the requestor and not emit them to all listeners.
1157
*/
1158
correlates(correlationId: number): boolean {
1159
return this.correlationId === correlationId;
1160
}
1161
1162
/**
1163
* Figure out if the event contains changes that correlate to one
1164
* correlation identifier.
1165
*
1166
* File change event correlation is an advanced watch feature that
1167
* allows to identify from which watch request the events originate
1168
* from. This correlation allows to route events specifically
1169
* only to the requestor and not emit them to all listeners.
1170
*/
1171
hasCorrelation(): boolean {
1172
return typeof this.correlationId === 'number';
1173
}
1174
1175
/**
1176
* @deprecated use the `contains` or `affects` method to efficiently find
1177
* out if the event relates to a given resource. these methods ensure:
1178
* - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1179
* - correctly handles `FileChangeType.DELETED` events
1180
*/
1181
readonly rawAdded: URI[] = [];
1182
1183
/**
1184
* @deprecated use the `contains` or `affects` method to efficiently find
1185
* out if the event relates to a given resource. these methods ensure:
1186
* - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1187
* - correctly handles `FileChangeType.DELETED` events
1188
*/
1189
readonly rawUpdated: URI[] = [];
1190
1191
/**
1192
* @deprecated use the `contains` or `affects` method to efficiently find
1193
* out if the event relates to a given resource. these methods ensure:
1194
* - that there is no expensive lookup needed (by using a `TernarySearchTree`)
1195
* - correctly handles `FileChangeType.DELETED` events
1196
*/
1197
readonly rawDeleted: URI[] = [];
1198
}
1199
1200
export function isParent(path: string, candidate: string, ignoreCase?: boolean): boolean {
1201
if (!path || !candidate || path === candidate) {
1202
return false;
1203
}
1204
1205
if (candidate.length > path.length) {
1206
return false;
1207
}
1208
1209
if (candidate.charAt(candidate.length - 1) !== sep) {
1210
candidate += sep;
1211
}
1212
1213
if (ignoreCase) {
1214
return startsWithIgnoreCase(path, candidate);
1215
}
1216
1217
return path.indexOf(candidate) === 0;
1218
}
1219
1220
export interface IBaseFileStat {
1221
1222
/**
1223
* The unified resource identifier of this file or folder.
1224
*/
1225
readonly resource: URI;
1226
1227
/**
1228
* The name which is the last segment
1229
* of the {{path}}.
1230
*/
1231
readonly name: string;
1232
1233
/**
1234
* The size of the file.
1235
*
1236
* The value may or may not be resolved as
1237
* it is optional.
1238
*/
1239
readonly size?: number;
1240
1241
/**
1242
* The last modification date represented as millis from unix epoch.
1243
*
1244
* The value may or may not be resolved as
1245
* it is optional.
1246
*/
1247
readonly mtime?: number;
1248
1249
/**
1250
* The creation date represented as millis from unix epoch.
1251
*
1252
* The value may or may not be resolved as
1253
* it is optional.
1254
*/
1255
readonly ctime?: number;
1256
1257
/**
1258
* A unique identifier that represents the
1259
* current state of the file or directory.
1260
*
1261
* The value may or may not be resolved as
1262
* it is optional.
1263
*/
1264
readonly etag?: string;
1265
1266
/**
1267
* File is readonly. Components like editors should not
1268
* offer to edit the contents.
1269
*/
1270
readonly readonly?: boolean;
1271
1272
/**
1273
* File is locked. Components like editors should offer
1274
* to edit the contents and ask the user upon saving to
1275
* remove the lock.
1276
*/
1277
readonly locked?: boolean;
1278
1279
/**
1280
* File is executable. Relevant for Unix-like systems where
1281
* the executable bit determines if a file can be run.
1282
*/
1283
readonly executable?: boolean;
1284
}
1285
1286
export interface IBaseFileStatWithMetadata extends Required<IBaseFileStat> { }
1287
1288
/**
1289
* A file resource with meta information and resolved children if any.
1290
*/
1291
export interface IFileStat extends IBaseFileStat {
1292
1293
/**
1294
* The resource is a file.
1295
*/
1296
readonly isFile: boolean;
1297
1298
/**
1299
* The resource is a directory.
1300
*/
1301
readonly isDirectory: boolean;
1302
1303
/**
1304
* The resource is a symbolic link. Note: even when the
1305
* file is a symbolic link, you can test for `FileType.File`
1306
* and `FileType.Directory` to know the type of the target
1307
* the link points to.
1308
*/
1309
readonly isSymbolicLink: boolean;
1310
1311
/**
1312
* The children of the file stat or undefined if none.
1313
*/
1314
children: IFileStat[] | undefined;
1315
}
1316
1317
export interface IFileStatWithMetadata extends IFileStat, IBaseFileStatWithMetadata {
1318
readonly mtime: number;
1319
readonly ctime: number;
1320
readonly etag: string;
1321
readonly size: number;
1322
readonly readonly: boolean;
1323
readonly locked: boolean;
1324
readonly executable: boolean;
1325
readonly children: IFileStatWithMetadata[] | undefined;
1326
}
1327
1328
export interface IFileStatResult {
1329
readonly stat?: IFileStat;
1330
readonly success: boolean;
1331
}
1332
1333
export interface IFileStatResultWithMetadata extends IFileStatResult {
1334
readonly stat?: IFileStatWithMetadata;
1335
}
1336
1337
export interface IFileStatWithPartialMetadata extends Omit<IFileStatWithMetadata, 'children'> { }
1338
1339
export interface IFileContent extends IBaseFileStatWithMetadata {
1340
1341
/**
1342
* The content of a file as buffer.
1343
*/
1344
readonly value: VSBuffer;
1345
}
1346
1347
export interface IFileStreamContent extends IBaseFileStatWithMetadata {
1348
1349
/**
1350
* The content of a file as stream.
1351
*/
1352
readonly value: VSBufferReadableStream;
1353
}
1354
1355
export interface IBaseReadFileOptions extends IFileReadStreamOptions {
1356
1357
/**
1358
* The optional etag parameter allows to return early from resolving the resource if
1359
* the contents on disk match the etag. This prevents accumulated reading of resources
1360
* that have been read already with the same etag.
1361
* It is the task of the caller to makes sure to handle this error case from the promise.
1362
*/
1363
readonly etag?: string;
1364
}
1365
1366
export interface IReadFileStreamOptions extends IBaseReadFileOptions { }
1367
1368
export interface IReadFileOptions extends IBaseReadFileOptions {
1369
1370
/**
1371
* The optional `atomic` flag can be used to make sure
1372
* the `readFile` method is not running in parallel with
1373
* any `write` operations in the same process.
1374
*
1375
* Typically you should not need to use this flag but if
1376
* for example you are quickly reading a file right after
1377
* a file event occurred and the file changes a lot, there
1378
* is a chance that a read returns an empty or partial file
1379
* because a pending write has not finished yet.
1380
*
1381
* Note: this does not prevent the file from being written
1382
* to from a different process. If you need such atomic
1383
* operations, you better use a real database as storage.
1384
*/
1385
readonly atomic?: boolean;
1386
}
1387
1388
export interface IWriteFileOptions {
1389
1390
/**
1391
* The last known modification time of the file. This can be used to prevent dirty writes.
1392
*/
1393
readonly mtime?: number;
1394
1395
/**
1396
* The etag of the file. This can be used to prevent dirty writes.
1397
*/
1398
readonly etag?: string;
1399
1400
/**
1401
* Whether to attempt to unlock a file before writing.
1402
*/
1403
readonly unlock?: boolean;
1404
1405
/**
1406
* The optional `atomic` flag can be used to make sure
1407
* the `writeFile` method updates the target file atomically
1408
* by first writing to a temporary file in the same folder
1409
* and then renaming it over the target.
1410
*/
1411
readonly atomic?: IFileAtomicOptions | false;
1412
1413
/**
1414
* If set to true, will append to the end of the file instead of
1415
* replacing its contents. Will create the file if it doesn't exist.
1416
*/
1417
readonly append?: boolean;
1418
}
1419
1420
export interface IResolveFileOptions {
1421
1422
/**
1423
* Automatically continue resolving children of a directory until the provided resources
1424
* are found.
1425
*/
1426
readonly resolveTo?: readonly URI[];
1427
1428
/**
1429
* Automatically continue resolving children of a directory if the number of children is 1.
1430
*/
1431
readonly resolveSingleChildDescendants?: boolean;
1432
1433
/**
1434
* Will resolve mtime, ctime, size and etag of files if enabled. This can have a negative impact
1435
* on performance and thus should only be used when these values are required.
1436
*/
1437
readonly resolveMetadata?: boolean;
1438
}
1439
1440
export interface IResolveMetadataFileOptions extends IResolveFileOptions {
1441
readonly resolveMetadata: true;
1442
}
1443
1444
export interface ICreateFileOptions {
1445
1446
/**
1447
* Overwrite the file to create if it already exists on disk. Otherwise
1448
* an error will be thrown (FILE_MODIFIED_SINCE).
1449
*/
1450
readonly overwrite?: boolean;
1451
}
1452
1453
export class FileOperationError extends Error {
1454
constructor(
1455
message: string,
1456
readonly fileOperationResult: FileOperationResult,
1457
readonly options?: IReadFileOptions | IWriteFileOptions | ICreateFileOptions
1458
) {
1459
super(message);
1460
}
1461
}
1462
1463
export class TooLargeFileOperationError extends FileOperationError {
1464
constructor(
1465
message: string,
1466
override readonly fileOperationResult: FileOperationResult.FILE_TOO_LARGE,
1467
readonly size: number,
1468
options?: IReadFileOptions
1469
) {
1470
super(message, fileOperationResult, options);
1471
}
1472
}
1473
1474
export class NotModifiedSinceFileOperationError extends FileOperationError {
1475
1476
constructor(
1477
message: string,
1478
readonly stat: IFileStatWithMetadata,
1479
options?: IReadFileOptions
1480
) {
1481
super(message, FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
1482
}
1483
}
1484
1485
export const enum FileOperationResult {
1486
FILE_IS_DIRECTORY,
1487
FILE_NOT_FOUND,
1488
FILE_NOT_MODIFIED_SINCE,
1489
FILE_MODIFIED_SINCE,
1490
FILE_MOVE_CONFLICT,
1491
FILE_WRITE_LOCKED,
1492
FILE_PERMISSION_DENIED,
1493
FILE_TOO_LARGE,
1494
FILE_INVALID_PATH,
1495
FILE_NOT_DIRECTORY,
1496
FILE_OTHER_ERROR
1497
}
1498
1499
//#endregion
1500
1501
//#region Settings
1502
1503
export const AutoSaveConfiguration = {
1504
OFF: 'off',
1505
AFTER_DELAY: 'afterDelay',
1506
ON_FOCUS_CHANGE: 'onFocusChange',
1507
ON_WINDOW_CHANGE: 'onWindowChange'
1508
};
1509
1510
export const HotExitConfiguration = {
1511
OFF: 'off',
1512
ON_EXIT: 'onExit',
1513
ON_EXIT_AND_WINDOW_CLOSE: 'onExitAndWindowClose'
1514
};
1515
1516
export const FILES_ASSOCIATIONS_CONFIG = 'files.associations';
1517
export const FILES_EXCLUDE_CONFIG = 'files.exclude';
1518
export const FILES_READONLY_INCLUDE_CONFIG = 'files.readonlyInclude';
1519
export const FILES_READONLY_EXCLUDE_CONFIG = 'files.readonlyExclude';
1520
export const FILES_READONLY_FROM_PERMISSIONS_CONFIG = 'files.readonlyFromPermissions';
1521
1522
export interface IGlobPatterns {
1523
[filepattern: string]: boolean;
1524
}
1525
1526
export interface IFilesConfiguration {
1527
files?: IFilesConfigurationNode;
1528
}
1529
1530
export interface IFilesConfigurationNode {
1531
associations: { [filepattern: string]: string };
1532
exclude: IExpression;
1533
watcherExclude: IGlobPatterns;
1534
watcherInclude: string[];
1535
encoding: string;
1536
autoGuessEncoding: boolean;
1537
candidateGuessEncodings: string[];
1538
defaultLanguage: string;
1539
trimTrailingWhitespace: boolean;
1540
autoSave: string;
1541
autoSaveDelay: number;
1542
autoSaveWorkspaceFilesOnly: boolean;
1543
autoSaveWhenNoErrors: boolean;
1544
eol: string;
1545
enableTrash: boolean;
1546
hotExit: string;
1547
saveConflictResolution: 'askUser' | 'overwriteFileOnDisk';
1548
readonlyInclude: IGlobPatterns;
1549
readonlyExclude: IGlobPatterns;
1550
readonlyFromPermissions: boolean;
1551
}
1552
1553
//#endregion
1554
1555
//#region Utilities
1556
1557
export enum FileKind {
1558
FILE,
1559
FOLDER,
1560
ROOT_FOLDER
1561
}
1562
1563
/**
1564
* A hint to disable etag checking for reading/writing.
1565
*/
1566
export const ETAG_DISABLED = '';
1567
1568
export function etag(stat: { mtime: number; size: number }): string;
1569
export function etag(stat: { mtime: number | undefined; size: number | undefined }): string | undefined;
1570
export function etag(stat: { mtime: number | undefined; size: number | undefined }): string | undefined {
1571
if (typeof stat.size !== 'number' || typeof stat.mtime !== 'number') {
1572
return undefined;
1573
}
1574
1575
return stat.mtime.toString(29) + stat.size.toString(31);
1576
}
1577
1578
export async function whenProviderRegistered(file: URI, fileService: IFileService): Promise<void> {
1579
if (fileService.hasProvider(URI.from({ scheme: file.scheme }))) {
1580
return;
1581
}
1582
1583
return new Promise(resolve => {
1584
const disposable = fileService.onDidChangeFileSystemProviderRegistrations(e => {
1585
if (e.scheme === file.scheme && e.added) {
1586
disposable.dispose();
1587
resolve();
1588
}
1589
});
1590
});
1591
}
1592
1593
/**
1594
* Helper to format a raw byte size into a human readable label.
1595
*/
1596
export class ByteSize {
1597
1598
static readonly KB = 1024;
1599
static readonly MB = ByteSize.KB * ByteSize.KB;
1600
static readonly GB = ByteSize.MB * ByteSize.KB;
1601
static readonly TB = ByteSize.GB * ByteSize.KB;
1602
1603
static formatSize(size: number): string {
1604
if (!isNumber(size)) {
1605
size = 0;
1606
}
1607
1608
if (size < ByteSize.KB) {
1609
return localize('sizeB', "{0}B", size.toFixed(0));
1610
}
1611
1612
if (size < ByteSize.MB) {
1613
return localize('sizeKB', "{0}KB", (size / ByteSize.KB).toFixed(2));
1614
}
1615
1616
if (size < ByteSize.GB) {
1617
return localize('sizeMB', "{0}MB", (size / ByteSize.MB).toFixed(2));
1618
}
1619
1620
if (size < ByteSize.TB) {
1621
return localize('sizeGB', "{0}GB", (size / ByteSize.GB).toFixed(2));
1622
}
1623
1624
return localize('sizeTB', "{0}TB", (size / ByteSize.TB).toFixed(2));
1625
}
1626
}
1627
1628
// File limits
1629
1630
export function getLargeFileConfirmationLimit(remoteAuthority?: string): number;
1631
export function getLargeFileConfirmationLimit(uri?: URI): number;
1632
export function getLargeFileConfirmationLimit(arg?: string | URI): number {
1633
const isRemote = typeof arg === 'string' || arg?.scheme === Schemas.vscodeRemote;
1634
const isLocal = typeof arg !== 'string' && arg?.scheme === Schemas.file;
1635
1636
if (isLocal) {
1637
// Local almost has no limit in file size
1638
return 1024 * ByteSize.MB;
1639
}
1640
1641
if (isRemote) {
1642
// With a remote, pick a low limit to avoid
1643
// potentially costly file transfers
1644
return 10 * ByteSize.MB;
1645
}
1646
1647
if (isWeb) {
1648
// Web: we cannot know for sure if a cost
1649
// is associated with the file transfer
1650
// so we pick a reasonably small limit
1651
return 50 * ByteSize.MB;
1652
}
1653
1654
// Local desktop: almost no limit in file size
1655
return 1024 * ByteSize.MB;
1656
}
1657
1658
//#endregion
1659
1660