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