@theia/plugin-ext 1.34.0 → 1.35.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 (123) hide show
  1. package/lib/common/plugin-api-rpc-model.d.ts +18 -1
  2. package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc-model.js.map +1 -1
  4. package/lib/common/plugin-api-rpc.d.ts +11 -6
  5. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  6. package/lib/common/plugin-api-rpc.js.map +1 -1
  7. package/lib/common/plugin-protocol.d.ts +30 -0
  8. package/lib/common/plugin-protocol.d.ts.map +1 -1
  9. package/lib/common/plugin-protocol.js.map +1 -1
  10. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  11. package/lib/hosted/browser/hosted-plugin.js +10 -17
  12. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  13. package/lib/hosted/node/hosted-plugin-process.js.map +1 -1
  14. package/lib/hosted/node/plugin-activation-events.d.ts +7 -0
  15. package/lib/hosted/node/plugin-activation-events.d.ts.map +1 -0
  16. package/lib/hosted/node/plugin-activation-events.js +96 -0
  17. package/lib/hosted/node/plugin-activation-events.js.map +1 -0
  18. package/lib/hosted/node/plugin-manifest-loader.d.ts +2 -1
  19. package/lib/hosted/node/plugin-manifest-loader.d.ts.map +1 -1
  20. package/lib/hosted/node/plugin-manifest-loader.js +2 -1
  21. package/lib/hosted/node/plugin-manifest-loader.js.map +1 -1
  22. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  23. package/lib/hosted/node/scanners/scanner-theia.js +27 -36
  24. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  25. package/lib/main/browser/data-transfer/data-transfer-type-converters.d.ts +9 -0
  26. package/lib/main/browser/data-transfer/data-transfer-type-converters.d.ts.map +1 -0
  27. package/lib/main/browser/data-transfer/data-transfer-type-converters.js +65 -0
  28. package/lib/main/browser/data-transfer/data-transfer-type-converters.js.map +1 -0
  29. package/lib/main/browser/dialogs/modal-notification.d.ts.map +1 -1
  30. package/lib/main/browser/dialogs/modal-notification.js +4 -2
  31. package/lib/main/browser/dialogs/modal-notification.js.map +1 -1
  32. package/lib/main/browser/languages-main.d.ts +8 -1
  33. package/lib/main/browser/languages-main.d.ts.map +1 -1
  34. package/lib/main/browser/languages-main.js +26 -2
  35. package/lib/main/browser/languages-main.js.map +1 -1
  36. package/lib/main/browser/main-context.d.ts.map +1 -1
  37. package/lib/main/browser/main-context.js +3 -0
  38. package/lib/main/browser/main-context.js.map +1 -1
  39. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  40. package/lib/main/browser/plugin-ext-frontend-module.js +3 -0
  41. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  42. package/lib/main/browser/tabs/tabs-main.d.ts +33 -2
  43. package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
  44. package/lib/main/browser/tabs/tabs-main.js +256 -6
  45. package/lib/main/browser/tabs/tabs-main.js.map +1 -1
  46. package/lib/main/browser/text-editors-main.d.ts +2 -2
  47. package/lib/main/browser/text-editors-main.d.ts.map +1 -1
  48. package/lib/main/browser/text-editors-main.js +2 -2
  49. package/lib/main/browser/text-editors-main.js.map +1 -1
  50. package/lib/main/browser/webview/webview-context-keys.d.ts +13 -0
  51. package/lib/main/browser/webview/webview-context-keys.d.ts.map +1 -0
  52. package/lib/main/browser/webview/webview-context-keys.js +64 -0
  53. package/lib/main/browser/webview/webview-context-keys.js.map +1 -0
  54. package/lib/plugin/dialogs.js +2 -2
  55. package/lib/plugin/dialogs.js.map +1 -1
  56. package/lib/plugin/file-system-ext-impl.d.ts +11 -5
  57. package/lib/plugin/file-system-ext-impl.d.ts.map +1 -1
  58. package/lib/plugin/file-system-ext-impl.js +8 -16
  59. package/lib/plugin/file-system-ext-impl.js.map +1 -1
  60. package/lib/plugin/known-commands.d.ts.map +1 -1
  61. package/lib/plugin/known-commands.js +13 -0
  62. package/lib/plugin/known-commands.js.map +1 -1
  63. package/lib/plugin/languages/document-drop-edit.d.ts +16 -0
  64. package/lib/plugin/languages/document-drop-edit.d.ts.map +1 -0
  65. package/lib/plugin/languages/document-drop-edit.js +23 -0
  66. package/lib/plugin/languages/document-drop-edit.js.map +1 -0
  67. package/lib/plugin/languages.d.ts +9 -2
  68. package/lib/plugin/languages.d.ts.map +1 -1
  69. package/lib/plugin/languages.js +27 -1
  70. package/lib/plugin/languages.js.map +1 -1
  71. package/lib/plugin/plugin-context.d.ts.map +1 -1
  72. package/lib/plugin/plugin-context.js +8 -4
  73. package/lib/plugin/plugin-context.js.map +1 -1
  74. package/lib/plugin/preference-registry.d.ts +5 -3
  75. package/lib/plugin/preference-registry.d.ts.map +1 -1
  76. package/lib/plugin/preference-registry.js +31 -42
  77. package/lib/plugin/preference-registry.js.map +1 -1
  78. package/lib/plugin/preference-registry.spec.js +29 -41
  79. package/lib/plugin/preference-registry.spec.js.map +1 -1
  80. package/lib/plugin/tabs.d.ts.map +1 -1
  81. package/lib/plugin/tabs.js +10 -13
  82. package/lib/plugin/tabs.js.map +1 -1
  83. package/lib/plugin/text-editors.d.ts +1 -1
  84. package/lib/plugin/text-editors.d.ts.map +1 -1
  85. package/lib/plugin/text-editors.js +2 -2
  86. package/lib/plugin/text-editors.js.map +1 -1
  87. package/lib/plugin/type-converters.d.ts +6 -0
  88. package/lib/plugin/type-converters.d.ts.map +1 -1
  89. package/lib/plugin/type-converters.js +37 -1
  90. package/lib/plugin/type-converters.js.map +1 -1
  91. package/lib/plugin/types-impl.d.ts +5 -0
  92. package/lib/plugin/types-impl.d.ts.map +1 -1
  93. package/lib/plugin/types-impl.js +13 -3
  94. package/lib/plugin/types-impl.js.map +1 -1
  95. package/package.json +27 -26
  96. package/src/common/plugin-api-rpc-model.ts +22 -1
  97. package/src/common/plugin-api-rpc.ts +19 -6
  98. package/src/common/plugin-protocol.ts +32 -4
  99. package/src/hosted/browser/hosted-plugin.ts +9 -16
  100. package/src/hosted/node/hosted-plugin-process.ts +2 -2
  101. package/src/hosted/node/plugin-activation-events.ts +111 -0
  102. package/src/hosted/node/plugin-manifest-loader.ts +4 -3
  103. package/src/hosted/node/scanners/scanner-theia.ts +59 -75
  104. package/src/main/browser/data-transfer/data-transfer-type-converters.ts +70 -0
  105. package/src/main/browser/dialogs/modal-notification.ts +4 -2
  106. package/src/main/browser/languages-main.ts +34 -4
  107. package/src/main/browser/main-context.ts +4 -0
  108. package/src/main/browser/plugin-ext-frontend-module.ts +3 -0
  109. package/src/main/browser/tabs/tabs-main.ts +287 -6
  110. package/src/main/browser/text-editors-main.ts +3 -2
  111. package/src/main/browser/webview/webview-context-keys.ts +49 -0
  112. package/src/plugin/dialogs.ts +2 -2
  113. package/src/plugin/file-system-ext-impl.ts +8 -18
  114. package/src/plugin/known-commands.ts +16 -1
  115. package/src/plugin/languages/document-drop-edit.ts +44 -0
  116. package/src/plugin/languages.ts +41 -3
  117. package/src/plugin/plugin-context.ts +9 -4
  118. package/src/plugin/preference-registry.spec.ts +29 -45
  119. package/src/plugin/preference-registry.ts +33 -45
  120. package/src/plugin/tabs.ts +10 -14
  121. package/src/plugin/text-editors.ts +2 -2
  122. package/src/plugin/type-converters.ts +37 -0
  123. package/src/plugin/types-impl.ts +11 -0
