@theia/filesystem 1.47.0-next.0 → 1.47.1
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/lib/browser/breadcrumbs/filepath-breadcrumbs-container.js +8 -19
- package/lib/browser/breadcrumbs/filepath-breadcrumbs-container.js.map +1 -1
- package/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution.js +8 -16
- package/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution.js.map +1 -1
- package/lib/browser/download/file-download-command-contribution.js +6 -14
- package/lib/browser/download/file-download-command-contribution.js.map +1 -1
- package/lib/browser/download/file-download-service.js +6 -14
- package/lib/browser/download/file-download-service.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-hidden-files-renderer.js +7 -15
- package/lib/browser/file-dialog/file-dialog-hidden-files-renderer.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-model.js +8 -16
- package/lib/browser/file-dialog/file-dialog-model.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-service.js +14 -22
- package/lib/browser/file-dialog/file-dialog-service.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-tree-filters-renderer.js +4 -15
- package/lib/browser/file-dialog/file-dialog-tree-filters-renderer.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-tree.js +2 -7
- package/lib/browser/file-dialog/file-dialog-tree.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog-widget.js +6 -17
- package/lib/browser/file-dialog/file-dialog-widget.js.map +1 -1
- package/lib/browser/file-dialog/file-dialog.js +34 -45
- package/lib/browser/file-dialog/file-dialog.js.map +1 -1
- package/lib/browser/file-dialog/index.js +5 -14
- package/lib/browser/file-dialog/index.js.map +1 -1
- package/lib/browser/file-resource.d.ts +6 -2
- package/lib/browser/file-resource.d.ts.map +1 -1
- package/lib/browser/file-resource.js +39 -22
- package/lib/browser/file-resource.js.map +1 -1
- package/lib/browser/file-service.d.ts +12 -0
- package/lib/browser/file-service.d.ts.map +1 -1
- package/lib/browser/file-service.js +32 -28
- package/lib/browser/file-service.js.map +1 -1
- package/lib/browser/file-tree/file-tree-decorator-adapter.js +10 -18
- package/lib/browser/file-tree/file-tree-decorator-adapter.js.map +1 -1
- package/lib/browser/file-tree/file-tree-label-provider.js +6 -14
- package/lib/browser/file-tree/file-tree-label-provider.js.map +1 -1
- package/lib/browser/file-tree/file-tree-model.js +14 -22
- package/lib/browser/file-tree/file-tree-model.js.map +1 -1
- package/lib/browser/file-tree/file-tree-widget.js +10 -21
- package/lib/browser/file-tree/file-tree-widget.js.map +1 -1
- package/lib/browser/file-tree/file-tree.js +6 -14
- package/lib/browser/file-tree/file-tree.js.map +1 -1
- package/lib/browser/file-tree/index.js +7 -16
- package/lib/browser/file-tree/index.js.map +1 -1
- package/lib/browser/file-upload-service.d.ts +2 -4
- package/lib/browser/file-upload-service.d.ts.map +1 -1
- package/lib/browser/file-upload-service.js +14 -22
- package/lib/browser/file-upload-service.js.map +1 -1
- package/lib/browser/filesystem-frontend-contribution.js +18 -26
- package/lib/browser/filesystem-frontend-contribution.js.map +1 -1
- package/lib/browser/filesystem-save-resource-service.d.ts +1 -1
- package/lib/browser/filesystem-save-resource-service.d.ts.map +1 -1
- package/lib/browser/filesystem-save-resource-service.js +10 -17
- package/lib/browser/filesystem-save-resource-service.js.map +1 -1
- package/lib/browser/filesystem-watcher-error-handler.js +6 -14
- package/lib/browser/filesystem-watcher-error-handler.js.map +1 -1
- package/lib/browser/index.js +6 -15
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/location/index.js +3 -12
- package/lib/browser/location/index.js.map +1 -1
- package/lib/browser/location/location-renderer.js +12 -23
- package/lib/browser/location/location-renderer.js.map +1 -1
- package/lib/browser/remote-file-service-contribution.js +4 -12
- package/lib/browser/remote-file-service-contribution.js.map +1 -1
- package/lib/browser-only/browser-only-filesystem-provider-server.js +2 -7
- package/lib/browser-only/browser-only-filesystem-provider-server.js.map +1 -1
- package/lib/browser-only/browserfs-filesystem-initialization.js +2 -7
- package/lib/browser-only/browserfs-filesystem-initialization.js.map +1 -1
- package/lib/browser-only/browserfs-filesystem-provider.js +4 -15
- package/lib/browser-only/browserfs-filesystem-provider.js.map +1 -1
- package/lib/common/files.d.ts +8 -0
- package/lib/common/files.d.ts.map +1 -1
- package/lib/common/files.js +9 -1
- package/lib/common/files.js.map +1 -1
- package/lib/common/index.js +3 -12
- package/lib/common/index.js.map +1 -1
- package/lib/common/remote-file-system-provider.d.ts +11 -2
- package/lib/common/remote-file-system-provider.d.ts.map +1 -1
- package/lib/common/remote-file-system-provider.js +46 -24
- package/lib/common/remote-file-system-provider.js.map +1 -1
- package/lib/electron-browser/file-dialog/electron-file-dialog-service.js +4 -12
- package/lib/electron-browser/file-dialog/electron-file-dialog-service.js.map +1 -1
- package/lib/electron-main/electron-api-main.js +2 -7
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/lib/node/disk-file-system-provider.js +12 -20
- package/lib/node/disk-file-system-provider.js.map +1 -1
- package/lib/node/disk-file-system-provider.spec.js +3 -3
- package/lib/node/disk-file-system-provider.spec.js.map +1 -1
- package/lib/node/download/directory-archiver.js +2 -7
- package/lib/node/download/directory-archiver.js.map +1 -1
- package/lib/node/download/file-download-cache.js +4 -12
- package/lib/node/download/file-download-cache.js.map +1 -1
- package/lib/node/download/file-download-endpoint.js +8 -16
- package/lib/node/download/file-download-endpoint.js.map +1 -1
- package/lib/node/download/file-download-handler.d.ts.map +1 -1
- package/lib/node/download/file-download-handler.js +16 -24
- package/lib/node/download/file-download-handler.js.map +1 -1
- package/lib/node/filesystem-watcher-client.js +6 -14
- package/lib/node/filesystem-watcher-client.js.map +1 -1
- package/lib/node/filesystem-watcher-dispatcher.js +2 -7
- package/lib/node/filesystem-watcher-dispatcher.js.map +1 -1
- package/lib/node/node-file-upload-service.js +2 -7
- package/lib/node/node-file-upload-service.js.map +1 -1
- package/package.json +5 -6
- package/src/browser/file-resource.ts +36 -7
- package/src/browser/file-service.ts +25 -1
- package/src/browser/file-upload-service.ts +4 -6
- package/src/browser/filesystem-save-resource-service.ts +3 -2
- package/src/common/files.ts +13 -0
- package/src/common/remote-file-system-provider.ts +40 -2
- package/src/node/disk-file-system-provider.spec.ts +3 -3
- package/src/node/disk-file-system-provider.ts +2 -2
- package/src/node/download/file-download-handler.ts +5 -5
|
@@ -27,6 +27,7 @@ import { LabelProvider } from '@theia/core/lib/browser/label-provider';
|
|
|
27
27
|
import { GENERAL_MAX_FILE_SIZE_MB } from './filesystem-preferences';
|
|
28
28
|
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
|
29
29
|
import { nls } from '@theia/core';
|
|
30
|
+
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
30
31
|
|
|
31
32
|
export interface FileResourceVersion extends ResourceVersion {
|
|
32
33
|
readonly encoding: string;
|
|
@@ -40,7 +41,7 @@ export namespace FileResourceVersion {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export interface FileResourceOptions {
|
|
43
|
-
|
|
44
|
+
readOnly: boolean | MarkdownString
|
|
44
45
|
shouldOverwrite: () => Promise<boolean>
|
|
45
46
|
shouldOpenAsText: (error: string) => Promise<boolean>
|
|
46
47
|
}
|
|
@@ -54,6 +55,9 @@ export class FileResource implements Resource {
|
|
|
54
55
|
protected readonly onDidChangeContentsEmitter = new Emitter<void>();
|
|
55
56
|
readonly onDidChangeContents: Event<void> = this.onDidChangeContentsEmitter.event;
|
|
56
57
|
|
|
58
|
+
protected readonly onDidChangeReadOnlyEmitter = new Emitter<boolean | MarkdownString>();
|
|
59
|
+
readonly onDidChangeReadOnly: Event<boolean | MarkdownString> = this.onDidChangeReadOnlyEmitter.event;
|
|
60
|
+
|
|
57
61
|
protected _version: FileResourceVersion | undefined;
|
|
58
62
|
get version(): FileResourceVersion | undefined {
|
|
59
63
|
return this._version;
|
|
@@ -61,8 +65,8 @@ export class FileResource implements Resource {
|
|
|
61
65
|
get encoding(): string | undefined {
|
|
62
66
|
return this._version?.encoding;
|
|
63
67
|
}
|
|
64
|
-
get
|
|
65
|
-
return this.options.
|
|
68
|
+
get readOnly(): boolean | MarkdownString {
|
|
69
|
+
return this.options.readOnly;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
constructor(
|
|
@@ -71,6 +75,7 @@ export class FileResource implements Resource {
|
|
|
71
75
|
protected readonly options: FileResourceOptions
|
|
72
76
|
) {
|
|
73
77
|
this.toDispose.push(this.onDidChangeContentsEmitter);
|
|
78
|
+
this.toDispose.push(this.onDidChangeReadOnlyEmitter);
|
|
74
79
|
this.toDispose.push(this.fileService.onDidFilesChange(event => {
|
|
75
80
|
if (event.contains(this.uri)) {
|
|
76
81
|
this.sync();
|
|
@@ -87,11 +92,30 @@ export class FileResource implements Resource {
|
|
|
87
92
|
console.error(e);
|
|
88
93
|
}
|
|
89
94
|
this.updateSavingContentChanges();
|
|
90
|
-
this.toDispose.push(this.fileService.onDidChangeFileSystemProviderCapabilities(e => {
|
|
95
|
+
this.toDispose.push(this.fileService.onDidChangeFileSystemProviderCapabilities(async e => {
|
|
91
96
|
if (e.scheme === this.uri.scheme) {
|
|
92
|
-
this.
|
|
97
|
+
this.updateReadOnly();
|
|
93
98
|
}
|
|
94
99
|
}));
|
|
100
|
+
this.fileService.onDidChangeFileSystemProviderReadOnlyMessage(async e => {
|
|
101
|
+
if (e.scheme === this.uri.scheme) {
|
|
102
|
+
this.updateReadOnly();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
protected async updateReadOnly(): Promise<void> {
|
|
108
|
+
const oldReadOnly = this.options.readOnly;
|
|
109
|
+
const readOnlyMessage = this.fileService.getReadOnlyMessage(this.uri);
|
|
110
|
+
if (readOnlyMessage) {
|
|
111
|
+
this.options.readOnly = readOnlyMessage;
|
|
112
|
+
} else {
|
|
113
|
+
this.options.readOnly = this.fileService.hasCapability(this.uri, FileSystemProviderCapabilities.Readonly);
|
|
114
|
+
}
|
|
115
|
+
if (this.options.readOnly !== oldReadOnly) {
|
|
116
|
+
this.updateSavingContentChanges();
|
|
117
|
+
this.onDidChangeReadOnlyEmitter.fire(this.options.readOnly);
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
dispose(): void {
|
|
@@ -220,7 +244,7 @@ export class FileResource implements Resource {
|
|
|
220
244
|
saveContents?: Resource['saveContents'];
|
|
221
245
|
saveContentChanges?: Resource['saveContentChanges'];
|
|
222
246
|
protected updateSavingContentChanges(): void {
|
|
223
|
-
if (this.
|
|
247
|
+
if (this.readOnly) {
|
|
224
248
|
delete this.saveContentChanges;
|
|
225
249
|
delete this.saveContents;
|
|
226
250
|
delete this.saveStream;
|
|
@@ -320,8 +344,13 @@ export class FileResourceResolver implements ResourceResolver {
|
|
|
320
344
|
if (stat && stat.isDirectory) {
|
|
321
345
|
throw new Error('The given uri is a directory: ' + this.labelProvider.getLongName(uri));
|
|
322
346
|
}
|
|
347
|
+
|
|
348
|
+
const readOnlyMessage = this.fileService.getReadOnlyMessage(uri);
|
|
349
|
+
const isFileSystemReadOnly = this.fileService.hasCapability(uri, FileSystemProviderCapabilities.Readonly);
|
|
350
|
+
const readOnly = readOnlyMessage ?? (isFileSystemReadOnly ? isFileSystemReadOnly : (stat?.isReadonly ?? false));
|
|
351
|
+
|
|
323
352
|
return new FileResource(uri, this.fileService, {
|
|
324
|
-
|
|
353
|
+
readOnly: readOnly,
|
|
325
354
|
shouldOverwrite: () => this.shouldOverwrite(uri),
|
|
326
355
|
shouldOpenAsText: error => this.shouldOpenAsText(uri, error)
|
|
327
356
|
});
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
toFileOperationResult, toFileSystemProviderErrorCode,
|
|
52
52
|
ResolveFileResult, ResolveFileResultWithMetadata,
|
|
53
53
|
MoveFileOptions, CopyFileOptions, BaseStatWithMetadata, FileDeleteOptions, FileOperationOptions, hasAccessCapability, hasUpdateCapability,
|
|
54
|
-
hasFileReadStreamCapability, FileSystemProviderWithFileReadStreamCapability
|
|
54
|
+
hasFileReadStreamCapability, FileSystemProviderWithFileReadStreamCapability, ReadOnlyMessageFileSystemProvider
|
|
55
55
|
} from '../common/files';
|
|
56
56
|
import { BinaryBuffer, BinaryBufferReadable, BinaryBufferReadableStream, BinaryBufferReadableBufferedStream, BinaryBufferWriteableStream } from '@theia/core/lib/common/buffer';
|
|
57
57
|
import { ReadableStream, isReadableStream, isReadableBufferedStream, transform, consumeStream, peekStream, peekReadable, Readable } from '@theia/core/lib/common/stream';
|
|
@@ -68,6 +68,7 @@ import { readFileIntoStream } from '../common/io';
|
|
|
68
68
|
import { FileSystemWatcherErrorHandler } from './filesystem-watcher-error-handler';
|
|
69
69
|
import { FileSystemUtils } from '../common/filesystem-utils';
|
|
70
70
|
import { nls } from '@theia/core';
|
|
71
|
+
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
71
72
|
|
|
72
73
|
export interface FileOperationParticipant {
|
|
73
74
|
|
|
@@ -235,6 +236,15 @@ export interface FileSystemProviderCapabilitiesChangeEvent {
|
|
|
235
236
|
scheme: string;
|
|
236
237
|
}
|
|
237
238
|
|
|
239
|
+
export interface FileSystemProviderReadOnlyMessageChangeEvent {
|
|
240
|
+
/** The affected file system provider for which this event was fired. */
|
|
241
|
+
provider: FileSystemProvider;
|
|
242
|
+
/** The uri for which the provider is registered */
|
|
243
|
+
scheme: string;
|
|
244
|
+
/** The new read only message */
|
|
245
|
+
message: MarkdownString | undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
238
248
|
/**
|
|
239
249
|
* Represents the `FileSystemProviderActivation` event.
|
|
240
250
|
* This event is fired by the {@link FileService} if it wants to activate the
|
|
@@ -342,6 +352,9 @@ export class FileService {
|
|
|
342
352
|
private onDidChangeFileSystemProviderCapabilitiesEmitter = new Emitter<FileSystemProviderCapabilitiesChangeEvent>();
|
|
343
353
|
readonly onDidChangeFileSystemProviderCapabilities = this.onDidChangeFileSystemProviderCapabilitiesEmitter.event;
|
|
344
354
|
|
|
355
|
+
private onDidChangeFileSystemProviderReadOnlyMessageEmitter = new Emitter<FileSystemProviderReadOnlyMessageChangeEvent>();
|
|
356
|
+
readonly onDidChangeFileSystemProviderReadOnlyMessage = this.onDidChangeFileSystemProviderReadOnlyMessageEmitter.event;
|
|
357
|
+
|
|
345
358
|
private readonly providers = new Map<string, FileSystemProvider>();
|
|
346
359
|
private readonly activations = new Map<string, Promise<FileSystemProvider>>();
|
|
347
360
|
|
|
@@ -364,6 +377,9 @@ export class FileService {
|
|
|
364
377
|
providerDisposables.push(provider.onDidChangeFile(changes => this.onDidFilesChangeEmitter.fire(new FileChangesEvent(changes))));
|
|
365
378
|
providerDisposables.push(provider.onFileWatchError(() => this.handleFileWatchError()));
|
|
366
379
|
providerDisposables.push(provider.onDidChangeCapabilities(() => this.onDidChangeFileSystemProviderCapabilitiesEmitter.fire({ provider, scheme })));
|
|
380
|
+
if (ReadOnlyMessageFileSystemProvider.is(provider)) {
|
|
381
|
+
providerDisposables.push(provider.onDidChangeReadOnlyMessage(message => this.onDidChangeFileSystemProviderReadOnlyMessageEmitter.fire({ provider, scheme, message})));
|
|
382
|
+
}
|
|
367
383
|
|
|
368
384
|
return Disposable.create(() => {
|
|
369
385
|
this.onDidChangeFileSystemProviderRegistrationsEmitter.fire({ added: false, scheme, provider });
|
|
@@ -413,6 +429,14 @@ export class FileService {
|
|
|
413
429
|
return this.providers.has(resource.scheme);
|
|
414
430
|
}
|
|
415
431
|
|
|
432
|
+
getReadOnlyMessage(resource: URI): MarkdownString | undefined {
|
|
433
|
+
const provider = this.providers.get(resource.scheme);
|
|
434
|
+
if (ReadOnlyMessageFileSystemProvider.is(provider)) {
|
|
435
|
+
return provider.readOnlyMessage;
|
|
436
|
+
}
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
|
|
416
440
|
/**
|
|
417
441
|
* Tests if the service (i.e the {@link FileSystemProvider} registered for the given uri scheme) provides the given capability.
|
|
418
442
|
* @param resource `URI` of the resource to test.
|
|
@@ -33,13 +33,11 @@ import { nls } from '@theia/core/lib/common/nls';
|
|
|
33
33
|
|
|
34
34
|
export const HTTP_UPLOAD_URL: string = new Endpoint({ path: HTTP_FILE_UPLOAD_PATH }).getRestUrl().toString(true);
|
|
35
35
|
|
|
36
|
-
export
|
|
37
|
-
values(): Iterable<CustomDataTransferItem>
|
|
38
|
-
}
|
|
36
|
+
export type CustomDataTransfer = Iterable<readonly [string, CustomDataTransferItem]>;
|
|
39
37
|
|
|
40
38
|
export interface CustomDataTransferItem {
|
|
41
|
-
readonly id: string;
|
|
42
39
|
asFile(): {
|
|
40
|
+
readonly id: string;
|
|
43
41
|
readonly name: string;
|
|
44
42
|
data(): Promise<Uint8Array>;
|
|
45
43
|
} | undefined
|
|
@@ -420,10 +418,10 @@ export class FileUploadService {
|
|
|
420
418
|
}
|
|
421
419
|
|
|
422
420
|
protected async indexCustomDataTransfer(targetUri: URI, dataTransfer: CustomDataTransfer, context: FileUploadService.Context): Promise<void> {
|
|
423
|
-
for (const item of dataTransfer
|
|
421
|
+
for (const [_, item] of dataTransfer) {
|
|
424
422
|
const fileInfo = item.asFile();
|
|
425
423
|
if (fileInfo) {
|
|
426
|
-
await this.indexFile(targetUri, new File([await fileInfo.data()],
|
|
424
|
+
await this.indexFile(targetUri, new File([await fileInfo.data()], fileInfo.id), context);
|
|
427
425
|
}
|
|
428
426
|
}
|
|
429
427
|
}
|
|
@@ -47,7 +47,7 @@ export class FilesystemSaveResourceService extends SaveResourceService {
|
|
|
47
47
|
/**
|
|
48
48
|
* Save `sourceWidget` to a new file picked by the user.
|
|
49
49
|
*/
|
|
50
|
-
override async saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise<
|
|
50
|
+
override async saveAs(sourceWidget: Widget & SaveableSource & Navigatable, options?: SaveOptions): Promise<URI | undefined> {
|
|
51
51
|
let exist: boolean = false;
|
|
52
52
|
let overwrite: boolean = false;
|
|
53
53
|
let selected: URI | undefined;
|
|
@@ -68,10 +68,11 @@ export class FilesystemSaveResourceService extends SaveResourceService {
|
|
|
68
68
|
}
|
|
69
69
|
} while ((selected && exist && !overwrite) || (selected?.isEqual(uri) && !canSave));
|
|
70
70
|
if (selected && selected.isEqual(uri)) {
|
|
71
|
-
|
|
71
|
+
return this.save(sourceWidget, options);
|
|
72
72
|
} else if (selected) {
|
|
73
73
|
try {
|
|
74
74
|
await this.copyAndSave(sourceWidget, selected, overwrite);
|
|
75
|
+
return selected;
|
|
75
76
|
} catch (e) {
|
|
76
77
|
console.warn(e);
|
|
77
78
|
}
|
package/src/common/files.ts
CHANGED
|
@@ -27,6 +27,7 @@ import type { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-l
|
|
|
27
27
|
import { ReadableStreamEvents } from '@theia/core/lib/common/stream';
|
|
28
28
|
import { CancellationToken } from '@theia/core/lib/common/cancellation';
|
|
29
29
|
import { isObject } from '@theia/core/lib/common';
|
|
30
|
+
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
30
31
|
|
|
31
32
|
export const enum FileOperation {
|
|
32
33
|
CREATE,
|
|
@@ -765,6 +766,18 @@ export function hasUpdateCapability(provider: FileSystemProvider): provider is F
|
|
|
765
766
|
return !!(provider.capabilities & FileSystemProviderCapabilities.Update);
|
|
766
767
|
}
|
|
767
768
|
|
|
769
|
+
export interface ReadOnlyMessageFileSystemProvider {
|
|
770
|
+
readOnlyMessage: MarkdownString | undefined;
|
|
771
|
+
readonly onDidChangeReadOnlyMessage: Event<MarkdownString | undefined>;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export namespace ReadOnlyMessageFileSystemProvider {
|
|
775
|
+
export function is(arg: unknown): arg is ReadOnlyMessageFileSystemProvider {
|
|
776
|
+
return isObject<ReadOnlyMessageFileSystemProvider>(arg)
|
|
777
|
+
&& 'readOnlyMessage' in arg;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
768
781
|
/**
|
|
769
782
|
* Subtype of {@link FileSystemProvider} that ensures that the optional functions, needed for providers
|
|
770
783
|
* that should be able to read & write files, are implemented.
|
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
FileWriteOptions, FileOpenOptions, FileChangeType,
|
|
24
24
|
FileSystemProviderCapabilities, FileChange, Stat, FileOverwriteOptions, WatchOptions, FileType, FileSystemProvider, FileDeleteOptions,
|
|
25
25
|
hasOpenReadWriteCloseCapability, hasFileFolderCopyCapability, hasReadWriteCapability, hasAccessCapability,
|
|
26
|
-
FileSystemProviderError, FileSystemProviderErrorCode, FileUpdateOptions, hasUpdateCapability, FileUpdateResult, FileReadStreamOptions, hasFileReadStreamCapability
|
|
26
|
+
FileSystemProviderError, FileSystemProviderErrorCode, FileUpdateOptions, hasUpdateCapability, FileUpdateResult, FileReadStreamOptions, hasFileReadStreamCapability,
|
|
27
|
+
ReadOnlyMessageFileSystemProvider
|
|
27
28
|
} from './files';
|
|
28
29
|
import { RpcServer, RpcProxy, RpcProxyFactory } from '@theia/core/lib/common/messaging/proxy-factory';
|
|
29
30
|
import { ApplicationError } from '@theia/core/lib/common/application-error';
|
|
@@ -31,6 +32,7 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
|
31
32
|
import type { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
32
33
|
import { newWriteableStream, ReadableStreamEvents } from '@theia/core/lib/common/stream';
|
|
33
34
|
import { CancellationToken, cancelled } from '@theia/core/lib/common/cancellation';
|
|
35
|
+
import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
|
|
34
36
|
|
|
35
37
|
export const remoteFileSystemPath = '/services/remote-filesystem';
|
|
36
38
|
|
|
@@ -38,6 +40,7 @@ export const RemoteFileSystemServer = Symbol('RemoteFileSystemServer');
|
|
|
38
40
|
export interface RemoteFileSystemServer extends RpcServer<RemoteFileSystemClient> {
|
|
39
41
|
getCapabilities(): Promise<FileSystemProviderCapabilities>
|
|
40
42
|
stat(resource: string): Promise<Stat>;
|
|
43
|
+
getReadOnlyMessage(): Promise<MarkdownString | undefined>;
|
|
41
44
|
access(resource: string, mode?: number): Promise<void>;
|
|
42
45
|
fsPath(resource: string): Promise<string>;
|
|
43
46
|
open(resource: string, opts: FileOpenOptions): Promise<number>;
|
|
@@ -70,6 +73,7 @@ export interface RemoteFileSystemClient {
|
|
|
70
73
|
notifyDidChangeFile(event: { changes: RemoteFileChange[] }): void;
|
|
71
74
|
notifyFileWatchError(): void;
|
|
72
75
|
notifyDidChangeCapabilities(capabilities: FileSystemProviderCapabilities): void;
|
|
76
|
+
notifyDidChangeReadOnlyMessage(readOnlyMessage: MarkdownString | undefined): void;
|
|
73
77
|
onFileStreamData(handle: number, data: Uint8Array): void;
|
|
74
78
|
onFileStreamEnd(handle: number, error: RemoteFileStreamError | undefined): void;
|
|
75
79
|
}
|
|
@@ -109,7 +113,7 @@ export class RemoteFileSystemProxyFactory<T extends object> extends RpcProxyFact
|
|
|
109
113
|
* Wraps the remote filesystem provider living on the backend.
|
|
110
114
|
*/
|
|
111
115
|
@injectable()
|
|
112
|
-
export class RemoteFileSystemProvider implements Required<FileSystemProvider>, Disposable {
|
|
116
|
+
export class RemoteFileSystemProvider implements Required<FileSystemProvider>, Disposable, ReadOnlyMessageFileSystemProvider {
|
|
113
117
|
|
|
114
118
|
private readonly onDidChangeFileEmitter = new Emitter<readonly FileChange[]>();
|
|
115
119
|
readonly onDidChangeFile = this.onDidChangeFileEmitter.event;
|
|
@@ -120,6 +124,9 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
120
124
|
private readonly onDidChangeCapabilitiesEmitter = new Emitter<void>();
|
|
121
125
|
readonly onDidChangeCapabilities = this.onDidChangeCapabilitiesEmitter.event;
|
|
122
126
|
|
|
127
|
+
private readonly onDidChangeReadOnlyMessageEmitter = new Emitter<MarkdownString | undefined>();
|
|
128
|
+
readonly onDidChangeReadOnlyMessage = this.onDidChangeReadOnlyMessageEmitter.event;
|
|
129
|
+
|
|
123
130
|
private readonly onFileStreamDataEmitter = new Emitter<[number, Uint8Array]>();
|
|
124
131
|
private readonly onFileStreamData = this.onFileStreamDataEmitter.event;
|
|
125
132
|
|
|
@@ -129,6 +136,7 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
129
136
|
protected readonly toDispose = new DisposableCollection(
|
|
130
137
|
this.onDidChangeFileEmitter,
|
|
131
138
|
this.onDidChangeCapabilitiesEmitter,
|
|
139
|
+
this.onDidChangeReadOnlyMessageEmitter,
|
|
132
140
|
this.onFileStreamDataEmitter,
|
|
133
141
|
this.onFileStreamEndEmitter
|
|
134
142
|
);
|
|
@@ -146,6 +154,11 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
146
154
|
private _capabilities: FileSystemProviderCapabilities = 0;
|
|
147
155
|
get capabilities(): FileSystemProviderCapabilities { return this._capabilities; }
|
|
148
156
|
|
|
157
|
+
private _readOnlyMessage: MarkdownString | undefined = undefined;
|
|
158
|
+
get readOnlyMessage(): MarkdownString | undefined {
|
|
159
|
+
return this._readOnlyMessage;
|
|
160
|
+
}
|
|
161
|
+
|
|
149
162
|
protected readonly readyDeferred = new Deferred<void>();
|
|
150
163
|
readonly ready = this.readyDeferred.promise;
|
|
151
164
|
|
|
@@ -161,6 +174,9 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
161
174
|
this._capabilities = capabilities;
|
|
162
175
|
this.readyDeferred.resolve();
|
|
163
176
|
}, this.readyDeferred.reject);
|
|
177
|
+
this.server.getReadOnlyMessage().then(readOnlyMessage => {
|
|
178
|
+
this._readOnlyMessage = readOnlyMessage;
|
|
179
|
+
});
|
|
164
180
|
this.server.setClient({
|
|
165
181
|
notifyDidChangeFile: ({ changes }) => {
|
|
166
182
|
this.onDidChangeFileEmitter.fire(changes.map(event => ({ resource: new URI(event.resource), type: event.type })));
|
|
@@ -169,6 +185,7 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
169
185
|
this.onFileWatchErrorEmitter.fire();
|
|
170
186
|
},
|
|
171
187
|
notifyDidChangeCapabilities: capabilities => this.setCapabilities(capabilities),
|
|
188
|
+
notifyDidChangeReadOnlyMessage: readOnlyMessage => this.setReadOnlyMessage(readOnlyMessage),
|
|
172
189
|
onFileStreamData: (handle, data) => this.onFileStreamDataEmitter.fire([handle, data]),
|
|
173
190
|
onFileStreamEnd: (handle, error) => this.onFileStreamEndEmitter.fire([handle, error])
|
|
174
191
|
});
|
|
@@ -188,6 +205,11 @@ export class RemoteFileSystemProvider implements Required<FileSystemProvider>, D
|
|
|
188
205
|
this.onDidChangeCapabilitiesEmitter.fire(undefined);
|
|
189
206
|
}
|
|
190
207
|
|
|
208
|
+
protected setReadOnlyMessage(readOnlyMessage: MarkdownString | undefined): void {
|
|
209
|
+
this._readOnlyMessage = readOnlyMessage;
|
|
210
|
+
this.onDidChangeReadOnlyMessageEmitter.fire(readOnlyMessage);
|
|
211
|
+
}
|
|
212
|
+
|
|
191
213
|
// --- forwarding calls
|
|
192
214
|
|
|
193
215
|
stat(resource: URI): Promise<Stat> {
|
|
@@ -362,6 +384,14 @@ export class FileSystemProviderServer implements RemoteFileSystemServer {
|
|
|
362
384
|
this.client.notifyDidChangeCapabilities(this.provider.capabilities);
|
|
363
385
|
}
|
|
364
386
|
}));
|
|
387
|
+
if (ReadOnlyMessageFileSystemProvider.is(this.provider)) {
|
|
388
|
+
const providerWithReadOnlyMessage: ReadOnlyMessageFileSystemProvider = this.provider;
|
|
389
|
+
this.toDispose.push(this.provider.onDidChangeReadOnlyMessage(() => {
|
|
390
|
+
if (this.client) {
|
|
391
|
+
this.client.notifyDidChangeReadOnlyMessage(providerWithReadOnlyMessage.readOnlyMessage);
|
|
392
|
+
}
|
|
393
|
+
}));
|
|
394
|
+
}
|
|
365
395
|
this.toDispose.push(this.provider.onDidChangeFile(changes => {
|
|
366
396
|
if (this.client) {
|
|
367
397
|
this.client.notifyDidChangeFile({
|
|
@@ -380,6 +410,14 @@ export class FileSystemProviderServer implements RemoteFileSystemServer {
|
|
|
380
410
|
return this.provider.capabilities;
|
|
381
411
|
}
|
|
382
412
|
|
|
413
|
+
async getReadOnlyMessage(): Promise<MarkdownString | undefined> {
|
|
414
|
+
if (ReadOnlyMessageFileSystemProvider.is(this.provider)) {
|
|
415
|
+
return this.provider.readOnlyMessage;
|
|
416
|
+
} else {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
383
421
|
stat(resource: string): Promise<Stat> {
|
|
384
422
|
return this.provider.stat(new URI(resource));
|
|
385
423
|
}
|
|
@@ -25,7 +25,7 @@ import { equal, fail } from 'assert';
|
|
|
25
25
|
import { promises as fs } from 'fs';
|
|
26
26
|
import { join } from 'path';
|
|
27
27
|
import * as temp from 'temp';
|
|
28
|
-
import {
|
|
28
|
+
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
29
29
|
import { FilePermission, FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode } from '../common/files';
|
|
30
30
|
import { DiskFileSystemProvider } from './disk-file-system-provider';
|
|
31
31
|
import { bindFileSystemWatcherServer } from './filesystem-backend-module';
|
|
@@ -53,7 +53,7 @@ describe('disk-file-system-provider', () => {
|
|
|
53
53
|
describe('stat', () => {
|
|
54
54
|
it("should omit the 'permissions' property of the stat if the file can be both read and write", async () => {
|
|
55
55
|
const tempDirPath = tracked.mkdirSync();
|
|
56
|
-
const tempFilePath = join(tempDirPath, `${
|
|
56
|
+
const tempFilePath = join(tempDirPath, `${generateUuid()}.txt`);
|
|
57
57
|
await fs.writeFile(tempFilePath, 'some content', { encoding: 'utf8' });
|
|
58
58
|
|
|
59
59
|
let content = await fs.readFile(tempFilePath, { encoding: 'utf8' });
|
|
@@ -70,7 +70,7 @@ describe('disk-file-system-provider', () => {
|
|
|
70
70
|
|
|
71
71
|
it("should set the 'permissions' property to `Readonly` if the file can be read but not write", async () => {
|
|
72
72
|
const tempDirPath = tracked.mkdirSync();
|
|
73
|
-
const tempFilePath = join(tempDirPath, `${
|
|
73
|
+
const tempFilePath = join(tempDirPath, `${generateUuid()}.txt`);
|
|
74
74
|
await fs.writeFile(tempFilePath, 'readonly content', {
|
|
75
75
|
encoding: 'utf8',
|
|
76
76
|
});
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
26
26
|
import { basename, dirname, normalize, join } from 'path';
|
|
27
|
-
import {
|
|
27
|
+
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
28
28
|
import * as os from 'os';
|
|
29
29
|
import * as fs from 'fs';
|
|
30
30
|
import {
|
|
@@ -530,7 +530,7 @@ export class DiskFileSystemProvider implements Disposable,
|
|
|
530
530
|
|
|
531
531
|
protected async rimrafMove(path: string): Promise<void> {
|
|
532
532
|
try {
|
|
533
|
-
const pathInTemp = join(os.tmpdir(),
|
|
533
|
+
const pathInTemp = join(os.tmpdir(), generateUuid());
|
|
534
534
|
try {
|
|
535
535
|
await promisify(rename)(path, pathInTemp);
|
|
536
536
|
} catch (error) {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import * as os from 'os';
|
|
18
18
|
import * as fs from '@theia/core/shared/fs-extra';
|
|
19
19
|
import * as path from 'path';
|
|
20
|
-
import {
|
|
20
|
+
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
21
21
|
import { Request, Response } from '@theia/core/shared/express';
|
|
22
22
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
23
|
import { OK, BAD_REQUEST, METHOD_NOT_ALLOWED, NOT_FOUND, INTERNAL_SERVER_ERROR, REQUESTED_RANGE_NOT_SATISFIABLE, PARTIAL_CONTENT } from 'http-status-codes';
|
|
@@ -135,12 +135,12 @@ export abstract class FileDownloadHandler {
|
|
|
135
135
|
end: (isNaN(end) || end > statSize - 1) ? (statSize - 1) : end
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
-
protected async archive(inputPath: string, outputPath: string = path.join(os.tmpdir(),
|
|
138
|
+
protected async archive(inputPath: string, outputPath: string = path.join(os.tmpdir(), generateUuid()), entries?: string[]): Promise<string> {
|
|
139
139
|
await this.directoryArchiver.archive(inputPath, outputPath, entries);
|
|
140
140
|
return outputPath;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
protected async createTempDir(downloadId: string =
|
|
143
|
+
protected async createTempDir(downloadId: string = generateUuid()): Promise<string> {
|
|
144
144
|
const outputPath = path.join(os.tmpdir(), downloadId);
|
|
145
145
|
await fs.mkdir(outputPath);
|
|
146
146
|
return outputPath;
|
|
@@ -221,7 +221,7 @@ export class SingleFileDownloadHandler extends FileDownloadHandler {
|
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
223
|
try {
|
|
224
|
-
const downloadId =
|
|
224
|
+
const downloadId = generateUuid();
|
|
225
225
|
const options: PrepareDownloadOptions = { filePath, downloadId, remove: false };
|
|
226
226
|
if (!stat.isDirectory()) {
|
|
227
227
|
await this.prepareDownload(request, response, options);
|
|
@@ -271,7 +271,7 @@ export class MultiFileDownloadHandler extends FileDownloadHandler {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
try {
|
|
274
|
-
const downloadId =
|
|
274
|
+
const downloadId = generateUuid();
|
|
275
275
|
const outputRootPath = await this.createTempDir(downloadId);
|
|
276
276
|
const distinctUris = Array.from(new Set(body.uris.map(uri => new URI(uri))));
|
|
277
277
|
const tarPaths = [];
|