@theia/plugin-ext 1.42.0 → 1.43.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 (189) hide show
  1. package/lib/common/arrays.d.ts +4 -0
  2. package/lib/common/arrays.d.ts.map +1 -1
  3. package/lib/common/arrays.js +15 -1
  4. package/lib/common/arrays.js.map +1 -1
  5. package/lib/common/commands.d.ts +4 -0
  6. package/lib/common/commands.d.ts.map +1 -0
  7. package/lib/common/commands.js +17 -0
  8. package/lib/common/commands.js.map +1 -0
  9. package/lib/common/plugin-api-rpc-model.d.ts +1 -0
  10. package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
  11. package/lib/common/plugin-api-rpc-model.js.map +1 -1
  12. package/lib/common/plugin-api-rpc.d.ts +48 -6
  13. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  14. package/lib/common/plugin-api-rpc.js +3 -2
  15. package/lib/common/plugin-api-rpc.js.map +1 -1
  16. package/lib/common/plugin-protocol.d.ts +27 -3
  17. package/lib/common/plugin-protocol.d.ts.map +1 -1
  18. package/lib/common/plugin-protocol.js +8 -1
  19. package/lib/common/plugin-protocol.js.map +1 -1
  20. package/lib/common/test-types.d.ts +83 -0
  21. package/lib/common/test-types.d.ts.map +1 -0
  22. package/lib/common/test-types.js +40 -0
  23. package/lib/common/test-types.js.map +1 -0
  24. package/lib/hosted/browser/hosted-plugin.d.ts +4 -1
  25. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  26. package/lib/hosted/browser/hosted-plugin.js +11 -0
  27. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  28. package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
  29. package/lib/hosted/node/hosted-plugin-localization-service.js +71 -33
  30. package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
  31. package/lib/hosted/node/plugin-reader.d.ts.map +1 -1
  32. package/lib/hosted/node/plugin-reader.js +4 -2
  33. package/lib/hosted/node/plugin-reader.js.map +1 -1
  34. package/lib/hosted/node/scanners/scanner-theia.d.ts +5 -4
  35. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  36. package/lib/hosted/node/scanners/scanner-theia.js +79 -19
  37. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  38. package/lib/main/browser/command-registry-main.d.ts +3 -0
  39. package/lib/main/browser/command-registry-main.d.ts.map +1 -1
  40. package/lib/main/browser/command-registry-main.js +11 -1
  41. package/lib/main/browser/command-registry-main.js.map +1 -1
  42. package/lib/main/browser/languages-main.d.ts.map +1 -1
  43. package/lib/main/browser/languages-main.js +7 -5
  44. package/lib/main/browser/languages-main.js.map +1 -1
  45. package/lib/main/browser/main-context.d.ts.map +1 -1
  46. package/lib/main/browser/main-context.js +3 -0
  47. package/lib/main/browser/main-context.js.map +1 -1
  48. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +2 -2
  49. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  50. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +3 -0
  51. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  52. package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js +2 -2
  53. package/lib/main/browser/notebooks/notebook-documents-and-editors-main.js.map +1 -1
  54. package/lib/main/browser/notebooks/notebook-documents-main.js +1 -1
  55. package/lib/main/browser/notebooks/notebook-documents-main.js.map +1 -1
  56. package/lib/main/browser/notebooks/notebook-dto.js +2 -2
  57. package/lib/main/browser/notebooks/notebook-dto.js.map +1 -1
  58. package/lib/main/browser/notebooks/notebook-kernels-main.d.ts.map +1 -1
  59. package/lib/main/browser/notebooks/notebook-kernels-main.js +4 -10
  60. package/lib/main/browser/notebooks/notebook-kernels-main.js.map +1 -1
  61. package/lib/main/browser/notebooks/notebook-renderers-main.js +1 -1
  62. package/lib/main/browser/notebooks/notebook-renderers-main.js.map +1 -1
  63. package/lib/main/browser/notebooks/notebooks-main.d.ts +2 -2
  64. package/lib/main/browser/notebooks/notebooks-main.d.ts.map +1 -1
  65. package/lib/main/browser/notebooks/notebooks-main.js +5 -5
  66. package/lib/main/browser/notebooks/notebooks-main.js.map +1 -1
  67. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
  68. package/lib/main/browser/notebooks/renderers/cell-output-webview.js +3 -0
  69. package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
  70. package/lib/main/browser/notebooks/renderers/output-webview-internal.d.ts.map +1 -1
  71. package/lib/main/browser/notebooks/renderers/output-webview-internal.js +4 -2
  72. package/lib/main/browser/notebooks/renderers/output-webview-internal.js.map +1 -1
  73. package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
  74. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  75. package/lib/main/browser/plugin-contribution-handler.js +19 -1
  76. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  77. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  78. package/lib/main/browser/plugin-ext-frontend-module.js +3 -0
  79. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  80. package/lib/main/browser/plugin-icon-service.d.ts +20 -0
  81. package/lib/main/browser/plugin-icon-service.d.ts.map +1 -0
  82. package/lib/main/browser/plugin-icon-service.js +156 -0
  83. package/lib/main/browser/plugin-icon-service.js.map +1 -0
  84. package/lib/main/browser/terminal-main.d.ts +2 -2
  85. package/lib/main/browser/terminal-main.d.ts.map +1 -1
  86. package/lib/main/browser/terminal-main.js +5 -9
  87. package/lib/main/browser/terminal-main.js.map +1 -1
  88. package/lib/main/browser/test-main.d.ts +141 -0
  89. package/lib/main/browser/test-main.d.ts.map +1 -0
  90. package/lib/main/browser/test-main.js +560 -0
  91. package/lib/main/browser/test-main.js.map +1 -0
  92. package/lib/main/browser/view/plugin-view-registry.d.ts +14 -3
  93. package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
  94. package/lib/main/browser/view/plugin-view-registry.js +108 -56
  95. package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
  96. package/lib/main/browser/webview-views/webview-views-main.d.ts.map +1 -1
  97. package/lib/main/browser/webview-views/webview-views-main.js +5 -2
  98. package/lib/main/browser/webview-views/webview-views-main.js.map +1 -1
  99. package/lib/main/browser/webview-views/webview-views.d.ts +1 -0
  100. package/lib/main/browser/webview-views/webview-views.d.ts.map +1 -1
  101. package/lib/main/node/plugin-service.d.ts +2 -0
  102. package/lib/main/node/plugin-service.d.ts.map +1 -1
  103. package/lib/main/node/plugin-service.js +14 -1
  104. package/lib/main/node/plugin-service.js.map +1 -1
  105. package/lib/plugin/command-registry.d.ts +1 -3
  106. package/lib/plugin/command-registry.d.ts.map +1 -1
  107. package/lib/plugin/command-registry.js.map +1 -1
  108. package/lib/plugin/notebook/notebook-kernels.d.ts.map +1 -1
  109. package/lib/plugin/notebook/notebook-kernels.js +1 -0
  110. package/lib/plugin/notebook/notebook-kernels.js.map +1 -1
  111. package/lib/plugin/notebook/notebooks.d.ts.map +1 -1
  112. package/lib/plugin/notebook/notebooks.js +2 -2
  113. package/lib/plugin/notebook/notebooks.js.map +1 -1
  114. package/lib/plugin/plugin-context.d.ts.map +1 -1
  115. package/lib/plugin/plugin-context.js +9 -18
  116. package/lib/plugin/plugin-context.js.map +1 -1
  117. package/lib/plugin/telemetry-ext.js +1 -1
  118. package/lib/plugin/telemetry-ext.js.map +1 -1
  119. package/lib/plugin/terminal-ext.d.ts +11 -9
  120. package/lib/plugin/terminal-ext.d.ts.map +1 -1
  121. package/lib/plugin/terminal-ext.js +37 -25
  122. package/lib/plugin/terminal-ext.js.map +1 -1
  123. package/lib/plugin/test-item.d.ts +47 -0
  124. package/lib/plugin/test-item.d.ts.map +1 -0
  125. package/lib/plugin/test-item.js +196 -0
  126. package/lib/plugin/test-item.js.map +1 -0
  127. package/lib/plugin/tests.d.ts +117 -0
  128. package/lib/plugin/tests.d.ts.map +1 -0
  129. package/lib/plugin/tests.js +402 -0
  130. package/lib/plugin/tests.js.map +1 -0
  131. package/lib/plugin/tree/tree-views.d.ts.map +1 -1
  132. package/lib/plugin/tree/tree-views.js +2 -1
  133. package/lib/plugin/tree/tree-views.js.map +1 -1
  134. package/lib/plugin/type-converters.d.ts +10 -1
  135. package/lib/plugin/type-converters.d.ts.map +1 -1
  136. package/lib/plugin/type-converters.js +74 -2
  137. package/lib/plugin/type-converters.js.map +1 -1
  138. package/lib/plugin/types-impl.d.ts +29 -4
  139. package/lib/plugin/types-impl.d.ts.map +1 -1
  140. package/lib/plugin/types-impl.js +30 -8
  141. package/lib/plugin/types-impl.js.map +1 -1
  142. package/package.json +30 -29
  143. package/src/common/arrays.ts +16 -0
  144. package/src/common/commands.ts +19 -0
  145. package/src/common/plugin-api-rpc-model.ts +1 -0
  146. package/src/common/plugin-api-rpc.ts +69 -7
  147. package/src/common/plugin-protocol.ts +31 -3
  148. package/src/common/test-types.ts +133 -0
  149. package/src/hosted/browser/hosted-plugin.ts +13 -1
  150. package/src/hosted/node/hosted-plugin-localization-service.ts +72 -37
  151. package/src/hosted/node/plugin-reader.ts +4 -2
  152. package/src/hosted/node/scanners/scanner-theia.ts +85 -20
  153. package/src/main/browser/command-registry-main.ts +14 -1
  154. package/src/main/browser/languages-main.ts +7 -5
  155. package/src/main/browser/main-context.ts +4 -0
  156. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +3 -0
  157. package/src/main/browser/notebooks/notebook-documents-and-editors-main.ts +2 -2
  158. package/src/main/browser/notebooks/notebook-documents-main.ts +1 -1
  159. package/src/main/browser/notebooks/notebook-dto.ts +2 -2
  160. package/src/main/browser/notebooks/notebook-kernels-main.ts +6 -11
  161. package/src/main/browser/notebooks/notebook-renderers-main.ts +1 -1
  162. package/src/main/browser/notebooks/notebooks-main.ts +6 -6
  163. package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +3 -0
  164. package/src/main/browser/notebooks/renderers/output-webview-internal.ts +3 -2
  165. package/src/main/browser/plugin-contribution-handler.ts +19 -2
  166. package/src/main/browser/plugin-ext-frontend-module.ts +4 -0
  167. package/src/main/browser/plugin-icon-service.ts +156 -0
  168. package/src/main/browser/terminal-main.ts +7 -11
  169. package/src/main/browser/test-main.ts +618 -0
  170. package/src/main/browser/view/plugin-view-registry.ts +114 -56
  171. package/src/main/browser/webview-views/webview-views-main.ts +5 -2
  172. package/src/main/browser/webview-views/webview-views.ts +1 -0
  173. package/src/main/node/plugin-service.ts +12 -1
  174. package/src/plugin/command-registry.ts +1 -5
  175. package/src/plugin/notebook/notebook-kernels.ts +3 -1
  176. package/src/plugin/notebook/notebooks.ts +1 -3
  177. package/src/plugin/plugin-context.ts +13 -32
  178. package/src/plugin/telemetry-ext.ts +1 -1
  179. package/src/plugin/terminal-ext.ts +40 -26
  180. package/src/plugin/test-item.ts +174 -0
  181. package/src/plugin/tests.ts +482 -0
  182. package/src/plugin/tree/tree-views.ts +2 -1
  183. package/src/plugin/type-converters.ts +87 -3
  184. package/src/plugin/types-impl.ts +36 -5
  185. package/lib/plugin/stubs/tests-api.d.ts +0 -25
  186. package/lib/plugin/stubs/tests-api.d.ts.map +0 -1
  187. package/lib/plugin/stubs/tests-api.js +0 -70
  188. package/lib/plugin/stubs/tests-api.js.map +0 -1
  189. package/src/plugin/stubs/tests-api.ts +0 -102