@@ -87,6 +87,7 @@ import {
87
87
  DocumentHighlightKind,
88
88
  DocumentHighlight,
89
89
  DocumentLink,
90
+ DocumentDropEdit,
90
91
  CodeLens,
91
92
  CodeActionKind,
92
93
  CodeActionTrigger,
@@ -257,11 +258,11 @@ export function createAPIFactory(
257
258
  const statusBarMessageRegistryExt = new StatusBarMessageRegistryExt(rpc);
258
259
  const terminalExt = rpc.set(MAIN_RPC_CONTEXT.TERMINAL_EXT, new TerminalServiceExtImpl(rpc));
259
260
  const outputChannelRegistryExt = rpc.set(MAIN_RPC_CONTEXT.OUTPUT_CHANNEL_REGISTRY_EXT, new OutputChannelRegistryExtImpl(rpc));
260
- const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry));
261
261
  const treeViewsExt = rpc.set(MAIN_RPC_CONTEXT.TREE_VIEWS_EXT, new TreeViewsExtImpl(rpc, commandRegistry));
262
262
  const tasksExt = rpc.set(MAIN_RPC_CONTEXT.TASKS_EXT, new TasksExtImpl(rpc, terminalExt));
263
263
  const connectionExt = rpc.set(MAIN_RPC_CONTEXT.CONNECTION_EXT, new ConnectionImpl(rpc.getProxy(PLUGIN_RPC_CONTEXT.CONNECTION_MAIN)));
