@theia/plugin-ext 1.63.0-next.52 → 1.63.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.
Files changed (73) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +19 -1
  2. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc.js.map +1 -1
  4. package/lib/hosted/browser/worker/worker-plugin-module.d.ts.map +1 -1
  5. package/lib/hosted/browser/worker/worker-plugin-module.js +2 -0
  6. package/lib/hosted/browser/worker/worker-plugin-module.js.map +1 -1
  7. package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
  8. package/lib/hosted/node/plugin-host-module.js +2 -0
  9. package/lib/hosted/node/plugin-host-module.js.map +1 -1
  10. package/lib/main/browser/comments/comment-thread-widget.d.ts +1 -2
  11. package/lib/main/browser/comments/comment-thread-widget.d.ts.map +1 -1
  12. package/lib/main/browser/comments/comment-thread-widget.js +8 -6
  13. package/lib/main/browser/comments/comment-thread-widget.js.map +1 -1
  14. package/lib/main/browser/documents-main.d.ts +3 -3
  15. package/lib/main/browser/documents-main.d.ts.map +1 -1
  16. package/lib/main/browser/documents-main.js +41 -28
  17. package/lib/main/browser/documents-main.js.map +1 -1
  18. package/lib/main/browser/editors-and-documents-main.d.ts +1 -0
  19. package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
  20. package/lib/main/browser/editors-and-documents-main.js +4 -1
  21. package/lib/main/browser/editors-and-documents-main.js.map +1 -1
  22. package/lib/main/browser/main-context.d.ts.map +1 -1
  23. package/lib/main/browser/main-context.js +1 -3
  24. package/lib/main/browser/main-context.js.map +1 -1
  25. package/lib/main/browser/text-editor-model-service.d.ts +9 -2
  26. package/lib/main/browser/text-editor-model-service.d.ts.map +1 -1
  27. package/lib/main/browser/text-editor-model-service.js +10 -0
  28. package/lib/main/browser/text-editor-model-service.js.map +1 -1
  29. package/lib/main/browser/view/tree-view-widget.d.ts.map +1 -1
  30. package/lib/main/browser/view/tree-view-widget.js +4 -3
  31. package/lib/main/browser/view/tree-view-widget.js.map +1 -1
  32. package/lib/main/browser/workspace-main.d.ts +20 -0
  33. package/lib/main/browser/workspace-main.d.ts.map +1 -1
  34. package/lib/main/browser/workspace-main.js +54 -15
  35. package/lib/main/browser/workspace-main.js.map +1 -1
  36. package/lib/plugin/document-data.d.ts +3 -1
  37. package/lib/plugin/document-data.d.ts.map +1 -1
  38. package/lib/plugin/document-data.js +7 -1
  39. package/lib/plugin/document-data.js.map +1 -1
  40. package/lib/plugin/documents.d.ts +7 -1
  41. package/lib/plugin/documents.d.ts.map +1 -1
  42. package/lib/plugin/documents.js +21 -3
  43. package/lib/plugin/documents.js.map +1 -1
  44. package/lib/plugin/editors-and-documents.d.ts.map +1 -1
  45. package/lib/plugin/editors-and-documents.js +1 -1
  46. package/lib/plugin/editors-and-documents.js.map +1 -1
  47. package/lib/plugin/notebook/notebook-document.d.ts.map +1 -1
  48. package/lib/plugin/notebook/notebook-document.js +2 -1
  49. package/lib/plugin/notebook/notebook-document.js.map +1 -1
  50. package/lib/plugin/plugin-context.d.ts.map +1 -1
  51. package/lib/plugin/plugin-context.js +23 -10
  52. package/lib/plugin/plugin-context.js.map +1 -1
  53. package/lib/plugin/workspace.d.ts +11 -0
  54. package/lib/plugin/workspace.d.ts.map +1 -1
  55. package/lib/plugin/workspace.js +33 -0
  56. package/lib/plugin/workspace.js.map +1 -1
  57. package/package.json +30 -30
  58. package/src/common/plugin-api-rpc.ts +7 -2
  59. package/src/hosted/browser/worker/worker-plugin-module.ts +2 -0
  60. package/src/hosted/node/plugin-host-module.ts +2 -0
  61. package/src/main/browser/comments/comment-thread-widget.tsx +6 -7
  62. package/src/main/browser/documents-main.ts +49 -39
  63. package/src/main/browser/editors-and-documents-main.ts +5 -1
  64. package/src/main/browser/main-context.ts +1 -3
  65. package/src/main/browser/text-editor-model-service.ts +14 -2
  66. package/src/main/browser/view/tree-view-widget.tsx +4 -3
  67. package/src/main/browser/workspace-main.ts +66 -1
  68. package/src/plugin/document-data.ts +8 -1
  69. package/src/plugin/documents.ts +22 -5
  70. package/src/plugin/editors-and-documents.ts +2 -1
  71. package/src/plugin/notebook/notebook-document.ts +2 -1
  72. package/src/plugin/plugin-context.ts +27 -14
  73. package/src/plugin/workspace.ts +39 -1