@@ -18,7 +18,7 @@ import { injectable, inject, postConstruct, optional } from '@theia/core/shared/
18
18
  import {
19
19
  ApplicationShell, ViewContainer as ViewContainerWidget, WidgetManager, QuickViewService,
20
20
  ViewContainerIdentifier, ViewContainerTitleOptions, Widget, FrontendApplicationContribution,
21
- StatefulWidget, CommonMenus, TreeViewWelcomeWidget, codicon, ViewContainerPart, BaseWidget
21
+ StatefulWidget, CommonMenus, TreeViewWelcomeWidget, ViewContainerPart, BaseWidget
22
22
  } from '@theia/core/lib/browser';
23
23
  import { ViewContainer, View, ViewWelcome, PluginViewType } from '../../../common';
24
24
  import { PluginSharedStyle } from '../plugin-shared-style';
@@ -39,6 +39,7 @@ import { OutputWidget } from '@theia/output/lib/browser/output-widget';
39
39
  import { DebugConsoleContribution } from '@theia/debug/lib/browser/console/debug-console-contribution';
40
40
  import { TreeViewWidget } from './tree-view-widget';
41
41
  import { SEARCH_VIEW_CONTAINER_ID } from '@theia/search-in-workspace/lib/browser/search-in-workspace-factory';
