@codingame/monaco-vscode-files-service-override 25.1.2 → 26.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/vscode/src/vs/platform/files/browser/indexedDBFileSystemProvider.js +129 -80
- package/vscode/src/vs/platform/files/common/fileService.js +398 -218
- package/vscode/src/vs/platform/files/common/io.js +30 -20
- package/vscode/src/vs/workbench/services/files/browser/elevatedFileService.js +1 -1
- package/vscode/src/vs/workbench/services/textfile/browser/browserTextFileService.js +44 -23
- package/vscode/src/vs/workbench/services/textfile/browser/textFileService.js +176 -116
- package/vscode/src/vs/workbench/services/textfile/common/textFileEditorModelManager.js +95 -71
- package/vscode/src/vs/workbench/services/textfile/common/textFileSaveParticipant.js +6 -11
|
@@ -14,7 +14,7 @@ import { mark } from '@codingame/monaco-vscode-api/vscode/vs/base/common/perform
|
|
|
14
14
|
import { isAbsolutePath, extUri, extUriIgnorePathCase } from '@codingame/monaco-vscode-api/vscode/vs/base/common/resources';
|
|
15
15
|
import { isReadableStream, peekStream, peekReadable, consumeStream, transform, newWriteableStream, isReadableBufferedStream, listenStream } from '@codingame/monaco-vscode-api/vscode/vs/base/common/stream';
|
|
16
16
|
import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
|
|
17
|
-
import { FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, FileChangesEvent, FileOperationError, FileOperationResult, hasOpenReadWriteCloseCapability, hasReadWriteCapability, hasFileReadStreamCapability, ensureFileSystemProviderError, etag, FilePermission, FileSystemProviderCapabilities, hasFileRealpathCapability, FileOperationEvent, FileOperation, hasFileAtomicWriteCapability, toFileOperationResult, ETAG_DISABLED, hasFileAtomicReadCapability, NotModifiedSinceFileOperationError, TooLargeFileOperationError, hasFileFolderCopyCapability, hasFileAtomicDeleteCapability, hasFileCloneCapability } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files';
|
|
17
|
+
import { FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, FileChangesEvent, FileOperationError, FileOperationResult, hasOpenReadWriteCloseCapability, hasReadWriteCapability, hasFileReadStreamCapability, ensureFileSystemProviderError, etag, FilePermission, FileSystemProviderCapabilities, hasFileRealpathCapability, FileOperationEvent, FileOperation, hasFileAtomicWriteCapability, toFileOperationResult, hasFileAppendCapability, ETAG_DISABLED, hasFileAtomicReadCapability, NotModifiedSinceFileOperationError, TooLargeFileOperationError, hasFileFolderCopyCapability, hasFileAtomicDeleteCapability, hasFileCloneCapability } from '@codingame/monaco-vscode-api/vscode/vs/platform/files/common/files';
|
|
18
18
|
import { readFileIntoStream } from './io.js';
|
|
19
19
|
import { ILogService } from '@codingame/monaco-vscode-api/vscode/vs/platform/log/common/log.service';
|
|
20
20
|
import { ErrorNoTelemetry } from '@codingame/monaco-vscode-api/vscode/vs/base/common/errors';
|
|
@@ -39,8 +39,7 @@ async function mkdirp(providerExtUri, provider, directory) {
|
|
|
39
39
|
))));
|
|
40
40
|
}
|
|
41
41
|
break;
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
42
|
+
} catch (error) {
|
|
44
43
|
if (toFileSystemProviderErrorCode(error) !== FileSystemProviderErrorCode.FileNotFound) {
|
|
45
44
|
throw error;
|
|
46
45
|
}
|
|
@@ -52,8 +51,7 @@ async function mkdirp(providerExtUri, provider, directory) {
|
|
|
52
51
|
directory = providerExtUri.joinPath(directory, directoriesToCreate[i]);
|
|
53
52
|
try {
|
|
54
53
|
await provider.mkdir(directory);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
54
|
+
} catch (error) {
|
|
57
55
|
if (toFileSystemProviderErrorCode(error) !== FileSystemProviderErrorCode.FileExists) {
|
|
58
56
|
throw error;
|
|
59
57
|
}
|
|
@@ -61,7 +59,9 @@ async function mkdirp(providerExtUri, provider, directory) {
|
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
61
|
let FileService = class FileService extends Disposable {
|
|
64
|
-
static {
|
|
62
|
+
static {
|
|
63
|
+
FileService_1 = this;
|
|
64
|
+
}
|
|
65
65
|
constructor(logService) {
|
|
66
66
|
super();
|
|
67
67
|
this.logService = logService;
|
|
@@ -90,7 +90,11 @@ let FileService = class FileService extends Disposable {
|
|
|
90
90
|
mark(`code/registerFilesystem/${scheme}`);
|
|
91
91
|
const providerDisposables = ( new DisposableStore());
|
|
92
92
|
this.provider.set(scheme, provider);
|
|
93
|
-
this._onDidChangeFileSystemProviderRegistrations.fire({
|
|
93
|
+
this._onDidChangeFileSystemProviderRegistrations.fire({
|
|
94
|
+
added: true,
|
|
95
|
+
scheme,
|
|
96
|
+
provider
|
|
97
|
+
});
|
|
94
98
|
providerDisposables.add(provider.onDidChangeFile(changes => {
|
|
95
99
|
const event = ( new FileChangesEvent(changes, !this.isPathCaseSensitive(provider)));
|
|
96
100
|
this.internalOnDidFilesChange.fire(event);
|
|
@@ -98,12 +102,23 @@ let FileService = class FileService extends Disposable {
|
|
|
98
102
|
this._onDidUncorrelatedFilesChange.fire(event);
|
|
99
103
|
}
|
|
100
104
|
}));
|
|
101
|
-
if (typeof provider.onDidWatchError ===
|
|
102
|
-
providerDisposables.add(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
if (typeof provider.onDidWatchError === "function") {
|
|
106
|
+
providerDisposables.add(
|
|
107
|
+
provider.onDidWatchError(error => this._onDidWatchError.fire(( new Error(error))))
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
providerDisposables.add(
|
|
111
|
+
provider.onDidChangeCapabilities(() => this._onDidChangeFileSystemProviderCapabilities.fire({
|
|
112
|
+
provider,
|
|
113
|
+
scheme
|
|
114
|
+
}))
|
|
115
|
+
);
|
|
105
116
|
return toDisposable(() => {
|
|
106
|
-
this._onDidChangeFileSystemProviderRegistrations.fire({
|
|
117
|
+
this._onDidChangeFileSystemProviderRegistrations.fire({
|
|
118
|
+
added: false,
|
|
119
|
+
scheme,
|
|
120
|
+
provider
|
|
121
|
+
});
|
|
107
122
|
this.provider.delete(scheme);
|
|
108
123
|
dispose(providerDisposables);
|
|
109
124
|
});
|
|
@@ -117,7 +132,7 @@ let FileService = class FileService extends Disposable {
|
|
|
117
132
|
scheme,
|
|
118
133
|
join(promise) {
|
|
119
134
|
joiners.push(promise);
|
|
120
|
-
}
|
|
135
|
+
}
|
|
121
136
|
});
|
|
122
137
|
if (( this.provider.has(scheme))) {
|
|
123
138
|
return;
|
|
@@ -136,10 +151,10 @@ let FileService = class FileService extends Disposable {
|
|
|
136
151
|
return !!(provider && (provider.capabilities & capability));
|
|
137
152
|
}
|
|
138
153
|
listCapabilities() {
|
|
139
|
-
return ( Iterable.map(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
));
|
|
154
|
+
return ( Iterable.map(this.provider, ([scheme, provider]) => ({
|
|
155
|
+
scheme,
|
|
156
|
+
capabilities: provider.capabilities
|
|
157
|
+
})));
|
|
143
158
|
}
|
|
144
159
|
async withProvider(resource) {
|
|
145
160
|
if (!isAbsolutePath(resource)) {
|
|
@@ -183,8 +198,7 @@ let FileService = class FileService extends Disposable {
|
|
|
183
198
|
async resolve(resource, options) {
|
|
184
199
|
try {
|
|
185
200
|
return await this.doResolveFile(resource, options);
|
|
186
|
-
}
|
|
187
|
-
catch (error) {
|
|
201
|
+
} catch (error) {
|
|
188
202
|
if (toFileSystemProviderErrorCode(error) === FileSystemProviderErrorCode.FileNotFound) {
|
|
189
203
|
throw ( new FileOperationError(( localize(
|
|
190
204
|
1916,
|
|
@@ -203,25 +217,37 @@ let FileService = class FileService extends Disposable {
|
|
|
203
217
|
const resolveMetadata = options?.resolveMetadata;
|
|
204
218
|
const stat = await provider.stat(resource);
|
|
205
219
|
let trie;
|
|
206
|
-
return this.toFileStat(
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
220
|
+
return this.toFileStat(
|
|
221
|
+
provider,
|
|
222
|
+
resource,
|
|
223
|
+
stat,
|
|
224
|
+
undefined,
|
|
225
|
+
!!resolveMetadata,
|
|
226
|
+
(stat, siblings) => {
|
|
227
|
+
if (!trie) {
|
|
228
|
+
trie = TernarySearchTree.forUris(() => !isPathCaseSensitive);
|
|
229
|
+
trie.set(resource, true);
|
|
230
|
+
if (resolveTo) {
|
|
231
|
+
trie.fill(true, resolveTo);
|
|
232
|
+
}
|
|
212
233
|
}
|
|
234
|
+
if (trie.get(stat.resource) || trie.findSuperstr(stat.resource.with({
|
|
235
|
+
query: null,
|
|
236
|
+
fragment: null
|
|
237
|
+
} ))) {
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
if (stat.isDirectory && resolveSingleChildDescendants) {
|
|
241
|
+
return siblings === 1;
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
213
244
|
}
|
|
214
|
-
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
if (stat.isDirectory && resolveSingleChildDescendants) {
|
|
218
|
-
return siblings === 1;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
});
|
|
245
|
+
);
|
|
222
246
|
}
|
|
223
247
|
async toFileStat(provider, resource, stat, siblings, resolveMetadata, recurse) {
|
|
224
|
-
const {
|
|
248
|
+
const {
|
|
249
|
+
providerExtUri
|
|
250
|
+
} = this.getExtUri(provider);
|
|
225
251
|
const fileStat = {
|
|
226
252
|
resource,
|
|
227
253
|
name: providerExtUri.basename(resource),
|
|
@@ -234,7 +260,10 @@ let FileService = class FileService extends Disposable {
|
|
|
234
260
|
readonly: Boolean((stat.permissions ?? 0) & FilePermission.Readonly) || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
|
|
235
261
|
locked: Boolean((stat.permissions ?? 0) & FilePermission.Locked),
|
|
236
262
|
executable: Boolean((stat.permissions ?? 0) & FilePermission.Executable),
|
|
237
|
-
etag: etag({
|
|
263
|
+
etag: etag({
|
|
264
|
+
mtime: stat.mtime,
|
|
265
|
+
size: stat.size
|
|
266
|
+
}),
|
|
238
267
|
children: undefined
|
|
239
268
|
};
|
|
240
269
|
if (fileStat.isDirectory && recurse(fileStat, siblings)) {
|
|
@@ -243,17 +272,24 @@ let FileService = class FileService extends Disposable {
|
|
|
243
272
|
const resolvedEntries = await Promises.settled(( entries.map(async ([name, type]) => {
|
|
244
273
|
try {
|
|
245
274
|
const childResource = providerExtUri.joinPath(resource, name);
|
|
246
|
-
const childStat = resolveMetadata ? await provider.stat(childResource) : {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
275
|
+
const childStat = resolveMetadata ? await provider.stat(childResource) : {
|
|
276
|
+
type
|
|
277
|
+
};
|
|
278
|
+
return await this.toFileStat(
|
|
279
|
+
provider,
|
|
280
|
+
childResource,
|
|
281
|
+
childStat,
|
|
282
|
+
entries.length,
|
|
283
|
+
resolveMetadata,
|
|
284
|
+
recurse
|
|
285
|
+
);
|
|
286
|
+
} catch (error) {
|
|
250
287
|
this.logService.trace(error);
|
|
251
288
|
return null;
|
|
252
289
|
}
|
|
253
290
|
})));
|
|
254
291
|
fileStat.children = coalesce(resolvedEntries);
|
|
255
|
-
}
|
|
256
|
-
catch (error) {
|
|
292
|
+
} catch (error) {
|
|
257
293
|
this.logService.trace(error);
|
|
258
294
|
fileStat.children = [];
|
|
259
295
|
}
|
|
@@ -262,26 +298,40 @@ let FileService = class FileService extends Disposable {
|
|
|
262
298
|
return fileStat;
|
|
263
299
|
}
|
|
264
300
|
async resolveAll(toResolve) {
|
|
265
|
-
return Promises.settled(( toResolve.map(async
|
|
301
|
+
return Promises.settled(( toResolve.map(async entry => {
|
|
266
302
|
try {
|
|
267
|
-
return {
|
|
268
|
-
|
|
269
|
-
|
|
303
|
+
return {
|
|
304
|
+
stat: await this.doResolveFile(entry.resource, entry.options),
|
|
305
|
+
success: true
|
|
306
|
+
};
|
|
307
|
+
} catch (error) {
|
|
270
308
|
this.logService.trace(error);
|
|
271
|
-
return {
|
|
309
|
+
return {
|
|
310
|
+
stat: undefined,
|
|
311
|
+
success: false
|
|
312
|
+
};
|
|
272
313
|
}
|
|
273
314
|
})));
|
|
274
315
|
}
|
|
275
316
|
async stat(resource) {
|
|
276
317
|
const provider = await this.withProvider(resource);
|
|
277
318
|
const stat = await provider.stat(resource);
|
|
278
|
-
return this.toFileStat(
|
|
319
|
+
return this.toFileStat(
|
|
320
|
+
provider,
|
|
321
|
+
resource,
|
|
322
|
+
stat,
|
|
323
|
+
undefined,
|
|
324
|
+
true,
|
|
325
|
+
() => false
|
|
326
|
+
);
|
|
279
327
|
}
|
|
280
328
|
async realpath(resource) {
|
|
281
329
|
const provider = await this.withProvider(resource);
|
|
282
330
|
if (hasFileRealpathCapability(provider)) {
|
|
283
331
|
const realpath = await provider.realpath(resource);
|
|
284
|
-
return resource.with({
|
|
332
|
+
return resource.with({
|
|
333
|
+
path: realpath
|
|
334
|
+
});
|
|
285
335
|
}
|
|
286
336
|
return undefined;
|
|
287
337
|
}
|
|
@@ -290,16 +340,14 @@ let FileService = class FileService extends Disposable {
|
|
|
290
340
|
try {
|
|
291
341
|
const stat = await provider.stat(resource);
|
|
292
342
|
return !!stat;
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
343
|
+
} catch (error) {
|
|
295
344
|
return false;
|
|
296
345
|
}
|
|
297
346
|
}
|
|
298
347
|
async canCreateFile(resource, options) {
|
|
299
348
|
try {
|
|
300
349
|
await this.doValidateCreateFile(resource, options);
|
|
301
|
-
}
|
|
302
|
-
catch (error) {
|
|
350
|
+
} catch (error) {
|
|
303
351
|
return error;
|
|
304
352
|
}
|
|
305
353
|
return true;
|
|
@@ -313,7 +361,7 @@ let FileService = class FileService extends Disposable {
|
|
|
313
361
|
)), FileOperationResult.FILE_MODIFIED_SINCE, options));
|
|
314
362
|
}
|
|
315
363
|
}
|
|
316
|
-
async createFile(resource, bufferOrReadableOrStream = VSBuffer.fromString(
|
|
364
|
+
async createFile(resource, bufferOrReadableOrStream = VSBuffer.fromString(""), options) {
|
|
317
365
|
await this.doValidateCreateFile(resource, options);
|
|
318
366
|
const fileStat = await this.writeFile(resource, bufferOrReadableOrStream);
|
|
319
367
|
this._onDidRunOperation.fire(( new FileOperationEvent(resource, FileOperation.CREATE, fileStat)));
|
|
@@ -321,34 +369,49 @@ let FileService = class FileService extends Disposable {
|
|
|
321
369
|
}
|
|
322
370
|
async writeFile(resource, bufferOrReadableOrStream, options) {
|
|
323
371
|
const provider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(resource), resource);
|
|
324
|
-
const {
|
|
372
|
+
const {
|
|
373
|
+
providerExtUri
|
|
374
|
+
} = this.getExtUri(provider);
|
|
325
375
|
let writeFileOptions = options;
|
|
326
376
|
if (hasFileAtomicWriteCapability(provider) && !writeFileOptions?.atomic) {
|
|
327
377
|
const enforcedAtomicWrite = provider.enforceAtomicWriteFile?.(resource);
|
|
328
378
|
if (enforcedAtomicWrite) {
|
|
329
|
-
writeFileOptions = {
|
|
379
|
+
writeFileOptions = {
|
|
380
|
+
...options,
|
|
381
|
+
atomic: enforcedAtomicWrite
|
|
382
|
+
};
|
|
330
383
|
}
|
|
331
384
|
}
|
|
332
385
|
try {
|
|
333
|
-
let {
|
|
386
|
+
let {
|
|
387
|
+
stat,
|
|
388
|
+
buffer: bufferOrReadableOrStreamOrBufferedStream
|
|
389
|
+
} = await this.validateWriteFile(provider, resource, bufferOrReadableOrStream, writeFileOptions);
|
|
334
390
|
if (!stat) {
|
|
335
391
|
await this.mkdirp(provider, providerExtUri.dirname(resource));
|
|
336
392
|
}
|
|
337
393
|
if (!bufferOrReadableOrStreamOrBufferedStream) {
|
|
338
394
|
bufferOrReadableOrStreamOrBufferedStream = await this.peekBufferForWriting(provider, bufferOrReadableOrStream);
|
|
339
395
|
}
|
|
340
|
-
if (
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
396
|
+
if (
|
|
397
|
+
!hasOpenReadWriteCloseCapability(provider) || (hasReadWriteCapability(provider) && bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer) || (hasReadWriteCapability(provider) && hasFileAtomicWriteCapability(provider) && writeFileOptions?.atomic)) {
|
|
398
|
+
await this.doWriteUnbuffered(
|
|
399
|
+
provider,
|
|
400
|
+
resource,
|
|
401
|
+
writeFileOptions,
|
|
402
|
+
bufferOrReadableOrStreamOrBufferedStream
|
|
403
|
+
);
|
|
345
404
|
}
|
|
346
405
|
else {
|
|
347
|
-
await this.doWriteBuffered(
|
|
406
|
+
await this.doWriteBuffered(
|
|
407
|
+
provider,
|
|
408
|
+
resource,
|
|
409
|
+
writeFileOptions,
|
|
410
|
+
bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer ? bufferToReadable(bufferOrReadableOrStreamOrBufferedStream) : bufferOrReadableOrStreamOrBufferedStream
|
|
411
|
+
);
|
|
348
412
|
}
|
|
349
413
|
this._onDidRunOperation.fire(( new FileOperationEvent(resource, FileOperation.WRITE)));
|
|
350
|
-
}
|
|
351
|
-
catch (error) {
|
|
414
|
+
} catch (error) {
|
|
352
415
|
throw ( new FileOperationError(( localize(
|
|
353
416
|
1918,
|
|
354
417
|
"Unable to write file '{0}' ({1})",
|
|
@@ -356,7 +419,9 @@ let FileService = class FileService extends Disposable {
|
|
|
356
419
|
(ensureFileSystemProviderError(error).toString())
|
|
357
420
|
)), toFileOperationResult(error), writeFileOptions));
|
|
358
421
|
}
|
|
359
|
-
return this.resolve(resource, {
|
|
422
|
+
return this.resolve(resource, {
|
|
423
|
+
resolveMetadata: true
|
|
424
|
+
});
|
|
360
425
|
}
|
|
361
426
|
async peekBufferForWriting(provider, bufferOrReadableOrStream) {
|
|
362
427
|
let peekResult;
|
|
@@ -365,16 +430,13 @@ let FileService = class FileService extends Disposable {
|
|
|
365
430
|
const bufferedStream = await peekStream(bufferOrReadableOrStream, 3);
|
|
366
431
|
if (bufferedStream.ended) {
|
|
367
432
|
peekResult = VSBuffer.concat(bufferedStream.buffer);
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
433
|
+
} else {
|
|
370
434
|
peekResult = bufferedStream;
|
|
371
435
|
}
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
436
|
+
} else {
|
|
374
437
|
peekResult = peekReadable(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3);
|
|
375
438
|
}
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
439
|
+
} else {
|
|
378
440
|
peekResult = bufferOrReadableOrStream;
|
|
379
441
|
}
|
|
380
442
|
return peekResult;
|
|
@@ -388,25 +450,32 @@ let FileService = class FileService extends Disposable {
|
|
|
388
450
|
this.resourceForError(resource)
|
|
389
451
|
))));
|
|
390
452
|
}
|
|
453
|
+
if (options?.append && !hasFileAppendCapability(provider)) {
|
|
454
|
+
throw ( new FileOperationError(( localize(
|
|
455
|
+
1920,
|
|
456
|
+
"Filesystem provider for scheme '{0}' does not does not support append",
|
|
457
|
+
this.resourceForError(resource)
|
|
458
|
+
)), FileOperationResult.FILE_PERMISSION_DENIED));
|
|
459
|
+
}
|
|
391
460
|
const atomic = !!options?.atomic;
|
|
392
461
|
if (atomic) {
|
|
393
462
|
if (!(provider.capabilities & FileSystemProviderCapabilities.FileAtomicWrite)) {
|
|
394
463
|
throw ( new Error(( localize(
|
|
395
|
-
|
|
464
|
+
1921,
|
|
396
465
|
"Unable to atomically write file '{0}' because provider does not support it.",
|
|
397
466
|
this.resourceForError(resource)
|
|
398
467
|
))));
|
|
399
468
|
}
|
|
400
469
|
if (!(provider.capabilities & FileSystemProviderCapabilities.FileReadWrite)) {
|
|
401
470
|
throw ( new Error(( localize(
|
|
402
|
-
|
|
471
|
+
1922,
|
|
403
472
|
"Unable to atomically write file '{0}' because provider does not support unbuffered writes.",
|
|
404
473
|
this.resourceForError(resource)
|
|
405
474
|
))));
|
|
406
475
|
}
|
|
407
476
|
if (unlock) {
|
|
408
477
|
throw ( new Error(( localize(
|
|
409
|
-
|
|
478
|
+
1923,
|
|
410
479
|
"Unable to unlock file '{0}' because atomic write is enabled.",
|
|
411
480
|
this.resourceForError(resource)
|
|
412
481
|
))));
|
|
@@ -415,36 +484,46 @@ let FileService = class FileService extends Disposable {
|
|
|
415
484
|
let stat = undefined;
|
|
416
485
|
try {
|
|
417
486
|
stat = await provider.stat(resource);
|
|
418
|
-
}
|
|
419
|
-
catch (error) {
|
|
487
|
+
} catch (error) {
|
|
420
488
|
return Object.create(null);
|
|
421
489
|
}
|
|
422
490
|
if ((stat.type & FileType.Directory) !== 0) {
|
|
423
491
|
throw ( new FileOperationError(( localize(
|
|
424
|
-
|
|
492
|
+
1924,
|
|
425
493
|
"Unable to write file '{0}' that is actually a directory",
|
|
426
494
|
this.resourceForError(resource)
|
|
427
495
|
)), FileOperationResult.FILE_IS_DIRECTORY, options));
|
|
428
496
|
}
|
|
429
497
|
this.throwIfFileIsReadonly(resource, stat);
|
|
430
498
|
let buffer;
|
|
431
|
-
if (typeof options?.mtime ===
|
|
432
|
-
|
|
433
|
-
|
|
499
|
+
if (typeof options?.mtime === "number" && typeof options.etag === "string" && options.etag !== ETAG_DISABLED && typeof stat.mtime === "number" && typeof stat.size === "number" && options.mtime < stat.mtime && options.etag !== etag({
|
|
500
|
+
mtime: options.mtime ,
|
|
501
|
+
size: stat.size
|
|
502
|
+
})) {
|
|
434
503
|
buffer = await this.peekBufferForWriting(provider, bufferOrReadableOrStream);
|
|
435
504
|
if (buffer instanceof VSBuffer && buffer.byteLength === stat.size) {
|
|
436
505
|
try {
|
|
437
|
-
const {
|
|
506
|
+
const {
|
|
507
|
+
value
|
|
508
|
+
} = await this.readFile(resource, {
|
|
509
|
+
limits: {
|
|
510
|
+
size: stat.size
|
|
511
|
+
}
|
|
512
|
+
});
|
|
438
513
|
if (buffer.equals(value)) {
|
|
439
|
-
return {
|
|
514
|
+
return {
|
|
515
|
+
stat,
|
|
516
|
+
buffer
|
|
517
|
+
};
|
|
440
518
|
}
|
|
441
|
-
}
|
|
442
|
-
catch (error) {
|
|
443
|
-
}
|
|
519
|
+
} catch (error) {}
|
|
444
520
|
}
|
|
445
|
-
throw ( new FileOperationError(( localize(
|
|
521
|
+
throw ( new FileOperationError(( localize(1925, "File Modified Since")), FileOperationResult.FILE_MODIFIED_SINCE, options));
|
|
446
522
|
}
|
|
447
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
stat,
|
|
525
|
+
buffer
|
|
526
|
+
};
|
|
448
527
|
}
|
|
449
528
|
async readFile(resource, options, token) {
|
|
450
529
|
const provider = await this.withReadProvider(resource);
|
|
@@ -459,8 +538,7 @@ let FileService = class FileService extends Disposable {
|
|
|
459
538
|
try {
|
|
460
539
|
const content = await this.doReadFile(provider, resource, options, token);
|
|
461
540
|
resolve(content);
|
|
462
|
-
}
|
|
463
|
-
catch (error) {
|
|
541
|
+
} catch (error) {
|
|
464
542
|
reject(error);
|
|
465
543
|
}
|
|
466
544
|
}, this.getExtUri(provider).providerExtUri);
|
|
@@ -484,7 +562,10 @@ let FileService = class FileService extends Disposable {
|
|
|
484
562
|
const cancellableSource = ( new CancellationTokenSource(token));
|
|
485
563
|
let readFileOptions = options;
|
|
486
564
|
if (hasFileAtomicReadCapability(provider) && provider.enforceAtomicReadFile?.(resource)) {
|
|
487
|
-
readFileOptions = {
|
|
565
|
+
readFileOptions = {
|
|
566
|
+
...options,
|
|
567
|
+
atomic: true
|
|
568
|
+
};
|
|
488
569
|
}
|
|
489
570
|
const statPromise = this.validateReadFile(resource, readFileOptions).then(stat => stat, error => {
|
|
490
571
|
cancellableSource.dispose(true);
|
|
@@ -492,13 +573,11 @@ let FileService = class FileService extends Disposable {
|
|
|
492
573
|
});
|
|
493
574
|
let fileStream = undefined;
|
|
494
575
|
try {
|
|
495
|
-
if (typeof readFileOptions?.etag ===
|
|
576
|
+
if (typeof readFileOptions?.etag === "string" && readFileOptions.etag !== ETAG_DISABLED) {
|
|
496
577
|
await statPromise;
|
|
497
578
|
}
|
|
498
|
-
if (
|
|
499
|
-
|
|
500
|
-
(hasReadWriteCapability(provider) && readFileOptions?.preferUnbuffered)
|
|
501
|
-
) {
|
|
579
|
+
if (
|
|
580
|
+
(readFileOptions?.atomic && hasFileAtomicReadCapability(provider)) || !(hasOpenReadWriteCloseCapability(provider) || hasFileReadStreamCapability(provider)) || (hasReadWriteCapability(provider) && readFileOptions?.preferUnbuffered)) {
|
|
502
581
|
fileStream = this.readFileUnbuffered(provider, resource, readFileOptions);
|
|
503
582
|
}
|
|
504
583
|
else if (hasFileReadStreamCapability(provider)) {
|
|
@@ -507,15 +586,14 @@ let FileService = class FileService extends Disposable {
|
|
|
507
586
|
else {
|
|
508
587
|
fileStream = this.readFileBuffered(provider, resource, cancellableSource.token, readFileOptions);
|
|
509
588
|
}
|
|
510
|
-
fileStream.on(
|
|
511
|
-
fileStream.on(
|
|
589
|
+
fileStream.on("end", () => cancellableSource.dispose());
|
|
590
|
+
fileStream.on("error", () => cancellableSource.dispose());
|
|
512
591
|
const fileStat = await statPromise;
|
|
513
592
|
return {
|
|
514
593
|
...fileStat,
|
|
515
594
|
value: fileStream
|
|
516
595
|
};
|
|
517
|
-
}
|
|
518
|
-
catch (error) {
|
|
596
|
+
} catch (error) {
|
|
519
597
|
if (fileStream) {
|
|
520
598
|
await consumeStream(fileStream);
|
|
521
599
|
}
|
|
@@ -524,7 +602,7 @@ let FileService = class FileService extends Disposable {
|
|
|
524
602
|
}
|
|
525
603
|
restoreReadError(error, resource, options) {
|
|
526
604
|
const message = ( localize(
|
|
527
|
-
|
|
605
|
+
1926,
|
|
528
606
|
"Unable to read file '{0}' ({1})",
|
|
529
607
|
this.resourceForError(resource),
|
|
530
608
|
(ensureFileSystemProviderError(error).toString())
|
|
@@ -559,21 +637,21 @@ let FileService = class FileService extends Disposable {
|
|
|
559
637
|
try {
|
|
560
638
|
let buffer;
|
|
561
639
|
if (options?.atomic && hasFileAtomicReadCapability(provider)) {
|
|
562
|
-
buffer = await provider.readFile(resource, {
|
|
563
|
-
|
|
564
|
-
|
|
640
|
+
buffer = await provider.readFile(resource, {
|
|
641
|
+
atomic: true
|
|
642
|
+
});
|
|
643
|
+
} else {
|
|
565
644
|
buffer = await provider.readFile(resource);
|
|
566
645
|
}
|
|
567
|
-
if (typeof options?.position ===
|
|
646
|
+
if (typeof options?.position === "number") {
|
|
568
647
|
buffer = buffer.slice(options.position);
|
|
569
648
|
}
|
|
570
|
-
if (typeof options?.length ===
|
|
649
|
+
if (typeof options?.length === "number") {
|
|
571
650
|
buffer = buffer.slice(0, options.length);
|
|
572
651
|
}
|
|
573
652
|
this.validateReadFileLimits(resource, buffer.byteLength, options);
|
|
574
653
|
stream.end(VSBuffer.wrap(buffer));
|
|
575
|
-
}
|
|
576
|
-
catch (err) {
|
|
654
|
+
} catch (err) {
|
|
577
655
|
stream.error(err);
|
|
578
656
|
stream.end();
|
|
579
657
|
}
|
|
@@ -581,43 +659,44 @@ let FileService = class FileService extends Disposable {
|
|
|
581
659
|
return stream;
|
|
582
660
|
}
|
|
583
661
|
async validateReadFile(resource, options) {
|
|
584
|
-
const stat = await this.resolve(resource, {
|
|
662
|
+
const stat = await this.resolve(resource, {
|
|
663
|
+
resolveMetadata: true
|
|
664
|
+
});
|
|
585
665
|
if (stat.isDirectory) {
|
|
586
666
|
throw ( new FileOperationError(( localize(
|
|
587
|
-
|
|
667
|
+
1927,
|
|
588
668
|
"Unable to read file '{0}' that is actually a directory",
|
|
589
669
|
this.resourceForError(resource)
|
|
590
670
|
)), FileOperationResult.FILE_IS_DIRECTORY, options));
|
|
591
671
|
}
|
|
592
|
-
if (typeof options?.etag ===
|
|
593
|
-
throw ( new NotModifiedSinceFileOperationError(( localize(
|
|
672
|
+
if (typeof options?.etag === "string" && options.etag !== ETAG_DISABLED && options.etag === stat.etag) {
|
|
673
|
+
throw ( new NotModifiedSinceFileOperationError(( localize(1928, "File not modified since")), stat, options));
|
|
594
674
|
}
|
|
595
675
|
this.validateReadFileLimits(resource, stat.size, options);
|
|
596
676
|
return stat;
|
|
597
677
|
}
|
|
598
678
|
validateReadFileLimits(resource, size, options) {
|
|
599
|
-
if (typeof options?.limits?.size ===
|
|
679
|
+
if (typeof options?.limits?.size === "number" && size > options.limits.size) {
|
|
600
680
|
throw ( new TooLargeFileOperationError(( localize(
|
|
601
|
-
|
|
681
|
+
1929,
|
|
602
682
|
"Unable to read file '{0}' that is too large to open",
|
|
603
683
|
this.resourceForError(resource)
|
|
604
684
|
)), FileOperationResult.FILE_TOO_LARGE, size, options));
|
|
605
685
|
}
|
|
606
686
|
}
|
|
607
687
|
async canMove(source, target, overwrite) {
|
|
608
|
-
return this.doCanMoveCopy(source, target,
|
|
688
|
+
return this.doCanMoveCopy(source, target, "move", overwrite);
|
|
609
689
|
}
|
|
610
690
|
async canCopy(source, target, overwrite) {
|
|
611
|
-
return this.doCanMoveCopy(source, target,
|
|
691
|
+
return this.doCanMoveCopy(source, target, "copy", overwrite);
|
|
612
692
|
}
|
|
613
693
|
async doCanMoveCopy(source, target, mode, overwrite) {
|
|
614
694
|
if (( source.toString()) !== ( target.toString())) {
|
|
615
695
|
try {
|
|
616
|
-
const sourceProvider = mode ===
|
|
696
|
+
const sourceProvider = mode === "move" ? this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source), source) : await this.withReadProvider(source);
|
|
617
697
|
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
|
|
618
698
|
await this.doValidateMoveCopy(sourceProvider, source, targetProvider, target, mode, overwrite);
|
|
619
|
-
}
|
|
620
|
-
catch (error) {
|
|
699
|
+
} catch (error) {
|
|
621
700
|
return error;
|
|
622
701
|
}
|
|
623
702
|
}
|
|
@@ -626,11 +705,13 @@ let FileService = class FileService extends Disposable {
|
|
|
626
705
|
async move(source, target, overwrite) {
|
|
627
706
|
const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source), source);
|
|
628
707
|
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
|
|
629
|
-
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target,
|
|
630
|
-
const fileStat = await this.resolve(target, {
|
|
708
|
+
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, "move", !!overwrite);
|
|
709
|
+
const fileStat = await this.resolve(target, {
|
|
710
|
+
resolveMetadata: true
|
|
711
|
+
});
|
|
631
712
|
this._onDidRunOperation.fire(( new FileOperationEvent(
|
|
632
713
|
source,
|
|
633
|
-
mode ===
|
|
714
|
+
mode === "move" ? FileOperation.MOVE : FileOperation.COPY,
|
|
634
715
|
fileStat
|
|
635
716
|
)));
|
|
636
717
|
return fileStat;
|
|
@@ -638,11 +719,13 @@ let FileService = class FileService extends Disposable {
|
|
|
638
719
|
async copy(source, target, overwrite) {
|
|
639
720
|
const sourceProvider = await this.withReadProvider(source);
|
|
640
721
|
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target), target);
|
|
641
|
-
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target,
|
|
642
|
-
const fileStat = await this.resolve(target, {
|
|
722
|
+
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, "copy", !!overwrite);
|
|
723
|
+
const fileStat = await this.resolve(target, {
|
|
724
|
+
resolveMetadata: true
|
|
725
|
+
});
|
|
643
726
|
this._onDidRunOperation.fire(( new FileOperationEvent(
|
|
644
727
|
source,
|
|
645
|
-
mode ===
|
|
728
|
+
mode === "copy" ? FileOperation.COPY : FileOperation.MOVE,
|
|
646
729
|
fileStat
|
|
647
730
|
)));
|
|
648
731
|
return fileStat;
|
|
@@ -651,21 +734,30 @@ let FileService = class FileService extends Disposable {
|
|
|
651
734
|
if (( source.toString()) === ( target.toString())) {
|
|
652
735
|
return mode;
|
|
653
736
|
}
|
|
654
|
-
const {
|
|
737
|
+
const {
|
|
738
|
+
exists,
|
|
739
|
+
isSameResourceWithDifferentPathCase
|
|
740
|
+
} = await this.doValidateMoveCopy(sourceProvider, source, targetProvider, target, mode, overwrite);
|
|
655
741
|
if (exists && !isSameResourceWithDifferentPathCase && overwrite) {
|
|
656
|
-
await this.del(target, {
|
|
742
|
+
await this.del(target, {
|
|
743
|
+
recursive: true
|
|
744
|
+
});
|
|
657
745
|
}
|
|
658
|
-
await this.mkdirp(
|
|
659
|
-
|
|
746
|
+
await this.mkdirp(
|
|
747
|
+
targetProvider,
|
|
748
|
+
this.getExtUri(targetProvider).providerExtUri.dirname(target)
|
|
749
|
+
);
|
|
750
|
+
if (mode === "copy") {
|
|
660
751
|
if (sourceProvider === targetProvider && hasFileFolderCopyCapability(sourceProvider)) {
|
|
661
|
-
await sourceProvider.copy(source, target, {
|
|
752
|
+
await sourceProvider.copy(source, target, {
|
|
753
|
+
overwrite
|
|
754
|
+
});
|
|
662
755
|
}
|
|
663
756
|
else {
|
|
664
757
|
const sourceFile = await this.resolve(source);
|
|
665
758
|
if (sourceFile.isDirectory) {
|
|
666
759
|
await this.doCopyFolder(sourceProvider, sourceFile, targetProvider, target);
|
|
667
|
-
}
|
|
668
|
-
else {
|
|
760
|
+
} else {
|
|
669
761
|
await this.doCopyFile(sourceProvider, source, targetProvider, target);
|
|
670
762
|
}
|
|
671
763
|
}
|
|
@@ -673,13 +765,17 @@ let FileService = class FileService extends Disposable {
|
|
|
673
765
|
}
|
|
674
766
|
else {
|
|
675
767
|
if (sourceProvider === targetProvider) {
|
|
676
|
-
await sourceProvider.rename(source, target, {
|
|
768
|
+
await sourceProvider.rename(source, target, {
|
|
769
|
+
overwrite
|
|
770
|
+
});
|
|
677
771
|
return mode;
|
|
678
772
|
}
|
|
679
773
|
else {
|
|
680
|
-
await this.doMoveCopy(sourceProvider, source, targetProvider, target,
|
|
681
|
-
await this.del(source, {
|
|
682
|
-
|
|
774
|
+
await this.doMoveCopy(sourceProvider, source, targetProvider, target, "copy", overwrite);
|
|
775
|
+
await this.del(source, {
|
|
776
|
+
recursive: true
|
|
777
|
+
});
|
|
778
|
+
return "copy";
|
|
683
779
|
}
|
|
684
780
|
}
|
|
685
781
|
}
|
|
@@ -700,12 +796,16 @@ let FileService = class FileService extends Disposable {
|
|
|
700
796
|
async doCopyFolder(sourceProvider, sourceFolder, targetProvider, targetFolder) {
|
|
701
797
|
await targetProvider.mkdir(targetFolder);
|
|
702
798
|
if (Array.isArray(sourceFolder.children)) {
|
|
703
|
-
await Promises.settled(( sourceFolder.children.map(async
|
|
799
|
+
await Promises.settled(( sourceFolder.children.map(async sourceChild => {
|
|
704
800
|
const targetChild = this.getExtUri(targetProvider).providerExtUri.joinPath(targetFolder, sourceChild.name);
|
|
705
801
|
if (sourceChild.isDirectory) {
|
|
706
|
-
return this.doCopyFolder(
|
|
707
|
-
|
|
708
|
-
|
|
802
|
+
return this.doCopyFolder(
|
|
803
|
+
sourceProvider,
|
|
804
|
+
await this.resolve(sourceChild.resource),
|
|
805
|
+
targetProvider,
|
|
806
|
+
targetChild
|
|
807
|
+
);
|
|
808
|
+
} else {
|
|
709
809
|
return this.doCopyFile(sourceProvider, sourceChild.resource, targetProvider, targetChild);
|
|
710
810
|
}
|
|
711
811
|
})));
|
|
@@ -714,13 +814,16 @@ let FileService = class FileService extends Disposable {
|
|
|
714
814
|
async doValidateMoveCopy(sourceProvider, source, targetProvider, target, mode, overwrite) {
|
|
715
815
|
let isSameResourceWithDifferentPathCase = false;
|
|
716
816
|
if (sourceProvider === targetProvider) {
|
|
717
|
-
const {
|
|
817
|
+
const {
|
|
818
|
+
providerExtUri,
|
|
819
|
+
isPathCaseSensitive
|
|
820
|
+
} = this.getExtUri(sourceProvider);
|
|
718
821
|
if (!isPathCaseSensitive) {
|
|
719
822
|
isSameResourceWithDifferentPathCase = providerExtUri.isEqual(source, target);
|
|
720
823
|
}
|
|
721
|
-
if (isSameResourceWithDifferentPathCase && mode ===
|
|
824
|
+
if (isSameResourceWithDifferentPathCase && mode === "copy") {
|
|
722
825
|
throw ( new Error(( localize(
|
|
723
|
-
|
|
826
|
+
1930,
|
|
724
827
|
"Unable to copy when source '{0}' is same as target '{1}' with different path case on a case insensitive file system",
|
|
725
828
|
this.resourceForError(source),
|
|
726
829
|
this.resourceForError(target)
|
|
@@ -728,7 +831,7 @@ let FileService = class FileService extends Disposable {
|
|
|
728
831
|
}
|
|
729
832
|
if (!isSameResourceWithDifferentPathCase && providerExtUri.isEqualOrParent(target, source)) {
|
|
730
833
|
throw ( new Error(( localize(
|
|
731
|
-
|
|
834
|
+
1931,
|
|
732
835
|
"Unable to move/copy when source '{0}' is parent of target '{1}'.",
|
|
733
836
|
this.resourceForError(source),
|
|
734
837
|
this.resourceForError(target)
|
|
@@ -739,17 +842,19 @@ let FileService = class FileService extends Disposable {
|
|
|
739
842
|
if (exists && !isSameResourceWithDifferentPathCase) {
|
|
740
843
|
if (!overwrite) {
|
|
741
844
|
throw ( new FileOperationError(( localize(
|
|
742
|
-
|
|
845
|
+
1932,
|
|
743
846
|
"Unable to move/copy '{0}' because target '{1}' already exists at destination.",
|
|
744
847
|
this.resourceForError(source),
|
|
745
848
|
this.resourceForError(target)
|
|
746
849
|
)), FileOperationResult.FILE_MOVE_CONFLICT));
|
|
747
850
|
}
|
|
748
851
|
if (sourceProvider === targetProvider) {
|
|
749
|
-
const {
|
|
852
|
+
const {
|
|
853
|
+
providerExtUri
|
|
854
|
+
} = this.getExtUri(sourceProvider);
|
|
750
855
|
if (providerExtUri.isEqualOrParent(source, target)) {
|
|
751
856
|
throw ( new Error(( localize(
|
|
752
|
-
|
|
857
|
+
1933,
|
|
753
858
|
"Unable to move/copy '{0}' into '{1}' since a file would replace the folder it is contained in.",
|
|
754
859
|
this.resourceForError(source),
|
|
755
860
|
this.resourceForError(target)
|
|
@@ -757,7 +862,10 @@ let FileService = class FileService extends Disposable {
|
|
|
757
862
|
}
|
|
758
863
|
}
|
|
759
864
|
}
|
|
760
|
-
return {
|
|
865
|
+
return {
|
|
866
|
+
exists,
|
|
867
|
+
isSameResourceWithDifferentPathCase
|
|
868
|
+
};
|
|
761
869
|
}
|
|
762
870
|
getExtUri(provider) {
|
|
763
871
|
const isPathCaseSensitive = this.isPathCaseSensitive(provider);
|
|
@@ -772,19 +880,22 @@ let FileService = class FileService extends Disposable {
|
|
|
772
880
|
async createFolder(resource) {
|
|
773
881
|
const provider = this.throwIfFileSystemIsReadonly(await this.withProvider(resource), resource);
|
|
774
882
|
await this.mkdirp(provider, resource);
|
|
775
|
-
const fileStat = await this.resolve(resource, {
|
|
883
|
+
const fileStat = await this.resolve(resource, {
|
|
884
|
+
resolveMetadata: true
|
|
885
|
+
});
|
|
776
886
|
this._onDidRunOperation.fire(( new FileOperationEvent(resource, FileOperation.CREATE, fileStat)));
|
|
777
887
|
return fileStat;
|
|
778
888
|
}
|
|
779
889
|
async mkdirp(provider, directory) {
|
|
780
|
-
const {
|
|
890
|
+
const {
|
|
891
|
+
providerExtUri
|
|
892
|
+
} = this.getExtUri(provider);
|
|
781
893
|
return mkdirp(providerExtUri, provider, directory);
|
|
782
894
|
}
|
|
783
895
|
async canDelete(resource, options) {
|
|
784
896
|
try {
|
|
785
897
|
await this.doValidateDelete(resource, options);
|
|
786
|
-
}
|
|
787
|
-
catch (error) {
|
|
898
|
+
} catch (error) {
|
|
788
899
|
return error;
|
|
789
900
|
}
|
|
790
901
|
return true;
|
|
@@ -794,7 +905,7 @@ let FileService = class FileService extends Disposable {
|
|
|
794
905
|
const useTrash = !!options?.useTrash;
|
|
795
906
|
if (useTrash && !(provider.capabilities & FileSystemProviderCapabilities.Trash)) {
|
|
796
907
|
throw ( new Error(( localize(
|
|
797
|
-
|
|
908
|
+
1934,
|
|
798
909
|
"Unable to delete file '{0}' via trash because provider does not support it.",
|
|
799
910
|
this.resourceForError(resource)
|
|
800
911
|
))));
|
|
@@ -802,14 +913,14 @@ let FileService = class FileService extends Disposable {
|
|
|
802
913
|
const atomic = options?.atomic;
|
|
803
914
|
if (atomic && !(provider.capabilities & FileSystemProviderCapabilities.FileAtomicDelete)) {
|
|
804
915
|
throw ( new Error(( localize(
|
|
805
|
-
|
|
916
|
+
1935,
|
|
806
917
|
"Unable to delete file '{0}' atomically because provider does not support it.",
|
|
807
918
|
this.resourceForError(resource)
|
|
808
919
|
))));
|
|
809
920
|
}
|
|
810
921
|
if (useTrash && atomic) {
|
|
811
922
|
throw ( new Error(( localize(
|
|
812
|
-
|
|
923
|
+
1936,
|
|
813
924
|
"Unable to atomically delete file '{0}' because using trash is enabled.",
|
|
814
925
|
this.resourceForError(resource)
|
|
815
926
|
))));
|
|
@@ -817,15 +928,12 @@ let FileService = class FileService extends Disposable {
|
|
|
817
928
|
let stat = undefined;
|
|
818
929
|
try {
|
|
819
930
|
stat = await provider.stat(resource);
|
|
820
|
-
}
|
|
821
|
-
catch (error) {
|
|
822
|
-
}
|
|
931
|
+
} catch (error) {}
|
|
823
932
|
if (stat) {
|
|
824
933
|
this.throwIfFileIsReadonly(resource, stat);
|
|
825
|
-
}
|
|
826
|
-
else {
|
|
934
|
+
} else {
|
|
827
935
|
throw ( new FileOperationError(( localize(
|
|
828
|
-
|
|
936
|
+
1937,
|
|
829
937
|
"Unable to delete nonexistent file '{0}'",
|
|
830
938
|
this.resourceForError(resource)
|
|
831
939
|
)), FileOperationResult.FILE_NOT_FOUND));
|
|
@@ -835,7 +943,7 @@ let FileService = class FileService extends Disposable {
|
|
|
835
943
|
const stat = await this.resolve(resource);
|
|
836
944
|
if (stat.isDirectory && Array.isArray(stat.children) && stat.children.length > 0) {
|
|
837
945
|
throw ( new Error(( localize(
|
|
838
|
-
|
|
946
|
+
1938,
|
|
839
947
|
"Unable to delete non-empty folder '{0}'.",
|
|
840
948
|
this.resourceForError(resource)
|
|
841
949
|
))));
|
|
@@ -849,13 +957,20 @@ let FileService = class FileService extends Disposable {
|
|
|
849
957
|
if (hasFileAtomicDeleteCapability(provider) && !deleteFileOptions?.atomic) {
|
|
850
958
|
const enforcedAtomicDelete = provider.enforceAtomicDelete?.(resource);
|
|
851
959
|
if (enforcedAtomicDelete) {
|
|
852
|
-
deleteFileOptions = {
|
|
960
|
+
deleteFileOptions = {
|
|
961
|
+
...options,
|
|
962
|
+
atomic: enforcedAtomicDelete
|
|
963
|
+
};
|
|
853
964
|
}
|
|
854
965
|
}
|
|
855
966
|
const useTrash = !!deleteFileOptions?.useTrash;
|
|
856
967
|
const recursive = !!deleteFileOptions?.recursive;
|
|
857
968
|
const atomic = deleteFileOptions?.atomic ?? false;
|
|
858
|
-
await provider.delete(resource, {
|
|
969
|
+
await provider.delete(resource, {
|
|
970
|
+
recursive,
|
|
971
|
+
useTrash,
|
|
972
|
+
atomic
|
|
973
|
+
});
|
|
859
974
|
this._onDidRunOperation.fire(( new FileOperationEvent(resource, FileOperation.DELETE)));
|
|
860
975
|
}
|
|
861
976
|
async cloneFile(source, target) {
|
|
@@ -867,40 +982,57 @@ let FileService = class FileService extends Disposable {
|
|
|
867
982
|
if (sourceProvider === targetProvider && hasFileCloneCapability(sourceProvider)) {
|
|
868
983
|
return sourceProvider.cloneFile(source, target);
|
|
869
984
|
}
|
|
870
|
-
await this.mkdirp(
|
|
985
|
+
await this.mkdirp(
|
|
986
|
+
targetProvider,
|
|
987
|
+
this.getExtUri(targetProvider).providerExtUri.dirname(target)
|
|
988
|
+
);
|
|
871
989
|
if (sourceProvider === targetProvider && hasFileFolderCopyCapability(sourceProvider)) {
|
|
872
|
-
return this.writeQueue.queueFor(source, () => sourceProvider.copy(source, target, {
|
|
990
|
+
return this.writeQueue.queueFor(source, () => sourceProvider.copy(source, target, {
|
|
991
|
+
overwrite: true
|
|
992
|
+
}), this.getExtUri(sourceProvider).providerExtUri);
|
|
873
993
|
}
|
|
874
|
-
return this.writeQueue.queueFor(
|
|
994
|
+
return this.writeQueue.queueFor(
|
|
995
|
+
source,
|
|
996
|
+
() => this.doCopyFile(sourceProvider, source, targetProvider, target),
|
|
997
|
+
this.getExtUri(sourceProvider).providerExtUri
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
static {
|
|
1001
|
+
this.WATCHER_CORRELATION_IDS = 0;
|
|
875
1002
|
}
|
|
876
|
-
static { this.WATCHER_CORRELATION_IDS = 0; }
|
|
877
1003
|
createWatcher(resource, options) {
|
|
878
1004
|
return this.watch(resource, {
|
|
879
1005
|
...options,
|
|
880
1006
|
correlationId: FileService_1.WATCHER_CORRELATION_IDS++
|
|
881
1007
|
});
|
|
882
1008
|
}
|
|
883
|
-
watch(
|
|
1009
|
+
watch(
|
|
1010
|
+
resource,
|
|
1011
|
+
options = {
|
|
1012
|
+
recursive: false,
|
|
1013
|
+
excludes: []
|
|
1014
|
+
}
|
|
1015
|
+
) {
|
|
884
1016
|
const disposables = ( new DisposableStore());
|
|
885
1017
|
let watchDisposed = false;
|
|
886
|
-
let disposeWatch = () => {
|
|
1018
|
+
let disposeWatch = () => {
|
|
1019
|
+
watchDisposed = true;
|
|
1020
|
+
};
|
|
887
1021
|
disposables.add(toDisposable(() => disposeWatch()));
|
|
888
1022
|
(async () => {
|
|
889
1023
|
try {
|
|
890
1024
|
const disposable = await this.doWatch(resource, options);
|
|
891
1025
|
if (watchDisposed) {
|
|
892
1026
|
dispose(disposable);
|
|
893
|
-
}
|
|
894
|
-
else {
|
|
1027
|
+
} else {
|
|
895
1028
|
disposeWatch = () => dispose(disposable);
|
|
896
1029
|
}
|
|
897
|
-
}
|
|
898
|
-
catch (error) {
|
|
1030
|
+
} catch (error) {
|
|
899
1031
|
this.logService.error(error);
|
|
900
1032
|
}
|
|
901
1033
|
})();
|
|
902
1034
|
const correlationId = options.correlationId;
|
|
903
|
-
if (typeof correlationId ===
|
|
1035
|
+
if (typeof correlationId === "number") {
|
|
904
1036
|
const fileChangeEmitter = disposables.add(( new Emitter()));
|
|
905
1037
|
disposables.add(this.internalOnDidFilesChange.event(e => {
|
|
906
1038
|
if (e.correlates(correlationId)) {
|
|
@@ -917,7 +1049,10 @@ let FileService = class FileService extends Disposable {
|
|
|
917
1049
|
}
|
|
918
1050
|
async doWatch(resource, options) {
|
|
919
1051
|
const provider = await this.withProvider(resource);
|
|
920
|
-
const watchHash = hash([
|
|
1052
|
+
const watchHash = hash([
|
|
1053
|
+
this.getExtUri(provider).providerExtUri.getComparisonKey(resource),
|
|
1054
|
+
options
|
|
1055
|
+
]);
|
|
921
1056
|
let watcher = this.activeWatchers.get(watchHash);
|
|
922
1057
|
if (!watcher) {
|
|
923
1058
|
watcher = {
|
|
@@ -946,19 +1081,20 @@ let FileService = class FileService extends Disposable {
|
|
|
946
1081
|
}
|
|
947
1082
|
async doWriteBuffered(provider, resource, options, readableOrStreamOrBufferedStream) {
|
|
948
1083
|
return this.writeQueue.queueFor(resource, async () => {
|
|
949
|
-
const handle = await provider.open(resource, {
|
|
1084
|
+
const handle = await provider.open(resource, {
|
|
1085
|
+
create: true,
|
|
1086
|
+
unlock: options?.unlock ?? false,
|
|
1087
|
+
append: options?.append ?? false
|
|
1088
|
+
});
|
|
950
1089
|
try {
|
|
951
1090
|
if (isReadableStream(readableOrStreamOrBufferedStream) || isReadableBufferedStream(readableOrStreamOrBufferedStream)) {
|
|
952
1091
|
await this.doWriteStreamBufferedQueued(provider, handle, readableOrStreamOrBufferedStream);
|
|
953
|
-
}
|
|
954
|
-
else {
|
|
1092
|
+
} else {
|
|
955
1093
|
await this.doWriteReadableBufferedQueued(provider, handle, readableOrStreamOrBufferedStream);
|
|
956
1094
|
}
|
|
957
|
-
}
|
|
958
|
-
catch (error) {
|
|
1095
|
+
} catch (error) {
|
|
959
1096
|
throw ensureFileSystemProviderError(error);
|
|
960
|
-
}
|
|
961
|
-
finally {
|
|
1097
|
+
} finally {
|
|
962
1098
|
await provider.close(handle);
|
|
963
1099
|
}
|
|
964
1100
|
}, this.getExtUri(provider).providerExtUri);
|
|
@@ -982,12 +1118,11 @@ let FileService = class FileService extends Disposable {
|
|
|
982
1118
|
}
|
|
983
1119
|
return ( new Promise((resolve, reject) => {
|
|
984
1120
|
listenStream(stream, {
|
|
985
|
-
onData: async
|
|
1121
|
+
onData: async chunk => {
|
|
986
1122
|
stream.pause();
|
|
987
1123
|
try {
|
|
988
1124
|
await this.doWriteBuffer(provider, handle, chunk, chunk.byteLength, posInFile, 0);
|
|
989
|
-
}
|
|
990
|
-
catch (error) {
|
|
1125
|
+
} catch (error) {
|
|
991
1126
|
return reject(error);
|
|
992
1127
|
}
|
|
993
1128
|
posInFile += chunk.byteLength;
|
|
@@ -1009,44 +1144,72 @@ let FileService = class FileService extends Disposable {
|
|
|
1009
1144
|
async doWriteBuffer(provider, handle, buffer, length, posInFile, posInBuffer) {
|
|
1010
1145
|
let totalBytesWritten = 0;
|
|
1011
1146
|
while (totalBytesWritten < length) {
|
|
1012
|
-
const bytesWritten = await provider.write(
|
|
1147
|
+
const bytesWritten = await provider.write(
|
|
1148
|
+
handle,
|
|
1149
|
+
posInFile + totalBytesWritten,
|
|
1150
|
+
buffer.buffer,
|
|
1151
|
+
posInBuffer + totalBytesWritten,
|
|
1152
|
+
length - totalBytesWritten
|
|
1153
|
+
);
|
|
1013
1154
|
totalBytesWritten += bytesWritten;
|
|
1014
1155
|
}
|
|
1015
1156
|
}
|
|
1016
1157
|
async doWriteUnbuffered(provider, resource, options, bufferOrReadableOrStreamOrBufferedStream) {
|
|
1017
|
-
return this.writeQueue.queueFor(
|
|
1158
|
+
return this.writeQueue.queueFor(
|
|
1159
|
+
resource,
|
|
1160
|
+
() => this.doWriteUnbufferedQueued(provider, resource, options, bufferOrReadableOrStreamOrBufferedStream),
|
|
1161
|
+
this.getExtUri(provider).providerExtUri
|
|
1162
|
+
);
|
|
1018
1163
|
}
|
|
1019
1164
|
async doWriteUnbufferedQueued(provider, resource, options, bufferOrReadableOrStreamOrBufferedStream) {
|
|
1020
1165
|
let buffer;
|
|
1021
1166
|
if (bufferOrReadableOrStreamOrBufferedStream instanceof VSBuffer) {
|
|
1022
1167
|
buffer = bufferOrReadableOrStreamOrBufferedStream;
|
|
1023
|
-
}
|
|
1024
|
-
else if (isReadableStream(bufferOrReadableOrStreamOrBufferedStream)) {
|
|
1168
|
+
} else if (isReadableStream(bufferOrReadableOrStreamOrBufferedStream)) {
|
|
1025
1169
|
buffer = await streamToBuffer(bufferOrReadableOrStreamOrBufferedStream);
|
|
1026
|
-
}
|
|
1027
|
-
else if (isReadableBufferedStream(bufferOrReadableOrStreamOrBufferedStream)) {
|
|
1170
|
+
} else if (isReadableBufferedStream(bufferOrReadableOrStreamOrBufferedStream)) {
|
|
1028
1171
|
buffer = await bufferedStreamToBuffer(bufferOrReadableOrStreamOrBufferedStream);
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1172
|
+
} else {
|
|
1031
1173
|
buffer = readableToBuffer(bufferOrReadableOrStreamOrBufferedStream);
|
|
1032
1174
|
}
|
|
1033
|
-
await provider.writeFile(resource, buffer.buffer, {
|
|
1175
|
+
await provider.writeFile(resource, buffer.buffer, {
|
|
1176
|
+
create: true,
|
|
1177
|
+
overwrite: true,
|
|
1178
|
+
unlock: options?.unlock ?? false,
|
|
1179
|
+
atomic: options?.atomic ?? false,
|
|
1180
|
+
append: options?.append ?? false
|
|
1181
|
+
});
|
|
1034
1182
|
}
|
|
1035
1183
|
async doPipeBuffered(sourceProvider, source, targetProvider, target) {
|
|
1036
|
-
return this.writeQueue.queueFor(
|
|
1184
|
+
return this.writeQueue.queueFor(
|
|
1185
|
+
target,
|
|
1186
|
+
() => this.doPipeBufferedQueued(sourceProvider, source, targetProvider, target),
|
|
1187
|
+
this.getExtUri(targetProvider).providerExtUri
|
|
1188
|
+
);
|
|
1037
1189
|
}
|
|
1038
1190
|
async doPipeBufferedQueued(sourceProvider, source, targetProvider, target) {
|
|
1039
1191
|
let sourceHandle = undefined;
|
|
1040
1192
|
let targetHandle = undefined;
|
|
1041
1193
|
try {
|
|
1042
|
-
sourceHandle = await sourceProvider.open(source, {
|
|
1043
|
-
|
|
1194
|
+
sourceHandle = await sourceProvider.open(source, {
|
|
1195
|
+
create: false
|
|
1196
|
+
});
|
|
1197
|
+
targetHandle = await targetProvider.open(target, {
|
|
1198
|
+
create: true,
|
|
1199
|
+
unlock: false
|
|
1200
|
+
});
|
|
1044
1201
|
const buffer = VSBuffer.alloc(this.BUFFER_SIZE);
|
|
1045
1202
|
let posInFile = 0;
|
|
1046
1203
|
let posInBuffer = 0;
|
|
1047
1204
|
let bytesRead = 0;
|
|
1048
1205
|
do {
|
|
1049
|
-
bytesRead = await sourceProvider.read(
|
|
1206
|
+
bytesRead = await sourceProvider.read(
|
|
1207
|
+
sourceHandle,
|
|
1208
|
+
posInFile,
|
|
1209
|
+
buffer.buffer,
|
|
1210
|
+
posInBuffer,
|
|
1211
|
+
buffer.byteLength - posInBuffer
|
|
1212
|
+
);
|
|
1050
1213
|
await this.doWriteBuffer(targetProvider, targetHandle, buffer, bytesRead, posInFile, posInBuffer);
|
|
1051
1214
|
posInFile += bytesRead;
|
|
1052
1215
|
posInBuffer += bytesRead;
|
|
@@ -1054,36 +1217,55 @@ let FileService = class FileService extends Disposable {
|
|
|
1054
1217
|
posInBuffer = 0;
|
|
1055
1218
|
}
|
|
1056
1219
|
} while (bytesRead > 0);
|
|
1057
|
-
}
|
|
1058
|
-
catch (error) {
|
|
1220
|
+
} catch (error) {
|
|
1059
1221
|
throw ensureFileSystemProviderError(error);
|
|
1060
|
-
}
|
|
1061
|
-
finally {
|
|
1222
|
+
} finally {
|
|
1062
1223
|
await Promises.settled([
|
|
1063
|
-
typeof sourceHandle ===
|
|
1064
|
-
typeof targetHandle ===
|
|
1224
|
+
typeof sourceHandle === "number" ? sourceProvider.close(sourceHandle) : Promise.resolve(),
|
|
1225
|
+
typeof targetHandle === "number" ? targetProvider.close(targetHandle) : Promise.resolve()
|
|
1065
1226
|
]);
|
|
1066
1227
|
}
|
|
1067
1228
|
}
|
|
1068
1229
|
async doPipeUnbuffered(sourceProvider, source, targetProvider, target) {
|
|
1069
|
-
return this.writeQueue.queueFor(
|
|
1230
|
+
return this.writeQueue.queueFor(
|
|
1231
|
+
target,
|
|
1232
|
+
() => this.doPipeUnbufferedQueued(sourceProvider, source, targetProvider, target),
|
|
1233
|
+
this.getExtUri(targetProvider).providerExtUri
|
|
1234
|
+
);
|
|
1070
1235
|
}
|
|
1071
1236
|
async doPipeUnbufferedQueued(sourceProvider, source, targetProvider, target) {
|
|
1072
|
-
return targetProvider.writeFile(target, await sourceProvider.readFile(source), {
|
|
1237
|
+
return targetProvider.writeFile(target, await sourceProvider.readFile(source), {
|
|
1238
|
+
create: true,
|
|
1239
|
+
overwrite: true,
|
|
1240
|
+
unlock: false,
|
|
1241
|
+
atomic: false
|
|
1242
|
+
});
|
|
1073
1243
|
}
|
|
1074
1244
|
async doPipeUnbufferedToBuffered(sourceProvider, source, targetProvider, target) {
|
|
1075
|
-
return this.writeQueue.queueFor(
|
|
1245
|
+
return this.writeQueue.queueFor(
|
|
1246
|
+
target,
|
|
1247
|
+
() => this.doPipeUnbufferedToBufferedQueued(sourceProvider, source, targetProvider, target),
|
|
1248
|
+
this.getExtUri(targetProvider).providerExtUri
|
|
1249
|
+
);
|
|
1076
1250
|
}
|
|
1077
1251
|
async doPipeUnbufferedToBufferedQueued(sourceProvider, source, targetProvider, target) {
|
|
1078
|
-
const targetHandle = await targetProvider.open(target, {
|
|
1252
|
+
const targetHandle = await targetProvider.open(target, {
|
|
1253
|
+
create: true,
|
|
1254
|
+
unlock: false
|
|
1255
|
+
});
|
|
1079
1256
|
try {
|
|
1080
1257
|
const buffer = await sourceProvider.readFile(source);
|
|
1081
|
-
await this.doWriteBuffer(
|
|
1082
|
-
|
|
1083
|
-
|
|
1258
|
+
await this.doWriteBuffer(
|
|
1259
|
+
targetProvider,
|
|
1260
|
+
targetHandle,
|
|
1261
|
+
VSBuffer.wrap(buffer),
|
|
1262
|
+
buffer.byteLength,
|
|
1263
|
+
0,
|
|
1264
|
+
0
|
|
1265
|
+
);
|
|
1266
|
+
} catch (error) {
|
|
1084
1267
|
throw ensureFileSystemProviderError(error);
|
|
1085
|
-
}
|
|
1086
|
-
finally {
|
|
1268
|
+
} finally {
|
|
1087
1269
|
await targetProvider.close(targetHandle);
|
|
1088
1270
|
}
|
|
1089
1271
|
}
|
|
@@ -1094,7 +1276,7 @@ let FileService = class FileService extends Disposable {
|
|
|
1094
1276
|
throwIfFileSystemIsReadonly(provider, resource) {
|
|
1095
1277
|
if (provider.capabilities & FileSystemProviderCapabilities.Readonly) {
|
|
1096
1278
|
throw ( new FileOperationError(( localize(
|
|
1097
|
-
|
|
1279
|
+
1939,
|
|
1098
1280
|
"Unable to modify read-only file '{0}'",
|
|
1099
1281
|
this.resourceForError(resource)
|
|
1100
1282
|
)), FileOperationResult.FILE_PERMISSION_DENIED));
|
|
@@ -1104,7 +1286,7 @@ let FileService = class FileService extends Disposable {
|
|
|
1104
1286
|
throwIfFileIsReadonly(resource, stat) {
|
|
1105
1287
|
if ((stat.permissions ?? 0) & FilePermission.Readonly) {
|
|
1106
1288
|
throw ( new FileOperationError(( localize(
|
|
1107
|
-
|
|
1289
|
+
1939,
|
|
1108
1290
|
"Unable to modify read-only file '{0}'",
|
|
1109
1291
|
this.resourceForError(resource)
|
|
1110
1292
|
)), FileOperationResult.FILE_PERMISSION_DENIED));
|
|
@@ -1114,8 +1296,6 @@ let FileService = class FileService extends Disposable {
|
|
|
1114
1296
|
return resourceForError(resource);
|
|
1115
1297
|
}
|
|
1116
1298
|
};
|
|
1117
|
-
FileService = FileService_1 = ( __decorate([
|
|
1118
|
-
( __param(0, ILogService))
|
|
1119
|
-
], FileService));
|
|
1299
|
+
FileService = FileService_1 = ( __decorate([( __param(0, ILogService))], FileService));
|
|
1120
1300
|
|
|
1121
1301
|
export { FileService, mkdirp };
|