@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.
Files changed (113) hide show
  1. package/lib/browser/breadcrumbs/filepath-breadcrumbs-container.js +8 -19
  2. package/lib/browser/breadcrumbs/filepath-breadcrumbs-container.js.map +1 -1
  3. package/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution.js +8 -16
  4. package/lib/browser/breadcrumbs/filepath-breadcrumbs-contribution.js.map +1 -1
  5. package/lib/browser/download/file-download-command-contribution.js +6 -14
  6. package/lib/browser/download/file-download-command-contribution.js.map +1 -1
  7. package/lib/browser/download/file-download-service.js +6 -14
  8. package/lib/browser/download/file-download-service.js.map +1 -1
  9. package/lib/browser/file-dialog/file-dialog-hidden-files-renderer.js +7 -15
  10. package/lib/browser/file-dialog/file-dialog-hidden-files-renderer.js.map +1 -1
  11. package/lib/browser/file-dialog/file-dialog-model.js +8 -16
  12. package/lib/browser/file-dialog/file-dialog-model.js.map +1 -1
  13. package/lib/browser/file-dialog/file-dialog-service.js +14 -22
  14. package/lib/browser/file-dialog/file-dialog-service.js.map +1 -1
  15. package/lib/browser/file-dialog/file-dialog-tree-filters-renderer.js +4 -15
  16. package/lib/browser/file-dialog/file-dialog-tree-filters-renderer.js.map +1 -1
  17. package/lib/browser/file-dialog/file-dialog-tree.js +2 -7
  18. package/lib/browser/file-dialog/file-dialog-tree.js.map +1 -1
  19. package/lib/browser/file-dialog/file-dialog-widget.js +6 -17
  20. package/lib/browser/file-dialog/file-dialog-widget.js.map +1 -1
  21. package/lib/browser/file-dialog/file-dialog.js +34 -45
  22. package/lib/browser/file-dialog/file-dialog.js.map +1 -1
  23. package/lib/browser/file-dialog/index.js +5 -14
  24. package/lib/browser/file-dialog/index.js.map +1 -1
  25. package/lib/browser/file-resource.d.ts +6 -2
  26. package/lib/browser/file-resource.d.ts.map +1 -1
  27. package/lib/browser/file-resource.js +39 -22
  28. package/lib/browser/file-resource.js.map +1 -1
  29. package/lib/browser/file-service.d.ts +12 -0
  30. package/lib/browser/file-service.d.ts.map +1 -1
  31. package/lib/browser/file-service.js +32 -28
  32. package/lib/browser/file-service.js.map +1 -1
  33. package/lib/browser/file-tree/file-tree-decorator-adapter.js +10 -18
  34. package/lib/browser/file-tree/file-tree-decorator-adapter.js.map +1 -1
  35. package/lib/browser/file-tree/file-tree-label-provider.js +6 -14
  36. package/lib/browser/file-tree/file-tree-label-provider.js.map +1 -1
  37. package/lib/browser/file-tree/file-tree-model.js +14 -22
  38. package/lib/browser/file-tree/file-tree-model.js.map +1 -1
  39. package/lib/browser/file-tree/file-tree-widget.js +10 -21
  40. package/lib/browser/file-tree/file-tree-widget.js.map +1 -1
  41. package/lib/browser/file-tree/file-tree.js +6 -14
  42. package/lib/browser/file-tree/file-tree.js.map +1 -1
  43. package/lib/browser/file-tree/index.js +7 -16
  44. package/lib/browser/file-tree/index.js.map +1 -1
  45. package/lib/browser/file-upload-service.d.ts +2 -4
  46. package/lib/browser/file-upload-service.d.ts.map +1 -1
  47. package/lib/browser/file-upload-service.js +14 -22
  48. package/lib/browser/file-upload-service.js.map +1 -1
  49. package/lib/browser/filesystem-frontend-contribution.js +18 -26
  50. package/lib/browser/filesystem-frontend-contribution.js.map +1 -1
  51. package/lib/browser/filesystem-save-resource-service.d.ts +1 -1
  52. package/lib/browser/filesystem-save-resource-service.d.ts.map +1 -1
  53. package/lib/browser/filesystem-save-resource-service.js +10 -17
  54. package/lib/browser/filesystem-save-resource-service.js.map +1 -1
  55. package/lib/browser/filesystem-watcher-error-handler.js +6 -14
  56. package/lib/browser/filesystem-watcher-error-handler.js.map +1 -1
  57. package/lib/browser/index.js +6 -15
  58. package/lib/browser/index.js.map +1 -1
  59. package/lib/browser/location/index.js +3 -12
  60. package/lib/browser/location/index.js.map +1 -1
  61. package/lib/browser/location/location-renderer.js +12 -23
  62. package/lib/browser/location/location-renderer.js.map +1 -1
  63. package/lib/browser/remote-file-service-contribution.js +4 -12
  64. package/lib/browser/remote-file-service-contribution.js.map +1 -1
  65. package/lib/browser-only/browser-only-filesystem-provider-server.js +2 -7
  66. package/lib/browser-only/browser-only-filesystem-provider-server.js.map +1 -1
  67. package/lib/browser-only/browserfs-filesystem-initialization.js +2 -7
  68. package/lib/browser-only/browserfs-filesystem-initialization.js.map +1 -1
  69. package/lib/browser-only/browserfs-filesystem-provider.js +4 -15
  70. package/lib/browser-only/browserfs-filesystem-provider.js.map +1 -1
  71. package/lib/common/files.d.ts +8 -0
  72. package/lib/common/files.d.ts.map +1 -1
  73. package/lib/common/files.js +9 -1
  74. package/lib/common/files.js.map +1 -1
  75. package/lib/common/index.js +3 -12
  76. package/lib/common/index.js.map +1 -1
  77. package/lib/common/remote-file-system-provider.d.ts +11 -2
  78. package/lib/common/remote-file-system-provider.d.ts.map +1 -1
  79. package/lib/common/remote-file-system-provider.js +46 -24
  80. package/lib/common/remote-file-system-provider.js.map +1 -1
  81. package/lib/electron-browser/file-dialog/electron-file-dialog-service.js +4 -12
  82. package/lib/electron-browser/file-dialog/electron-file-dialog-service.js.map +1 -1
  83. package/lib/electron-main/electron-api-main.js +2 -7
  84. package/lib/electron-main/electron-api-main.js.map +1 -1
  85. package/lib/node/disk-file-system-provider.js +12 -20
  86. package/lib/node/disk-file-system-provider.js.map +1 -1
  87. package/lib/node/disk-file-system-provider.spec.js +3 -3
  88. package/lib/node/disk-file-system-provider.spec.js.map +1 -1
  89. package/lib/node/download/directory-archiver.js +2 -7
  90. package/lib/node/download/directory-archiver.js.map +1 -1
  91. package/lib/node/download/file-download-cache.js +4 -12
  92. package/lib/node/download/file-download-cache.js.map +1 -1
  93. package/lib/node/download/file-download-endpoint.js +8 -16
  94. package/lib/node/download/file-download-endpoint.js.map +1 -1
  95. package/lib/node/download/file-download-handler.d.ts.map +1 -1
  96. package/lib/node/download/file-download-handler.js +16 -24
  97. package/lib/node/download/file-download-handler.js.map +1 -1
  98. package/lib/node/filesystem-watcher-client.js +6 -14
  99. package/lib/node/filesystem-watcher-client.js.map +1 -1
  100. package/lib/node/filesystem-watcher-dispatcher.js +2 -7
  101. package/lib/node/filesystem-watcher-dispatcher.js.map +1 -1
  102. package/lib/node/node-file-upload-service.js +2 -7
  103. package/lib/node/node-file-upload-service.js.map +1 -1
  104. package/package.json +5 -6
  105. package/src/browser/file-resource.ts +36 -7
  106. package/src/browser/file-service.ts +25 -1
  107. package/src/browser/file-upload-service.ts +4 -6
  108. package/src/browser/filesystem-save-resource-service.ts +3 -2
  109. package/src/common/files.ts +13 -0
  110. package/src/common/remote-file-system-provider.ts +40 -2
  111. package/src/node/disk-file-system-provider.spec.ts +3 -3
  112. package/src/node/disk-file-system-provider.ts +2 -2
  113. 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
- isReadonly: boolean
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 isReadonly(): boolean {
65
- return this.options.isReadonly || this.fileService.hasCapability(this.uri, FileSystemProviderCapabilities.Readonly);
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.updateSavingContentChanges();
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.isReadonly) {
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
- isReadonly: stat?.isReadonly ?? false,
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 interface CustomDataTransfer {
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.values()) {
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()], item.id), context);
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<void> {
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
- await this.save(sourceWidget, options);
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
  }
@@ -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 { v4 } from 'uuid';
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, `${v4()}.txt`);
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, `${v4()}.txt`);
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 { v4 } from 'uuid';
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(), v4());
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 { v4 } from 'uuid';
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(), v4()), entries?: string[]): Promise<string> {
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 = v4()): Promise<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 = v4();
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 = v4();
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 = [];