42
+ import { TEST_VIEW_CONTAINER_ID } from '@theia/test/lib/browser/view/test-view-contribution';
42
43
  import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService';
43
44
  import { WebviewView, WebviewViewResolver } from '../webview-views/webview-views';
44
45
  import { WebviewWidget, WebviewWidgetIdentifier } from '../webview/webview';
@@ -46,6 +47,7 @@ import { CancellationToken } from '@theia/core/lib/common/cancellation';
46
47
  import { v4 } from 'uuid';
47
48
  import { nls } from '@theia/core';
48
49
  import { TheiaDockPanel } from '@theia/core/lib/browser/shell/theia-dock-panel';
50
+ import { Deferred } from '@theia/core/lib/common/promise-util';
49
51
 
50
52
  export const PLUGIN_VIEW_FACTORY_ID = 'plugin-view';
51
53
  export const PLUGIN_VIEW_CONTAINER_FACTORY_ID = 'plugin-view-container';
@@ -102,6 +104,10 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
102
104
  private readonly viewDataState = new Map<string, object>();
103
105
 
104
106
  private readonly webviewViewResolvers = new Map<string, WebviewViewResolver>();
107
+ protected readonly onNewResolverRegisteredEmitter = new Emitter<{ readonly viewType: string }>();
108
+ readonly onNewResolverRegistered = this.onNewResolverRegisteredEmitter.event;
109
+
110
+ private readonly webviewViewRevivals = new Map<string, { readonly webview: WebviewView; readonly revival: Deferred<void> }>();
105
111
 
106
112
  private static readonly ID_MAPPINGS: Map<string, string> = new Map([
107
113
  // VS Code Viewlets
@@ -111,6 +117,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
111
117
  [DebugWidget.ID, 'workbench.view.debug'],
112
118
  ['vsx-extensions-view-container', 'workbench.view.extensions'], // cannot use the id from 'vsx-registry' package because of circular dependency
113
119
  [PROBLEMS_WIDGET_ID, 'workbench.panel.markers'],
120
+ [TEST_VIEW_CONTAINER_ID, 'workbench.view.testing'],
114
121
  [OutputWidget.ID, 'workbench.panel.output'],
115
122
  [DebugConsoleContribution.options.id, 'workbench.panel.repl'],
116
123
  // Theia does not have a single terminal widget, but instead each terminal gets its own widget. Therefore "the terminal widget is active" doesn't make sense in Theia
@@ -136,6 +143,9 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
136
143
  if (factoryId === SEARCH_VIEW_CONTAINER_ID && widget instanceof ViewContainerWidget) {
137
144
  waitUntil(this.prepareViewContainer('search', widget));
138
145
  }
146
+ if (factoryId === TEST_VIEW_CONTAINER_ID && widget instanceof ViewContainerWidget) {
147
+ waitUntil(this.prepareViewContainer('test', widget));
148
+ }
139
149
  if (factoryId === DebugWidget.ID && widget instanceof DebugWidget) {
140
150
  const viewContainer = widget['sessionWidget']['viewContainer'];
141
151
  waitUntil(this.prepareViewContainer('debug', viewContainer));
@@ -161,11 +171,6 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
161
171
  disposable.push(event.widget.onDidDispose(() => disposable.dispose()));
162
172
  }
163
173
  });