264
- const fileSystemExt = rpc.set(MAIN_RPC_CONTEXT.FILE_SYSTEM_EXT, new FileSystemExtImpl(rpc, languagesExt));
264
+ const fileSystemExt = rpc.set(MAIN_RPC_CONTEXT.FILE_SYSTEM_EXT, new FileSystemExtImpl(rpc));
265
+ const languagesExt = rpc.set(MAIN_RPC_CONTEXT.LANGUAGES_EXT, new LanguagesExtImpl(rpc, documents, commandRegistry, fileSystemExt));
265
266
  const extHostFileSystemEvent = rpc.set(MAIN_RPC_CONTEXT.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpc, editorsAndDocumentsExt));
266
267
  const scmExt = rpc.set(MAIN_RPC_CONTEXT.SCM_EXT, new ScmExtImpl(rpc, commandRegistry));
267
268
  const decorationsExt = rpc.set(MAIN_RPC_CONTEXT.DECORATIONS_EXT, new DecorationsExtImpl(rpc));
@@ -669,8 +670,8 @@ export function createAPIFactory(
669
670
  saveAll(includeUntitled?: boolean): PromiseLike<boolean> {
670
671
  return editors.saveAll(includeUntitled);
671
672
  },
672
- applyEdit(edit: theia.WorkspaceEdit): PromiseLike<boolean> {
673
- return editors.applyWorkspaceEdit(edit);
673
+ applyEdit(edit: theia.WorkspaceEdit, metadata?: theia.WorkspaceEditMetadata): PromiseLike<boolean> {
674
+ return editors.applyWorkspaceEdit(edit, metadata);
674
675
  },
675
676
  registerTextDocumentContentProvider(scheme: string, provider: theia.TextDocumentContentProvider): theia.Disposable {
676
677
  return workspaceExt.registerTextDocumentContentProvider(scheme, provider);
@@ -853,6 +854,9 @@ export function createAPIFactory(
853
854
  ): theia.Disposable {
854
855
  return languagesExt.registerOnTypeFormattingEditProvider(selector, provider, [firstTriggerCharacter].concat(moreTriggerCharacters), pluginToPluginInfo(plugin));
855
856
  },
857
+ registerDocumentDropEditProvider(selector: theia.DocumentSelector, provider: theia.DocumentDropEditProvider) {
858
+ return languagesExt.registerDocumentDropEditProvider(selector, provider);
859
+ },
856
860
  registerDocumentLinkProvider(selector: theia.DocumentSelector, provider: theia.DocumentLinkProvider): theia.Disposable {
857
861
  return languagesExt.registerDocumentLinkProvider(selector, provider, pluginToPluginInfo(plugin));
858
862
  },
@@ -1195,6 +1199,7 @@ export function createAPIFactory(
1195
1199
  DocumentHighlightKind,
1196
1200
  DocumentHighlight,
1197
1201
  DocumentLink,
1202
+ DocumentDropEdit,
1198
1203
  CodeLens,
1199
1204
  CodeActionKind,
1200
1205
  CodeActionTrigger,
@@ -41,36 +41,6 @@ describe('PreferenceRegistryExtImpl:', () => {
41
41
  preferenceRegistryExtImpl = new PreferenceRegistryExtImpl(mockRPC, mockWorkspace);
42
42
  });
43
43
 
44
- it('should parse configuration data without overrides', () => {
45
- const value: Record<string, any> = {
46
- 'my.key1.foo': 'value1',
47
- 'my.key1.bar': 'value2',
48
- };
49
- const result = preferenceRegistryExtImpl['parseConfigurationData'](value);
50
- expect(result.contents.my).to.be.an('object');
51
- expect(result.contents.my.key1).to.be.an('object');
52
-
53
- expect(result.contents.my.key1.foo).to.be.an('string');
54
- expect(result.contents.my.key1.foo).to.equal('value1');
55
-
56
- expect(result.contents.my.key1.bar).to.be.an('string');
57
- expect(result.contents.my.key1.bar).to.equal('value2');
58
- expect(result.keys).deep.equal(['my.key1.foo', 'my.key1.bar']);
59
- });
60
-
61
- it('should parse configuration with overrides', () => {
62
- const value: Record<string, any> = {
63
- 'editor.tabSize': 2,
64
- '[typescript].editor.tabSize': 4,
65
- };
66
- const result = preferenceRegistryExtImpl['parseConfigurationData'](value);
67
- expect(result.contents.editor.tabSize).to.equal(2);
68
- const tsOverride = result.overrides[0];
69
- expect(tsOverride.contents.editor.tabSize).to.equal(4);
70
- expect(tsOverride.identifiers).deep.equal(['typescript']);
71
- expect(tsOverride.keys).deep.equal(['editor.tabSize']);
72
- });
73
-
74
44
  describe('Prototype pollution', () => {
75
45
  it('Ignores key `__proto__`', () => {
76
46
  const value: Record<string, any> = {
@@ -80,16 +50,18 @@ describe('PreferenceRegistryExtImpl:', () => {
80
50
  '__proto__': {},
81
51
  '[typescript].someKey.foo': 'value',
82
52
  '[typescript].__proto__.injectedParsedPrototype': true,
53
+ 'b': { '__proto__.injectedParsedPrototype': true },
54
+ 'c': { '__proto__': { 'injectedParsedPrototype': true } }
83
55
  };
84
- const result = preferenceRegistryExtImpl['parseConfigurationData'](value);
85
- expect(result.contents.my).to.be.an('object');
86
- expect(result.contents.__proto__).to.be.an('undefined');
87
- expect(result.contents.my.key1.foo).to.equal('value1');
88
- expect(result.overrides[0].contents.__proto__).to.be.an('undefined');
56
+ const configuration = preferenceRegistryExtImpl['getConfigurationModel']('test', value);
57
+ const result = configuration['_contents'];
58
+ expect(result.my, 'Safe keys are preserved.').to.be.an('object');
59
+ expect(result.__proto__, 'Keys containing __proto__ are ignored').to.be.an('undefined');
60
+ expect(result.my.key1.foo, 'Safe keys are dendrified.').to.equal('value1');
89
61
  const prototypeObject = Object.prototype as any;
90
- expect(prototypeObject.injectedParsedPrototype).to.be.an('undefined');
62
+ expect(prototypeObject.injectedParsedPrototype, 'Object.prototype is unaffected').to.be.an('undefined');
91
63
  const rawObject = {} as any;
92
- expect(rawObject.injectedParsedPrototype).to.be.an('undefined');
64
+ expect(rawObject.injectedParsedPrototype, 'Instantiated objects are unaffected.').to.be.an('undefined');
93
65
  });
94
66
 
95
67
  it('Ignores key `constructor.prototype`', () => {
@@ -98,17 +70,19 @@ describe('PreferenceRegistryExtImpl:', () => {
98
70
  'a.constructor.prototype.injectedParsedConstructorPrototype': true,
99
71
  'constructor.prototype.injectedParsedConstructorPrototype': true,
100
72
  '[python].some.key.foo': 'value',
101
- '[python].a.constructor.prototype.injectedParsedConstructorPrototype': true
73
+ '[python].a.constructor.prototype.injectedParsedConstructorPrototype': true,
74
+ 'constructor': { 'prototype.injectedParsedConstructorPrototype': true },
75
+ 'b': { 'constructor': { 'prototype': { 'injectedParsedConstructorPrototype': true } } }
102
76
  };
103
- const result = preferenceRegistryExtImpl['parseConfigurationData'](value);
104
- expect(result.contents.my).to.be.an('object');
105
- expect(result.contents.__proto__).to.be.an('undefined');
106
- expect(result.contents.my.key1.foo).to.equal('value1');
77
+ const configuration = preferenceRegistryExtImpl['getConfigurationModel']('test', value);
78
+ const result = configuration['_contents'];
79
+ expect(result.my, 'Safe keys are preserved').to.be.an('object');
80
+ expect(result.__proto__, 'Keys containing __proto__ are ignored').to.be.an('undefined');
81
+ expect(result.my.key1.foo, 'Safe keys are dendrified.').to.equal('value1');
107
82
  const prototypeObject = Object.prototype as any;
108
- expect(prototypeObject.injectedParsedConstructorPrototype).to.be.an('undefined');
109
- expect(result.overrides[0].contents.__proto__).to.be.an('undefined');
83
+ expect(prototypeObject.injectedParsedConstructorPrototype, 'Object.prototype is unaffected').to.be.an('undefined');
110
84
  const rawObject = {} as any;
111
- expect(rawObject.injectedParsedConstructorPrototype).to.be.an('undefined');
85
+ expect(rawObject.injectedParsedConstructorPrototype, 'Instantiated objects are unaffected.').to.be.an('undefined');
112
86
  });
113
87
  });
114
88
 
@@ -250,6 +224,16 @@ describe('PreferenceRegistryExtImpl:', () => {
250
224
  const valuesRetrieved = preferenceRegistryExtImpl.getConfiguration(undefined, { uri: workspaceRoot, languageId: 'python' }).get('editor') as Record<string, unknown>;
251
225
  expect(valuesRetrieved.tabSize).equal(4);
252
226
  });
227
+ it('Allows access to language overrides in bracket form', () => {
228
+ const pythonOverrides = preferenceRegistryExtImpl.getConfiguration().get<Record<string, any>>('[python]');
229
+ expect(pythonOverrides).not.to.be.undefined;
230
+ expect(pythonOverrides?.['editor.renderWhitespace']).equal('all');
231
+ });
232
+ // https://github.com/eclipse-theia/theia/issues/12043
233
+ it('Allows access to preferences without specifying the section', () => {
234
+ const inspection = preferenceRegistryExtImpl.getConfiguration().inspect('editor.fontSize');
235
+ expect(inspection?.defaultValue).equal(14);
236
+ });
253
237
  });
254
238
 
255
239
  describe('Proxy Behavior', () => {
@@ -20,8 +20,8 @@ import { Emitter, Event } from '@theia/core/lib/common/event';
20
20
  import { isOSX, isWindows } from '@theia/core/lib/common/os';
21
21
  import { URI } from '@theia/core/shared/vscode-uri';
22
22
  import { ResourceMap } from '@theia/monaco-editor-core/esm/vs/base/common/map';
23
- import { IConfigurationOverrides, IOverrides } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration';
24
- import { Configuration, ConfigurationModel } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configurationModels';
23
+ import { IConfigurationOverrides } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration';
24
+ import { Configuration, ConfigurationModel, ConfigurationModelParser } from '@theia/monaco-editor-core/esm/vs/platform/configuration/common/configurationModels';
25
25
  import { Workspace, WorkspaceFolder } from '@theia/monaco-editor-core/esm/vs/platform/workspace/common/workspace';
26
26
  import * as theia from '@theia/plugin';
27
27
  import { v4 } from 'uuid';
@@ -234,12 +234,12 @@ export class PreferenceRegistryExtImpl implements PreferenceRegistryExt {
234
234
  }
235
235
 
236
236
  private parse(data: PreferenceData): Configuration {
237
- const defaultConfiguration = this.getConfigurationModel(data[PreferenceScope.Default]);
238
- const userConfiguration = this.getConfigurationModel(data[PreferenceScope.User]);
239
- const workspaceConfiguration = this.getConfigurationModel(data[PreferenceScope.Workspace]);
237
+ const defaultConfiguration = this.getConfigurationModel('Default', data[PreferenceScope.Default]);
238
+ const userConfiguration = this.getConfigurationModel('User', data[PreferenceScope.User]);
239
+ const workspaceConfiguration = this.getConfigurationModel('Workspace', data[PreferenceScope.Workspace]);
240
240
  const folderConfigurations = new ResourceMap<ConfigurationModel>();
241
241
  Object.keys(data[PreferenceScope.Folder]).forEach(resource => {
242
- folderConfigurations.set(URI.parse(resource), this.getConfigurationModel(data[PreferenceScope.Folder][resource]));
242
+ folderConfigurations.set(URI.parse(resource), this.getConfigurationModel(`Folder: ${resource}`, data[PreferenceScope.Folder][resource]));
243
243
  });
244
244
  return new Configuration(
245
245
  defaultConfiguration,
@@ -252,53 +252,41 @@ export class PreferenceRegistryExtImpl implements PreferenceRegistryExt {
252
252
  );
253
253
  }
254
254
 
255
- private getConfigurationModel(data: { [key: string]: any }): ConfigurationModel {
256
- if (!data) {
257
- return new ConfigurationModel();
258
- }
259
- const configData = this.parseConfigurationData(data);
260
- return new ConfigurationModel(configData.contents, configData.keys, configData.overrides);
255
+ private getConfigurationModel(label: string, data: { [key: string]: any }): ConfigurationModel {
256
+ const parser = new ConfigurationModelParser(label);
257
+ const sanitized = this.sanitize(data);
258
+ parser.parseRaw(sanitized);
259
+ return parser.configurationModel;
261
260
  }
262
261
 
263
- private readonly OVERRIDE_PROPERTY = '^\\[(.*)\\]$';
264
- private readonly OVERRIDE_PROPERTY_PATTERN = new RegExp(this.OVERRIDE_PROPERTY);
265
- private readonly OVERRIDE_KEY_TEST = /^\[([^\]]+)\]\./;
266
-
267
- private parseConfigurationData(data: { [key: string]: any }): Omit<IOverrides, 'identifiers'> & { overrides: IOverrides[] } {
268
- const keys = new Array<string>();
269
- const overrides: Record<string, IOverrides> = Object.create(null);
270
- const contents = Object.keys(data).reduce((result: any, key: string) => {
271
- if (injectionRe.test(key)) {
272
- return result;
273
- }
274
- const parts = key.split('.');
275
- let branch = result;
276
- const isOverride = this.OVERRIDE_KEY_TEST.test(key);
277
- if (!isOverride) {
278
- keys.push(key);
279
- }
280
- for (let i = 0; i < parts.length; i++) {
281
- if (i === 0 && isOverride) {
282
- const identifier = this.OVERRIDE_PROPERTY_PATTERN.exec(parts[i])![1];
283
- if (!overrides[identifier]) {
284
- overrides[identifier] = { keys: [], identifiers: [identifier], contents: Object.create(null) };
262
+ /**
263
+ * Creates a new object and assigns those keys of raw to it that are not likely to cause prototype polution.
264
+ * Also preprocesses override identifiers so that they take the form [identifier]: {...contents}.
265
+ */
266
+ private sanitize<T = unknown>(raw: T): T {
267
+ if (!isObject(raw)) { return raw; }
268
+ const asObject = raw as Record<string, unknown>;
269
+ const sanitized = Object.create(null);
270
+ for (const key of Object.keys(asObject)) {
271
+ if (!injectionRe.test(key)) {
272
+ const override = this.OVERRIDE_KEY_TEST.exec(key);
273
+ if (override) {
274
+ const overrideKey = `[${override[1]}]`;
275
+ const remainder = key.slice(override[0].length);
276
+ if (!isObject(sanitized[overrideKey])) {
277
+ sanitized[overrideKey] = Object.create(null);
285
278
  }
286
- branch = overrides[identifier].contents;
287
- overrides[identifier].keys.push(key.slice(parts[i].length + 1));
288
- } else if (i === parts.length - 1) {
289
- branch[parts[i]] = data[key];
279
+ sanitized[overrideKey][remainder] = this.sanitize(asObject[key]);
290
280
  } else {
291
- if (!branch[parts[i]]) {
292
- branch[parts[i]] = Object.create(null);
293
- }
294
- branch = branch[parts[i]];
281
+ sanitized[key] = this.sanitize(asObject[key]);
295
282
  }
296
283
  }
297
- return result;
298
- }, Object.create(null));
299
- return { contents, keys, overrides: Object.values(overrides) };
284
+ }
285
+ return sanitized;
300
286
  }
301
287
 
288
+ private readonly OVERRIDE_KEY_TEST = /^\[([^\]]+)\]\./;
289
+
302
290
  private toConfigurationChangeEvent(eventData: PreferenceChangeExt[]): theia.ConfigurationChangeEvent {
303
291
  return Object.freeze({
304
292
  affectsConfiguration: (section: string, scope?: theia.ConfigurationScope): boolean => {
@@ -291,14 +291,6 @@ export class TabsExtImpl implements TabsExt {
291
291
  return this._closeTabs(tabsOrTabGroups as theia.Tab[], preserveFocus);
292
292
  }
293
293
  },
294
- // move: async (tab: theia.Tab, viewColumn: ViewColumn, index: number, preserveFocus?: boolean) => {
295
- // const extHostTab = this._findExtHostTabFromApi(tab);
296
- // if (!extHostTab) {
297
- // throw new Error('Invalid tab');
298
- // }
299
- // this._proxy.$moveTab(extHostTab.tabId, index, typeConverters.ViewColumn.from(viewColumn), preserveFocus);
300
- // return;
301
- // }
302
294
  };
303
295
  this.apiObject = Object.freeze(obj);
304
296
  }
@@ -306,7 +298,6 @@ export class TabsExtImpl implements TabsExt {
306
298
  }
307
299
 
308
300
  $acceptEditorTabModel(tabGroups: TabGroupDto[]): void {
309
-
310
301
  const groupIdsBefore = new Set(this.tabGroupArr.map(group => group.groupId));
311
302
  const groupIdsAfter = new Set(tabGroups.map(dto => dto.groupId));
312
303
  const diff = diffSets(groupIdsBefore, groupIdsAfter);
@@ -314,23 +305,28 @@ export class TabsExtImpl implements TabsExt {
314
305
  const closed: theia.TabGroup[] = this.tabGroupArr.filter(group => diff.removed.includes(group.groupId)).map(group => group.apiObject);
315
306
  const opened: theia.TabGroup[] = [];
316
307
  const changed: theia.TabGroup[] = [];
308
+ const tabsOpened: theia.Tab[] = [];
317
309
 
318
310
  this.tabGroupArr = tabGroups.map(tabGroup => {
319
311
  const group = new TabGroupExt(tabGroup, () => this.activeGroupId);
320
312
  if (diff.added.includes(group.groupId)) {
321
- opened.push(group.apiObject);
313
+ opened.push({ activeTab: undefined, isActive: group.apiObject.isActive, tabs: [], viewColumn: group.apiObject.viewColumn });
314
+ tabsOpened.push(...group.apiObject.tabs);
322
315
  } else {
323
316
  changed.push(group.apiObject);
324
317
  }
325
318
  return group;
326
319
  });
327
320
 
328
- // Set the active tab group id
329
- const activeTabGroupId = assertIsDefined(tabGroups.find(group => group.isActive === true)?.groupId);
330
- if (activeTabGroupId !== undefined && this.activeGroupId !== activeTabGroupId) {
331
- this.activeGroupId = activeTabGroupId;
321
+ // Set the active tab group id. skip if no tabgroups are open
322
+ if (tabGroups.length > 0) {
323
+ const activeTabGroupId = assertIsDefined(tabGroups.find(group => group.isActive === true)?.groupId);
324
+ if (this.activeGroupId !== activeTabGroupId) {
325
+ this.activeGroupId = activeTabGroupId;
326
+ }
332
327
  }
333
328
  this.onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed }));
329
+ this.onDidChangeTabs.fire({ opened: tabsOpened, changed: [], closed: [] });
334
330
  }
335
331
 
336
332
  $acceptTabGroupUpdate(groupDto: TabGroupDto): void {
@@ -119,9 +119,9 @@ export class TextEditorsExtImpl implements TextEditorsExt {
119
119
  return new TextEditorDecorationType(this.proxy, options);
120
120
  }
121
121
 
122
- applyWorkspaceEdit(edit: theia.WorkspaceEdit): Promise<boolean> {
122
+ applyWorkspaceEdit(edit: theia.WorkspaceEdit, metadata?: theia.WorkspaceEditMetadata): Promise<boolean> {
123
123
  const dto = Converters.fromWorkspaceEdit(edit, this.editorsAndDocuments);
124
- return this.proxy.$tryApplyWorkspaceEdit(dto);
124
+ return this.proxy.$tryApplyWorkspaceEdit(dto, metadata);
125
125
  }
126
126
 
127
127
  saveAll(includeUntitled?: boolean): PromiseLike<boolean> {
@@ -1348,3 +1348,40 @@ export namespace InlayHintKind {
1348
1348
  return kind;
1349
1349
  }
1350
1350
  }
1351
+
1352
+ export namespace DataTransferItem {
1353
+ export function to(mime: string, item: model.DataTransferItemDTO, resolveFileData: (itemId: string) => Promise<Uint8Array>): theia.DataTransferItem {
1354
+ const file = item.fileData;
1355
+ if (file) {
1356
+ return new class extends types.DataTransferItem {
1357
+ override asFile(): theia.DataTransferFile {
1358
+ return {
1359
+ name: file.name,
1360
+ uri: URI.revive(file.uri),
1361
+ data: () => resolveFileData(item.id),
1362
+ };
1363
+ }
1364
+ }('');
1365
+ }
1366
+
1367
+ if (mime === 'text/uri-list' && item.uriListData) {
1368
+ return new types.DataTransferItem(reviveUriList(item.uriListData));
1369
+ }
1370
+
1371
+ return new types.DataTransferItem(item.asString);
1372
+ }
1373
+
1374
+ function reviveUriList(parts: ReadonlyArray<string | UriComponents>): string {
1375
+ return parts.map(part => typeof part === 'string' ? part : URI.revive(part).toString()).join('\r\n');
1376
+ }
1377
+ }
1378
+
1379
+ export namespace DataTransfer {
1380
+ export function toDataTransfer(value: model.DataTransferDTO, resolveFileData: (itemId: string) => Promise<Uint8Array>): theia.DataTransfer {
1381
+ const dataTransfer = new types.DataTransfer();
1382
+ for (const [mimeType, item] of value.items) {
1383
+ dataTransfer.set(mimeType, DataTransferItem.to(mimeType, item, resolveFileData));
1384
+ }
1385
+ return dataTransfer;
1386
+ }
1387
+ }
@@ -1530,6 +1530,17 @@ export class DocumentLink {
1530
1530
  }
1531
1531
  }
1532
1532
 
1533
+ @es5ClassCompat
1534
+ export class DocumentDropEdit {
1535
+ insertText: string | SnippetString;
1536
+
1537
+ additionalEdit?: WorkspaceEdit;
1538
+
1539
+ constructor(insertText: string | SnippetString) {
1540
+ this.insertText = insertText;
1541
+ }
1542
+ }
1543
+
1533
1544
  @es5ClassCompat
1534
1545
  export class CodeLens {
1535
1546