@@ -13,7 +13,11 @@
13
13
  //
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
-
16
+ /*---------------------------------------------------------------------------------------------
17
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+ // some code was copied and modified from https://github.com/Microsoft/vscode/blob/main/src/vs/workbench/api/browser/mainThreadWorkspace.ts
17
21
  import * as theia from '@theia/plugin';
18
22
  import { interfaces, injectable } from '@theia/core/shared/inversify';
19
23
  import { WorkspaceExt, StorageExt, MAIN_RPC_CONTEXT, WorkspaceMain, WorkspaceFolderPickOptionsMain, FindFilesOptions } from '../../common/plugin-api-rpc';
@@ -32,6 +36,9 @@ import { SearchInWorkspaceService } from '@theia/search-in-workspace/lib/browser
32
36
  import { FileStat } from '@theia/filesystem/lib/common/files';
33
37
  import { MonacoQuickInputService } from '@theia/monaco/lib/browser/monaco-quick-input-service';
34
38
  import { RequestService } from '@theia/core/shared/@theia/request';
39
+ import { UTF16be, UTF16le, UTF8, UTF8_with_bom } from '@theia/core/lib/common/encodings';
40
+ import { EncodingRegistry } from '@theia/core/lib/browser/encoding-registry';
41
+ import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
35
42
 
36
43
  export class WorkspaceMainImpl implements WorkspaceMain, Disposable {
37
44
 
@@ -59,8 +66,12 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable {
59
66
 
60
67
  private workspaceTrustService: WorkspaceTrustService;
61
68
 
69
+ private encodingRegistry: EncodingRegistry;
70
+
62
71
  private fsPreferences: FileSystemPreferences;
63
72
 
73
+ private preferenceService: PreferenceService;
74
+
64
75
  protected readonly toDispose = new DisposableCollection();
65
76
 
66
77
  protected workspaceSearch: Set<number> = new Set<number>();
@@ -79,7 +90,9 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable {
79
90
  this.workspaceService = container.get(WorkspaceService);
80
91
  this.canonicalUriService = container.get(CanonicalUriService);
81
92
  this.workspaceTrustService = container.get(WorkspaceTrustService);
93
+ this.encodingRegistry = container.get(EncodingRegistry);
82
94
  this.fsPreferences = container.get(FileSystemPreferences);
95
+ this.preferenceService = container.get(PreferenceService);
83
96
 
84
97
  this.processWorkspaceFoldersChanged(this.workspaceService.tryGetRoots().map(root => root.resource.toString()));
85
98
  this.toDispose.push(this.workspaceService.onWorkspaceChanged(roots => {
@@ -318,6 +331,58 @@ export class WorkspaceMainImpl implements WorkspaceMain, Disposable {
318
331
  const canonicalUri = await this.canonicalUriService.provideCanonicalUri(new URI(uri), targetScheme, token);
319
332
  return isUndefined(canonicalUri) ? undefined : canonicalUri.toString();
320
333
  }
334
+
335
+ async $resolveDecoding(resource: UriComponents | undefined, options?: { encoding?: string }): Promise<{ preferredEncoding: string; guessEncoding: boolean }> {
336
+ const preferredEncoding = await this.getPreferredReadEncoding(resource, options);
337
+
338
+ return {
339
+ preferredEncoding,
340
+ guessEncoding: this.preferenceService.get('files.autoGuessEncoding', false, resource ? resource.toString() : undefined)
341
+ };
342
+ }
343
+
344
+ async $resolveEncoding(resource: UriComponents | undefined, options?: { encoding?: string }): Promise<{ encoding: string; hasBOM: boolean }> {
345
+ let encoding: string;
346
+ if (resource) {
347
+ encoding = await this.encodingRegistry.getEncodingForResource(URI.fromComponents(resource), options?.encoding);
348
+ } else {
349
+ encoding = options?.encoding ?? UTF8;
350
+ }
351
+ // see https://github.com/microsoft/vscode/blob/118f9ecd71a8f101b71ae19e3bf44802aa173209/src/vs/workbench/services/textfile/browser/textFileService.ts#L806
352
+ const hasBOM = encoding === UTF16be || encoding === UTF16le || encoding === UTF8_with_bom;
353
+
354
+ return { encoding, hasBOM };
355
+ }
356
+
357
+ async $getValidEncoding(uri: UriComponents | undefined, detectedEncoding: string | undefined, options: { encoding: string } | undefined): Promise<string> {
358
+ return this.getPreferredReadEncoding(uri, options, detectedEncoding);
359
+ }
360
+
361
+ async getPreferredReadEncoding(uri: UriComponents | undefined, options?: { encoding?: string }, detectedEncoding?: string): Promise<string> {
362
+ let preferredEncoding: string | undefined;
363
+
364
+ // either encoding is passed as an option
365
+ // or we have a detected encoding,
366
+ // or we are looking in the preferences
367
+ // or we default at UTF8
368
+ if (options?.encoding) {
369
+ if (detectedEncoding === UTF8_with_bom && options.encoding === UTF8) {
370
+ preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8
371
+ } else {
372
+ preferredEncoding = options.encoding; // give passed in encoding highest priority
373
+ }
374
+ } else if (typeof detectedEncoding === 'string') {
375
+ preferredEncoding = detectedEncoding;
376
+ }
377
+ let encoding;
378
+ if (uri) {
379
+ encoding = await this.encodingRegistry.getEncodingForResource(URI.fromComponents(uri), preferredEncoding);
380
+ } else {
381
+ encoding = preferredEncoding ?? UTF8;
382
+ }
383
+
384
+ return encoding;
385
+ }
321
386
  }
322
387
 
323
388
  /**
@@ -34,13 +34,15 @@ export class DocumentDataExt {
34
34
 
35
35
  private disposed = false;
36
36
  private dirty: boolean;
37
+ private encoding: string;
37
38
  private _document: theia.TextDocument;
38
39
  private textLines = new Array<theia.TextLine>();
39
40
  private lineStarts: PrefixSumComputer | undefined;
40
41
 
41
42
  constructor(private proxy: DocumentsMain, private uri: URI, private lines: string[], private eol: string,
42
- private languageId: string, private versionId: number, isDirty: boolean) {
43
+ private languageId: string, private versionId: number, isDirty: boolean, encoding: string) {
43
44
  this.dirty = isDirty;
45
+ this.encoding = encoding;
44
46
  }
45
47
 
46
48
  dispose(): void {
@@ -74,6 +76,10 @@ export class DocumentDataExt {
74
76
  ok(!this.disposed);
75
77
  this.languageId = langId;
76
78
  }
79
+ acceptEncoding(encoding: string): void {
80
+ ok(!this.disposed);
81
+ this.encoding = encoding;
82
+ }
77
83
  get document(): theia.TextDocument {
78
84
  if (!this._document) {
79
85
  const that = this;
@@ -85,6 +91,7 @@ export class DocumentDataExt {
85
91
  get version(): number { return that.versionId; },
86
92
  get isClosed(): boolean { return that.disposed; },
87
93
  get isDirty(): boolean { return that.dirty; },
94
+ get encoding(): string { return that.encoding; },
88
95
  save(): Promise<boolean> { return that.save(); },
89
96
  getText(range?): string { return range ? that.getTextInRange(range) : that.getText(); },
90
97
  get eol(): theia.EndOfLine { return that.eol === '\n' ? EndOfLine.LF : EndOfLine.CRLF; },
@@ -167,6 +167,20 @@ export class DocumentsExtImpl implements DocumentsExt {
167
167
  reason: undefined,
168
168
  });
169
169
  }
170
+ $acceptEncodingChanged(strUrl: UriComponents, encoding: string): void {
171
+ const uri = URI.revive(strUrl);
172
+ const uriString = uri.toString();
173
+ const data = this.editorsAndDocuments.getDocument(uriString);
174
+ if (!data) {
175
+ throw new Error('unknown document: ' + uriString);
176
+ }
177
+ data.acceptEncoding(encoding);
178
+ this._onDidChangeDocument.fire({
179
+ document: data.document,
180
+ contentChanges: [],
181
+ reason: undefined,
182
+ });
183
+ }
170
184
  $acceptModelChanged(strUrl: UriComponents, e: ModelChangedEvent, isDirty: boolean): void {
171
185
  const uri = URI.revive(strUrl);
172
186
  const uriString = uri.toString();
@@ -238,13 +252,16 @@ export class DocumentsExtImpl implements DocumentsExt {
238
252
  }
239
253
  }
240
254
 
241
- async openDocument(uri: URI): Promise<DocumentDataExt | undefined> {
255
+ async openDocument(uri: URI, options?: { language?: string; content?: string; encoding?: string }): Promise<DocumentDataExt | undefined> {
256
+ // If we have the document cached and no encoding options are provided,
257
+ // we should just return current document
242
258
  const cached = this.editorsAndDocuments.getDocument(uri.toString());
243
259
  if (cached) {
244
- return cached;
260
+ if (!options?.encoding || options.encoding === cached.document.encoding) {
261
+ return cached;
262
+ }
245
263
  }
246
-
247
- await this.proxy.$tryOpenDocument(uri);
264
+ await this.proxy.$tryOpenDocument(uri, options?.encoding);
248
265
  return this.editorsAndDocuments.getDocument(uri.toString());
249
266
  }
250
267
 
@@ -272,7 +289,7 @@ export class DocumentsExtImpl implements DocumentsExt {
272
289
  return this.editorsAndDocuments.getDocument(uri.toString());
273
290
  }
274
291
 
275
- async createDocumentData(options?: { language?: string; content?: string }): Promise<URI> {
292
+ async createDocumentData(options?: { language?: string; content?: string, encoding?: string }): Promise<URI> {
276
293
  return this.proxy.$tryCreateDocument(options).then(data => URI.revive(data));
277
294
  }
278
295
 
@@ -77,7 +77,8 @@ export class EditorsAndDocumentsExtImpl implements EditorsAndDocumentsExt {
77
77
  data.EOL,
78
78
  data.modeId,
79
79
  data.versionId,
80
- data.isDirty
80
+ data.isDirty,
81
+ data.encoding
81
82
  );
82
83
  this.documents.set(resource.toString(), documentData);
83
84
  addedDocuments.push(documentData);
@@ -57,7 +57,8 @@ export class Cell {
57
57
  uri: cell.uri,
58
58
  isDirty: false,
59
59
  versionId: 1,
60
- modeId: cell.language
60
+ modeId: cell.language,
61
+ encoding: 'utf8' // see https://github.com/microsoft/vscode/blob/118f9ecd71a8f101b71ae19e3bf44802aa173209/src/vs/workbench/api/common/extHostNotebookDocument.ts#L44
61
62
  };
62
63
  }
63
64
 
@@ -785,24 +785,37 @@ export function createAPIFactory(
785
785
  onDidChangeConfiguration(listener, thisArgs?, disposables?): theia.Disposable {
786
786
  return preferenceRegistryExt.onDidChangeConfiguration(listener, thisArgs, disposables);
787
787
  },
788
- async openTextDocument(uriOrFileNameOrOptions?: theia.Uri | string | { language?: string; content?: string; }): Promise<theia.TextDocument | undefined> {
789
- const options = uriOrFileNameOrOptions as { language?: string; content?: string; };
790
-
788
+ decode(content: Uint8Array, options?: { uri?: theia.Uri; encoding?: string }) {
789
+ return workspaceExt.decode(content, options);
790
+ },
791
+ encode(content: string, options?: { uri?: theia.Uri; encoding?: string }) {
792
+ return workspaceExt.encode(content, options);
793
+ },
794
+ async openTextDocument(
795
+ uriOrPathOrOptions?: theia.Uri | string | { language?: string; content?: string; encoding?: string },
796
+ options?: { readonly encoding?: string }
797
+ ): Promise<theia.TextDocument | undefined> {
791
798
  let uri: URI;
792
- if (typeof uriOrFileNameOrOptions === 'string') {
793
- uri = URI.file(uriOrFileNameOrOptions);
794
-
795
- } else if (uriOrFileNameOrOptions instanceof URI) {
796
- uri = uriOrFileNameOrOptions;
797
-
798
- } else if (!options || typeof options === 'object') {
799
- uri = await documents.createDocumentData(options);
800
-
799
+ let documentOptions: { language?: string; content?: string; encoding?: string } | undefined;
800
+
801
+ if (typeof uriOrPathOrOptions === 'string') {
802
+ // It's a file path
803
+ uri = URI.file(uriOrPathOrOptions);
804
+ documentOptions = options;
805
+ } else if (URI.isUri(uriOrPathOrOptions)) {
806
+ // It's a URI
807
+ uri = uriOrPathOrOptions;
808
+ documentOptions = options;
809
+ } else if (!uriOrPathOrOptions || typeof uriOrPathOrOptions === 'object') {
810
+ // It's options for creating a new document
811
+ documentOptions = uriOrPathOrOptions as { language?: string; content?: string; encoding?: string };
812
+ uri = await documents.createDocumentData(documentOptions);
801
813
  } else {
802
- return Promise.reject(new Error('illegal argument - uriOrFileNameOrOptions'));
814
+ return Promise.reject(new Error('illegal argument - uriOrPathOrOptions'));
803
815
  }
804
816
 
805
- const data = await documents.openDocument(uri);
817
+ // If we have options with encoding from any source, we need to pass them to openDocument
818
+ const data = await documents.openDocument(uri, documentOptions);
806
819
  return data && data.document;
807
820
  },
808
821
  async openNotebookDocument(uriOrType: theia.Uri | string, content?: NotebookData): Promise<theia.NotebookDocument | undefined> {
@@ -38,13 +38,16 @@ import { EditorsAndDocumentsExtImpl } from './editors-and-documents';
38
38
  import { Disposable, URI } from './types-impl';
39
39
  import { normalize } from '@theia/core/lib/common/paths';
40
40
  import { relative } from '../common/paths-util';
41
- import { Schemes } from '../common/uri-components';
41
+ import { Schemes, UriComponents } from '../common/uri-components';
42
42
  import { toWorkspaceFolder } from './type-converters';
43
43
  import { MessageRegistryExt } from './message-registry';
44
44
  import * as Converter from './type-converters';
45
45
  import { FileStat } from '@theia/filesystem/lib/common/files';
46
46
  import { isUndefinedOrNull, isUndefined } from '../common/types';
47
47
  import { PluginLogger } from './logger';
48
+ import { consumeStream } from '@theia/core/lib/common/stream';
49
+ import { EncodingService } from '@theia/core/lib/common/encoding-service';
50
+ import { BinaryBuffer, BinaryBufferReadableStream } from '@theia/core/lib/common/buffer';
48
51
 
49
52
  @injectable()
50
53
  export class WorkspaceExtImpl implements WorkspaceExt {
@@ -61,6 +64,9 @@ export class WorkspaceExtImpl implements WorkspaceExt {
61
64
  private proxy: WorkspaceMain;
62
65
  private logger: PluginLogger;
63
66
 
67
+ @inject(EncodingService)
68
+ protected encodingService: EncodingService;
69
+
64
70
  private workspaceFoldersChangedEmitter = new Emitter<theia.WorkspaceFoldersChangeEvent>();
65
71
  public readonly onDidChangeWorkspaceFolders: Event<theia.WorkspaceFoldersChangeEvent> = this.workspaceFoldersChangedEmitter.event;
66
72
 
@@ -508,4 +514,36 @@ export class WorkspaceExtImpl implements WorkspaceExt {
508
514
  $registerEditSessionIdentityProvider(scheme: string, provider: theia.EditSessionIdentityProvider): theia.Disposable {
509
515
  return Disposable.NULL;
510
516
  }
517
+
518
+ async decode(content: Uint8Array, args?: { uri?: theia.Uri; encoding?: string }): Promise<string> {
519
+ const [uri, opts] = this.asEncodeDecodeParameters(args);
520
+ const { preferredEncoding, guessEncoding } = await this.proxy.$resolveDecoding(uri, opts);
521
+ const decodeStreamOptions = {
522
+ guessEncoding,
523
+ overwriteEncoding: (detectedEncoding: string | undefined) => {
524
+ if (detectedEncoding === null || detectedEncoding === preferredEncoding) {
525
+ return Promise.resolve(preferredEncoding);
526
+ }
527
+
528
+ return this.proxy.$getValidEncoding(uri, detectedEncoding, opts);
529
+ }
530
+ };
531
+ const stream = (await this.encodingService.decodeStream(BinaryBufferReadableStream.fromBuffer(BinaryBuffer.wrap(content)), decodeStreamOptions)).stream;
532
+ return consumeStream(stream, s => s.join(''));
533
+
534
+ }
535
+
536
+ async encode(content: string, args?: { uri?: theia.Uri; encoding?: string }): Promise<Uint8Array> {
537
+ const [uri, options] = this.asEncodeDecodeParameters(args);
538
+ const { encoding, hasBOM } = await this.proxy.$resolveEncoding(uri, options);
539
+ const buffer = this.encodingService.encode(content, { encoding, hasBOM });
540
+ return buffer.buffer;
541
+ }
542
+
543
+ private asEncodeDecodeParameters(opts?: { uri?: theia.Uri; encoding?: string }): [UriComponents | undefined, { encoding: string } | undefined] {
544
+ const uri = Converter.isUriComponents(opts?.uri) ? opts.uri : undefined;
545
+ const encoding = typeof opts?.encoding === 'string' ? opts.encoding : undefined;
546
+
547
+ return [uri, encoding ? { encoding } : undefined];
548
+ }
511
549
  }