164
- this.doRegisterViewContainer('test', 'left', {
165
- label: nls.localizeByDefault('Test'),
166
- iconClass: codicon('beaker'),
167
- closeable: true
168
- });
169
174
  this.contextKeyService.onDidChange(e => {
170
175
  for (const [, view] of this.views.values()) {
171
176
  const clauseContext = this.viewClauseContexts.get(view.id);
@@ -382,47 +387,51 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
382
387
  return toDispose;
383
388
  }
384
389
 
390
+ async resolveWebviewView(viewId: string, webview: WebviewView, cancellation: CancellationToken): Promise<void> {
391
+ const resolver = this.webviewViewResolvers.get(viewId);
392
+ if (resolver) {
393
+ return resolver.resolve(webview, cancellation);
394
+ }
395
+ const pendingRevival = this.webviewViewRevivals.get(viewId);
396
+ if (pendingRevival) {
397
+ return pendingRevival.revival.promise;
398
+ }
399
+ const pending = new Deferred<void>();
400
+ this.webviewViewRevivals.set(viewId, { webview, revival: pending });
401
+ return pending.promise;
402
+ }
403
+
385
404
  async registerWebviewView(viewId: string, resolver: WebviewViewResolver): Promise<Disposable> {
386
405
  if (this.webviewViewResolvers.has(viewId)) {
387
406
  throw new Error(`View resolver already registered for ${viewId}`);
388
407
  }
389
408
  this.webviewViewResolvers.set(viewId, resolver);
409
+ this.onNewResolverRegisteredEmitter.fire({ viewType: viewId });
390
410
 
391
- const webviewView = await this.createNewWebviewView();
392
- const token = CancellationToken.None;
393
- this.getView(viewId).then(async view => {
394
- if (view) {
395
- if (view.isVisible) {
396
- await this.prepareView(view, webviewView.webview.identifier.id);
397
- } else {
398
- const toDisposeOnDidExpandView = new DisposableCollection(this.onDidExpandView(async id => {
399
- if (id === viewId) {
400
- dispose();
401
- await this.prepareView(view, webviewView.webview.identifier.id);
402
- }
403
- }));
404
- const dispose = () => toDisposeOnDidExpandView.dispose();
405
- view.disposed.connect(dispose);
406
- toDisposeOnDidExpandView.push(Disposable.create(() => view.disposed.disconnect(dispose)));
407
- }
408
- }
409
- });
411
+ const toDispose = new DisposableCollection(Disposable.create(() => this.webviewViewResolvers.delete(viewId)));
412
+ this.initView(viewId, toDispose);
410
413
 
411
- resolver.resolve(webviewView, token);
414
+ const pendingRevival = this.webviewViewRevivals.get(viewId);
415
+ if (pendingRevival) {
416
+ resolver.resolve(pendingRevival.webview, CancellationToken.None).then(() => {
417
+ this.webviewViewRevivals.delete(viewId);
418
+ pendingRevival.revival.resolve();
419
+ });
420
+ }
412
421
 
413
- return Disposable.create(() => {
414
- this.webviewViewResolvers.delete(viewId);
415
- });
422
+ return toDispose;
416
423
  }
417
424
 
418
- async createNewWebviewView(): Promise<WebviewView> {
425
+ protected async createNewWebviewView(viewId: string): Promise<WebviewView> {
419
426
  const webview = await this.widgetManager.getOrCreateWidget<WebviewWidget>(
420
427
  WebviewWidget.FACTORY_ID, <WebviewWidgetIdentifier>{ id: v4() });
421
428
  webview.setContentOptions({ allowScripts: true });
422
429
 
423
430
  let _description: string | undefined;
431
+ let _resolved = false;
432
+ let _pendingResolution: Promise<void> | undefined;
424
433
 
425
- return {
434
+ const webviewView: WebviewView = {
426
435
  webview,
427
436
 
428
437
  get onDidChangeVisibility(): Event<boolean> { return webview.onDidChangeVisibility; },
@@ -442,9 +451,36 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
442
451
  onDidChangeBadge: webview.onDidChangeBadge,
443
452
  onDidChangeBadgeTooltip: webview.onDidChangeBadgeTooltip,
444
453
 
445
- dispose: webview.dispose,
454
+ dispose: () => {
455
+ _resolved = false;
456
+ webview.dispose();
457
+ toDispose.dispose();
458
+ },
459
+ resolve: async () => {
460
+ if (_resolved) {
461
+ return;
462
+ }
463
+ if (_pendingResolution) {
464
+ return _pendingResolution;
465
+ }
466
+ _pendingResolution = this.resolveWebviewView(viewId, webviewView, CancellationToken.None).then(() => {
467
+ _resolved = true;
468
+ _pendingResolution = undefined;
469
+ });
470
+ return _pendingResolution;
471
+ },
446
472
  show: webview.show
447
473
  };
474
+
475
+ const toDispose = this.onNewResolverRegistered(resolver => {
476
+ if (resolver.viewType === viewId) {
477
+ // Potentially re-activate if we have a new resolver
478
+ webviewView.resolve();
479
+ }
480
+ });
481
+
482
+ webviewView.resolve();
483
+ return webviewView;
448
484
  }
449
485
 
450
486
  registerViewWelcome(viewWelcome: ViewWelcome): Disposable {
@@ -526,7 +562,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
526
562
  return this.getView(viewId);
527
563
  }
528
564
 
529
- protected async prepareView(widget: PluginViewWidget, webviewId?: string): Promise<void> {
565
+ protected async prepareView(widget: PluginViewWidget): Promise<void> {
530
566
  const data = this.views.get(widget.options.viewId);
531
567
  if (!data) {
532
568
  return;
@@ -536,6 +572,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
536
572
  widget.title.label = view.name;
537
573
  }
538
574
  const currentDataWidget = widget.widgets[0];
575
+ const webviewId = currentDataWidget instanceof WebviewWidget ? currentDataWidget.identifier?.id : undefined;
539
576
  const viewDataWidget = await this.createViewDataWidget(view.id, webviewId);
540
577
  if (widget.isDisposed) {
541
578
  viewDataWidget?.dispose();
@@ -653,6 +690,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
653
690
  case EXPLORER_VIEW_CONTAINER_ID: return 'explorer';
654
691
  case SCM_VIEW_CONTAINER_ID: return 'scm';
655
692
  case SEARCH_VIEW_CONTAINER_ID: return 'search';
693
+ case TEST_VIEW_CONTAINER_ID: return 'test';
656
694
  case undefined: return container.parent?.parent instanceof DebugWidget ? 'debug' : container.id;
657
695
  case PLUGIN_VIEW_CONTAINER_FACTORY_ID: return this.toViewContainerId(description.options);
658
696
  default: return container.id;
@@ -669,6 +707,9 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
669
707
  if (viewContainerId === 'search') {
670
708
  return this.widgetManager.getWidget<ViewContainerWidget>(SEARCH_VIEW_CONTAINER_ID);
671
709
  }
710
+ if (viewContainerId === 'test') {
711
+ return this.widgetManager.getWidget<ViewContainerWidget>(TEST_VIEW_CONTAINER_ID);
712
+ }
672
713
  if (viewContainerId === 'debug') {
673
714
  const debug = await this.widgetManager.getWidget(DebugWidget.ID);
674
715
  if (debug instanceof DebugWidget) {
@@ -717,6 +758,12 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
717
758
  await this.prepareViewContainer('search', search);
718
759
  }
719
760
  })().catch(console.error));
761
+ promises.push((async () => {
762
+ const test = await this.widgetManager.getWidget(TEST_VIEW_CONTAINER_ID);
763
+ if (test instanceof ViewContainerWidget) {
764
+ await this.prepareViewContainer('test', test);
765
+ }
766
+ })().catch(console.error));
720
767
  promises.push((async () => {
721
768
  const debug = await this.widgetManager.getWidget(DebugWidget.ID);
722
769
  if (debug instanceof DebugWidget) {
@@ -793,35 +840,37 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
793
840
  this.viewDataProviders.delete(viewId);
794
841
  this.viewDataState.delete(viewId);
795
842
  }));
796
- this.getView(viewId).then(async view => {
797
- if (toDispose.disposed) {
798
- return;
799
- }
800
- if (view) {
801
- if (view.isVisible) {
802
- await this.prepareView(view);
803
- } else {
804
- const toDisposeOnDidExpandView = new DisposableCollection(this.onDidExpandView(async id => {
805
- if (id === viewId) {
806
- unsubscribe();
807
- await this.prepareView(view);
808
- }
809
- }));
810
- const unsubscribe = () => toDisposeOnDidExpandView.dispose();
811
- view.disposed.connect(unsubscribe);
812
- toDisposeOnDidExpandView.push(Disposable.create(() => view.disposed.disconnect(unsubscribe)));
813
- toDispose.push(toDisposeOnDidExpandView);
814
- }
815
- }
816
- });
843
+ this.initView(viewId, toDispose);
817
844
  return toDispose;
818
845
  }
819
846
 
847
+ protected async initView(viewId: string, toDispose: DisposableCollection): Promise<void> {
848
+ const view = await this.getView(viewId);
849
+ if (toDispose.disposed) {
850
+ return;
851
+ }
852
+ if (view) {
853
+ if (view.isVisible) {
854
+ await this.prepareView(view);
855
+ } else {
856
+ const toDisposeOnDidExpandView = new DisposableCollection(this.onDidExpandView(async id => {
857
+ if (id === viewId) {
858
+ unsubscribe();
859
+ await this.prepareView(view);
860
+ }
861
+ }));
862
+ const unsubscribe = () => toDisposeOnDidExpandView.dispose();
863
+ view.disposed.connect(unsubscribe);
864
+ toDisposeOnDidExpandView.push(Disposable.create(() => view.disposed.disconnect(unsubscribe)));
865
+ toDispose.push(toDisposeOnDidExpandView);
866
+ }
867
+ }
868
+ }
869
+
820
870
  protected async createViewDataWidget(viewId: string, webviewId?: string): Promise<Widget | undefined> {
821
871
  const view = this.views.get(viewId);
822
872
  if (view?.[1]?.type === PluginViewType.Webview) {
823
- const webviewWidget = this.widgetManager.getWidget(WebviewWidget.FACTORY_ID, <WebviewWidgetIdentifier>{ id: webviewId });
824
- return webviewWidget;
873
+ return this.createWebviewWidget(viewId, webviewId);
825
874
  }
826
875
  const provider = this.viewDataProviders.get(viewId);
827
876
  if (!view || !provider) {
@@ -839,6 +888,15 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
839
888
  return widget;
840
889
  }
841
890
 
891
+ protected async createWebviewWidget(viewId: string, webviewId?: string): Promise<Widget | undefined> {
892
+ if (!webviewId) {
893
+ const webviewView = await this.createNewWebviewView(viewId);
894
+ webviewId = webviewView.webview.identifier.id;
895
+ }
896
+ const webviewWidget = this.widgetManager.getWidget(WebviewWidget.FACTORY_ID, <WebviewWidgetIdentifier>{ id: webviewId });
897
+ return webviewWidget;
898
+ }
899
+
842
900
  protected storeViewDataStateOnDispose(viewId: string, widget: Widget & StatefulWidget): void {
843
901
  const dispose = widget.dispose.bind(widget);
844
902
  widget.dispose = () => {
@@ -83,7 +83,10 @@ export class WebviewViewsMainImpl implements WebviewViewsMain, Disposable {
83
83
  webviewView.webview.options = options;
84
84
  }
85
85
 
86
- webviewView.onDidChangeVisibility(visible => {
86
+ webviewView.onDidChangeVisibility(async visible => {
87
+ if (visible) {
88
+ await webviewView.resolve();
89
+ }
87
90
  this.proxy.$onDidChangeWebviewViewVisibility(handle, visible);
88
91
  });
89
92
 
@@ -93,7 +96,7 @@ export class WebviewViewsMainImpl implements WebviewViewsMain, Disposable {
93
96
  });
94
97
 
95
98
  try {
96
- this.proxy.$resolveWebviewView(handle, viewType, webviewView.title, state, cancellation);
99
+ await this.proxy.$resolveWebviewView(handle, viewType, webviewView.title, state, cancellation);
97
100
  } catch (error) {
98
101
  this.logger.error(`Error resolving webview view '${viewType}': ${error}`);
99
102
  webviewView.webview.setHTML('failed to load plugin webview view');
@@ -35,6 +35,7 @@ export interface WebviewView {
35
35
 
36
36
  dispose(): void;
37
37
  show(preserveFocus: boolean): void;
38
+ resolve(): Promise<void>;
38
39
  }
39
40
 
40
41
  export interface WebviewViewResolver {
@@ -26,6 +26,7 @@ import { environment } from '@theia/core/shared/@theia/application-package/lib/e
26
26
  import { WsRequestValidatorContribution } from '@theia/core/lib/node/ws-request-validators';
27
27
  import { MaybePromise } from '@theia/core/lib/common';
28
28
  import { ApplicationPackage } from '@theia/core/shared/@theia/application-package';
29
+ import { BackendRemoteService } from '@theia/core/lib/node/backend-remote-service';
29
30
 
30
31
  @injectable()
31
32
  export class PluginApiContribution implements BackendApplicationContribution, WsRequestValidatorContribution {
@@ -37,6 +38,9 @@ export class PluginApiContribution implements BackendApplicationContribution, Ws
37
38
  @inject(ApplicationPackage)
38
39
  protected readonly applicationPackage: ApplicationPackage;
39
40
 
41
+ @inject(BackendRemoteService)
42
+ protected readonly remoteService: BackendRemoteService;
43
+
40
44
  @postConstruct()
41
45
  protected init(): void {
42
46
  const webviewExternalEndpoint = this.webviewExternalEndpoint();
@@ -47,7 +51,14 @@ export class PluginApiContribution implements BackendApplicationContribution, Ws
47
51
  configure(app: express.Application): void {
48
52
  const webviewApp = express();
49
53
  webviewApp.use('/webview', express.static(path.join(this.applicationPackage.projectPath, 'lib', 'webview', 'pre')));
50
- app.use(vhost(this.webviewExternalEndpointRegExp, webviewApp));
54
+ if (this.remoteService.isRemoteServer()) {
55
+ // Any request to `subdomain.localhost:port/webview/...` will get redirected to the remote system.
56
+ // However, it will get redirected directly to the `localhost:remotePort` address, losing the subdomain info.
57
+ // In this case, we simply serve the webviews on a path.
58
+ app.use(webviewApp);
59
+ } else {
60
+ app.use(vhost(this.webviewExternalEndpointRegExp, webviewApp));
61
+ }
51
62
  }
52
63
 
53
64
  allowWsUpgrade(request: http.IncomingMessage): MaybePromise<boolean> {
@@ -25,15 +25,11 @@ import { RPCProtocol } from '../common/rpc-protocol';
25
25
  import { Disposable } from './types-impl';
26
26
  import { DisposableCollection } from '@theia/core';
27
27
  import { KnownCommands } from './known-commands';
28
+ import { ArgumentProcessor } from '../common/commands';
28
29
 
29
30
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
31
  export type Handler = <T>(...args: any[]) => T | PromiseLike<T | undefined>;
31
32
 
32
- export interface ArgumentProcessor {
33
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
- processArgument(arg: any): any;
35
- }
36
-
37
33
  export class CommandRegistryImpl implements CommandRegistryExt {
38
34
 
39
35
  private proxy: CommandRegistryMain;
@@ -483,11 +483,13 @@ class NotebookCellExecutionTask implements Disposable {
483
483
  });
484
484
  }
485
485
 
486
- private async updateOutputItems(items: theia.NotebookCellOutputItem | theia.NotebookCellOutputItem[], output: theia.NotebookCellOutput, append: boolean): Promise<void> {
486
+ private async updateOutputItems(items: theia.NotebookCellOutputItem | theia.NotebookCellOutputItem[],
487
+ output: theia.NotebookCellOutput, append: boolean): Promise<void> {
487
488
  items = NotebookCellOutputConverter.ensureUniqueMimeTypes(Array.isArray(items) ? items : [items], true);
488
489
  return this.updateSoon({
489
490
  editType: CellExecutionUpdateType.OutputItems,
490
491
  items: items.map(NotebookCellOutputItem.from),
492
+ outputId: output instanceof NotebookCellOutput ? output.outputId : '',
491
493
  append
492
494
  });
493
495
  }
@@ -137,7 +137,6 @@ export class NotebooksExtImpl implements NotebooksExt {
137
137
  this.notebookSerializer.set(handle, serializer);
138
138
  this.notebookProxy.$registerNotebookSerializer(
139
139
  handle,
140
- { id: plugin.model.id, location: plugin.pluginUri },
141
140
  viewType,
142
141
  typeConverters.NotebookDocumentContentOptions.from(options),
143
142
  );
@@ -347,8 +346,7 @@ export class NotebooksExtImpl implements NotebooksExt {
347
346
  }
348
347
 
349
348
  async showNotebookDocument(notebookOrUri: theia.NotebookDocument | TheiaURI, options?: theia.NotebookDocumentShowOptions): Promise<theia.NotebookEditor> {
350
-
351
- if (URI.isUri(notebookOrUri)) {
349
+ if (TheiaURI.isUri(notebookOrUri)) {
352
350
  notebookOrUri = await this.openNotebookDocument(notebookOrUri as TheiaURI);
353
351
  }
354
352
 
@@ -161,7 +161,6 @@ import {
161
161
  TerminalLocation,
162
162
  TerminalExitReason,
163
163
  TerminalProfile,
164
- TerminalQuickFixType,
165
164
  InlayHint,
166
165
  InlayHintKind,
167
166
  InlayHintLabelPart,
@@ -199,7 +198,10 @@ import {
199
198
  DocumentPasteEdit,
200
199
  ExternalUriOpenerPriority,
201
200
  EditSessionIdentityMatch,
202
- TerminalOutputAnchor
201
+ TerminalOutputAnchor,
202
+ TerminalQuickFixExecuteTerminalCommand,
203
+ TerminalQuickFixOpener,
204
+ TestResultState
203
205
  } from './types-impl';
204
206
  import { AuthenticationExtImpl } from './authentication-ext';
205
207
  import { SymbolKind } from '../common/plugin-api-rpc-model';
@@ -228,13 +230,6 @@ import { ClipboardExt } from './clipboard-ext';
228
230
  import { WebviewsExtImpl } from './webviews';
229
231
  import { ExtHostFileSystemEventService } from './file-system-event-service-ext-impl';
230
232
  import { LabelServiceExtImpl } from '../plugin/label-service';
231
- import {
232
- createRunProfile,
233
- createTestRun,
234
- testItemCollection,
235
- createTestItem,
236
- invalidateTestResults
237
- } from './stubs/tests-api';
238
233
  import { TimelineExtImpl } from './timeline';
239
234
  import { ThemingExtImpl } from './theming';
240
235
  import { CommentsExtImpl } from './comments';
@@ -252,6 +247,7 @@ import { NotebookRenderersExtImpl } from './notebook/notebook-renderers';
252
247
  import { NotebookKernelsExtImpl } from './notebook/notebook-kernels';
253
248
  import { NotebookDocumentsExtImpl } from './notebook/notebook-documents';
254
249
  import { NotebookEditorsExtImpl } from './notebook/notebook-editors';
250
+ import { TestingExtImpl } from './tests';
255
251
 
256
252
  export function createAPIFactory(
257
253
  rpc: RPCProtocol,
@@ -299,6 +295,7 @@ export function createAPIFactory(
299
295
  const customEditorExt = rpc.set(MAIN_RPC_CONTEXT.CUSTOM_EDITORS_EXT, new CustomEditorsExtImpl(rpc, documents, webviewExt, workspaceExt));
300
296
  const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt));
301
297
  const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl());
298
+ const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry));
302
299
  rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt);
303
300
 
304
301
  return function (plugin: InternalPlugin): typeof theia {
@@ -993,28 +990,10 @@ export function createAPIFactory(
993
990
  }
994
991
  };
995
992
 
996
- // Tests API (@stubbed)
997
- // The following implementation is temporarily `@stubbed` and marked as such under `theia.d.ts`
998
993
  const tests: typeof theia.tests = {
999
- createTestController(
1000
- provider,
1001
- controllerLabel: string,
1002
- refreshHandler?: (
1003
- token: theia.CancellationToken
1004
- ) => Thenable<void> | void
1005
- ) {
1006
- return {
1007
- id: provider,
1008
- label: controllerLabel,
1009
- items: testItemCollection,
1010
- refreshHandler,
1011
- createRunProfile,
1012
- createTestRun,
1013
- createTestItem,
1014
- invalidateTestResults,
1015
- dispose: () => undefined,
1016
- };
1017
- },
994
+ createTestController(id, label: string) {
995
+ return testingExt.createTestController(id, label);
996
+ }
1018
997
  };
1019
998
  /* End of Tests API */
1020
999
 
@@ -1391,8 +1370,10 @@ export function createAPIFactory(
1391
1370
  TerminalExitReason,
1392
1371
  DocumentPasteEdit,
1393
1372
  ExternalUriOpenerPriority,
1394
- TerminalQuickFixType,
1395
- EditSessionIdentityMatch
1373
+ TerminalQuickFixExecuteTerminalCommand,
1374
+ TerminalQuickFixOpener,
1375
+ EditSessionIdentityMatch,
1376
+ TestResultState
1396
1377
  };
1397
1378
  };
1398
1379
  }
@@ -294,5 +294,5 @@ export function cleanData(data: Record<string, any>, cleanUpPatterns: RegExp[]):
294
294
  return updatedProperty;
295
295
  }
296
296
  return undefined;
297
- }, new Set());
297
+ });
298
298
  }
@@ -18,11 +18,12 @@ import { Terminal, TerminalOptions, PseudoTerminalOptions, ExtensionTerminalOpti
18
18
  import { TerminalServiceExt, TerminalServiceMain, PLUGIN_RPC_CONTEXT } from '../common/plugin-api-rpc';
19
19
  import { RPCProtocol } from '../common/rpc-protocol';
20
20
  import { Event, Emitter } from '@theia/core/lib/common/event';
21
+ import { MultiKeyMap } from '@theia/core/lib/common/collections';
21
22
  import { Deferred } from '@theia/core/lib/common/promise-util';
22
23
  import * as theia from '@theia/plugin';
23
24
  import * as Converter from './type-converters';
24
25
  import { Disposable, EnvironmentVariableMutatorType, TerminalExitReason, ThemeIcon } from './types-impl';
25
- import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/base-terminal-protocol';
26
+ import { NO_ROOT_URI, SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/common/shell-terminal-protocol';
26
27
  import { ProvidedTerminalLink } from '../common/plugin-api-rpc-model';
27
28
  import { ThemeIcon as MonacoThemeIcon } from '@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService';
28
29
 
@@ -68,7 +69,7 @@ export class TerminalServiceExtImpl implements TerminalServiceExt {
68
69
  private readonly onDidChangeTerminalStateEmitter = new Emitter<Terminal>();
69
70
  readonly onDidChangeTerminalState: theia.Event<Terminal> = this.onDidChangeTerminalStateEmitter.event;
70
71
 
71
- protected environmentVariableCollections: Map<string, EnvironmentVariableCollection> = new Map();
72
+ protected environmentVariableCollections: MultiKeyMap<string, EnvironmentVariableCollectionImpl> = new MultiKeyMap(2);
72
73
 
73
74
  constructor(rpc: RPCProtocol) {
74
75
  this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.TERMINAL_MAIN);
@@ -303,46 +304,53 @@ export class TerminalServiceExtImpl implements TerminalServiceExt {
303
304
  *--------------------------------------------------------------------------------------------*/
304
305
  // some code copied and modified from https://github.com/microsoft/vscode/blob/1.49.0/src/vs/workbench/api/common/extHostTerminalService.ts
305
306
 
306
- getEnvironmentVariableCollection(extensionIdentifier: string): theia.EnvironmentVariableCollection {
307
- let collection = this.environmentVariableCollections.get(extensionIdentifier);
307
+ getEnvironmentVariableCollection(extensionIdentifier: string, rootUri: string = NO_ROOT_URI): theia.GlobalEnvironmentVariableCollection {
308
+ const that = this;
309
+ let collection = this.environmentVariableCollections.get([extensionIdentifier, rootUri]);
308
310
  if (!collection) {
309
- collection = new EnvironmentVariableCollection();
310
- this.setEnvironmentVariableCollection(extensionIdentifier, collection);
311
+ collection = new class extends EnvironmentVariableCollectionImpl {
312
+ override getScoped(scope: theia.EnvironmentVariableScope): theia.EnvironmentVariableCollection {
313
+ return that.getEnvironmentVariableCollection(extensionIdentifier, scope.workspaceFolder?.uri.toString());
314
+ }
315
+ }(true);
316
+ this.setEnvironmentVariableCollection(extensionIdentifier, rootUri, collection);
311
317
  }
312
318
  return collection;
313
319
  }
314
320
 
315
- private syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
321
+ private syncEnvironmentVariableCollection(extensionIdentifier: string, rootUri: string, collection: EnvironmentVariableCollectionImpl): void {
316
322
  const serialized = [...collection.map.entries()];
317
- this.proxy.$setEnvironmentVariableCollection(collection.persistent, {
318
- extensionIdentifier,
319
- collection: serialized.length === 0 ? undefined : serialized,
320
- description: Converter.fromMarkdownOrString(collection.description)
321
- });
323
+ this.proxy.$setEnvironmentVariableCollection(collection.persistent, extensionIdentifier,
324
+ rootUri,
325
+ {
326
+ mutators: serialized,
327
+ description: Converter.fromMarkdownOrString(collection.description)
328
+ });
322
329
  }
323
330
 
324
- private setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
325
- this.environmentVariableCollections.set(extensionIdentifier, collection);
331
+ private setEnvironmentVariableCollection(pluginIdentifier: string, rootUri: string, collection: EnvironmentVariableCollectionImpl): void {
332
+ this.environmentVariableCollections.set([pluginIdentifier, rootUri], collection);
326
333
  collection.onDidChangeCollection(() => {
327
334
  // When any collection value changes send this immediately, this is done to ensure
328
335
  // following calls to createTerminal will be created with the new environment. It will
329
336
  // result in more noise by sending multiple updates when called but collections are
330
337
  // expected to be small.
331
- this.syncEnvironmentVariableCollection(extensionIdentifier, collection);
338
+ this.syncEnvironmentVariableCollection(pluginIdentifier, rootUri, collection);
332
339
  });
333
340
  }
334
341
 
335
- $initEnvironmentVariableCollections(collections: [string, SerializableEnvironmentVariableCollection][]): void {
342
+ $initEnvironmentVariableCollections(collections: [string, string, boolean, SerializableEnvironmentVariableCollection][]): void {
336
343
  collections.forEach(entry => {
337
344
  const extensionIdentifier = entry[0];
338
- const collection = new EnvironmentVariableCollection(entry[1]);
339
- this.setEnvironmentVariableCollection(extensionIdentifier, collection);
345
+ const rootUri = entry[1];
346
+ const collection = new EnvironmentVariableCollectionImpl(entry[2], entry[3]);
347
+ this.setEnvironmentVariableCollection(extensionIdentifier, rootUri, collection);
340
348
  });
341
349
  }
342
350
 
343
351
  }
344
352
 
345
- export class EnvironmentVariableCollection implements theia.EnvironmentVariableCollection {
353
+ export class EnvironmentVariableCollectionImpl implements theia.GlobalEnvironmentVariableCollection {
346
354
  readonly map: Map<string, theia.EnvironmentVariableMutator> = new Map();
347
355
  private _description?: string | theia.MarkdownString;
348
356
  private _persistent: boolean = true;
@@ -363,25 +371,31 @@ export class EnvironmentVariableCollection implements theia.EnvironmentVariableC
363
371
  onDidChangeCollection: Event<void> = this.onDidChangeCollectionEmitter.event;
364
372
 
365
373
  constructor(
374
+ persistent: boolean,
366
375
  serialized?: SerializableEnvironmentVariableCollection
367
376
  ) {
368
- this.map = new Map(serialized);
377
+ this._persistent = persistent;
378
+ this.map = new Map(serialized?.mutators);
379
+ }
380
+
381
+ getScoped(scope: theia.EnvironmentVariableScope): theia.EnvironmentVariableCollection {
382
+ throw new Error('Cannot get scoped from a regular env var collection');
369
383
  }
370
384
 
371
385
  get size(): number {
372
386
  return this.map.size;
373
387
  }
374
388
 
375
- replace(variable: string, value: string): void {
376
- this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace });
389
+ replace(variable: string, value: string, options?: theia.EnvironmentVariableMutatorOptions): void {
390
+ this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, options: options ?? { applyAtProcessCreation: true } });
377
391
  }
378
392
 
379
- append(variable: string, value: string): void {
380
- this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append });
393
+ append(variable: string, value: string, options?: theia.EnvironmentVariableMutatorOptions): void {
394
+ this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, options: options ?? { applyAtProcessCreation: true } });
381
395
  }
382
396
 
383
- prepend(variable: string, value: string): void {
384
- this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend });
397
+ prepend(variable: string, value: string, options?: theia.EnvironmentVariableMutatorOptions): void {
398
+ this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, options: options ?? { applyAtProcessCreation: true } });
385
399
  }
386
400
 
387
401
  private _setIfDiffers(variable: string, mutator: theia.EnvironmentVariableMutator): void {