@theia/plugin-ext 1.50.0 → 1.51.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 (96) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +13 -2
  2. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc.js +12 -1
  4. package/lib/common/plugin-api-rpc.js.map +1 -1
  5. package/lib/common/proxy-handler.d.ts.map +1 -1
  6. package/lib/common/proxy-handler.js +3 -1
  7. package/lib/common/proxy-handler.js.map +1 -1
  8. package/lib/common/test-types.d.ts +1 -0
  9. package/lib/common/test-types.d.ts.map +1 -1
  10. package/lib/common/test-types.js.map +1 -1
  11. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  12. package/lib/hosted/browser/hosted-plugin.js +2 -0
  13. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  14. package/lib/hosted/browser/worker/worker-env-ext.d.ts +0 -3
  15. package/lib/hosted/browser/worker/worker-env-ext.d.ts.map +1 -1
  16. package/lib/hosted/browser/worker/worker-env-ext.js +2 -4
  17. package/lib/hosted/browser/worker/worker-env-ext.js.map +1 -1
  18. package/lib/main/browser/documents-main.d.ts +2 -4
  19. package/lib/main/browser/documents-main.d.ts.map +1 -1
  20. package/lib/main/browser/documents-main.js +2 -18
  21. package/lib/main/browser/documents-main.js.map +1 -1
  22. package/lib/main/browser/editors-and-documents-main.d.ts +2 -1
  23. package/lib/main/browser/editors-and-documents-main.d.ts.map +1 -1
  24. package/lib/main/browser/editors-and-documents-main.js +12 -5
  25. package/lib/main/browser/editors-and-documents-main.js.map +1 -1
  26. package/lib/main/browser/main-context.d.ts.map +1 -1
  27. package/lib/main/browser/main-context.js +4 -6
  28. package/lib/main/browser/main-context.js.map +1 -1
  29. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts +7 -0
  30. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
  31. package/lib/main/browser/notebooks/renderers/cell-output-webview.js +30 -4
  32. package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
  33. package/lib/main/browser/notebooks/renderers/output-webview-internal.d.ts.map +1 -1
  34. package/lib/main/browser/notebooks/renderers/output-webview-internal.js +15 -0
  35. package/lib/main/browser/notebooks/renderers/output-webview-internal.js.map +1 -1
  36. package/lib/main/browser/notebooks/renderers/webview-communication.d.ts +5 -1
  37. package/lib/main/browser/notebooks/renderers/webview-communication.d.ts.map +1 -1
  38. package/lib/main/browser/tabs/tabs-main.d.ts +5 -3
  39. package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
  40. package/lib/main/browser/tabs/tabs-main.js +37 -13
  41. package/lib/main/browser/tabs/tabs-main.js.map +1 -1
  42. package/lib/main/browser/test-main.d.ts +3 -2
  43. package/lib/main/browser/test-main.d.ts.map +1 -1
  44. package/lib/main/browser/test-main.js +7 -3
  45. package/lib/main/browser/test-main.js.map +1 -1
  46. package/lib/main/browser/text-editor-model-service.d.ts +1 -0
  47. package/lib/main/browser/text-editor-model-service.d.ts.map +1 -1
  48. package/lib/main/browser/text-editor-model-service.js +8 -0
  49. package/lib/main/browser/text-editor-model-service.js.map +1 -1
  50. package/lib/main/browser/view/tree-views-main.d.ts +0 -1
  51. package/lib/main/browser/view/tree-views-main.d.ts.map +1 -1
  52. package/lib/main/browser/view/tree-views-main.js +0 -3
  53. package/lib/main/browser/view/tree-views-main.js.map +1 -1
  54. package/lib/main/browser/view/view-context-key-service.d.ts +0 -2
  55. package/lib/main/browser/view/view-context-key-service.d.ts.map +1 -1
  56. package/lib/main/browser/view/view-context-key-service.js +0 -4
  57. package/lib/main/browser/view/view-context-key-service.js.map +1 -1
  58. package/lib/main/browser/window-state-main.js +1 -1
  59. package/lib/main/browser/window-state-main.js.map +1 -1
  60. package/lib/plugin/plugin-context.d.ts.map +1 -1
  61. package/lib/plugin/plugin-context.js +42 -2
  62. package/lib/plugin/plugin-context.js.map +1 -1
  63. package/lib/plugin/plugin-manager.d.ts +3 -1
  64. package/lib/plugin/plugin-manager.d.ts.map +1 -1
  65. package/lib/plugin/plugin-manager.js +13 -1
  66. package/lib/plugin/plugin-manager.js.map +1 -1
  67. package/lib/plugin/tests.d.ts +2 -2
  68. package/lib/plugin/tests.d.ts.map +1 -1
  69. package/lib/plugin/tests.js +12 -13
  70. package/lib/plugin/tests.js.map +1 -1
  71. package/lib/plugin/types-impl.d.ts +84 -1
  72. package/lib/plugin/types-impl.d.ts.map +1 -1
  73. package/lib/plugin/types-impl.js +106 -4
  74. package/lib/plugin/types-impl.js.map +1 -1
  75. package/package.json +29 -29
  76. package/src/common/plugin-api-rpc.ts +15 -2
  77. package/src/common/proxy-handler.ts +3 -1
  78. package/src/common/test-types.ts +1 -0
  79. package/src/hosted/browser/hosted-plugin.ts +3 -1
  80. package/src/hosted/browser/worker/worker-env-ext.ts +2 -4
  81. package/src/main/browser/documents-main.ts +3 -21
  82. package/src/main/browser/editors-and-documents-main.ts +13 -5
  83. package/src/main/browser/main-context.ts +5 -7
  84. package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +33 -4
  85. package/src/main/browser/notebooks/renderers/output-webview-internal.ts +18 -0
  86. package/src/main/browser/notebooks/renderers/webview-communication.ts +12 -1
  87. package/src/main/browser/tabs/tabs-main.ts +39 -15
  88. package/src/main/browser/test-main.ts +8 -3
  89. package/src/main/browser/text-editor-model-service.ts +9 -0
  90. package/src/main/browser/view/tree-views-main.ts +0 -4
  91. package/src/main/browser/view/view-context-key-service.ts +0 -6
  92. package/src/main/browser/window-state-main.ts +1 -1
  93. package/src/plugin/plugin-context.ts +59 -4
  94. package/src/plugin/plugin-manager.ts +17 -3
  95. package/src/plugin/tests.ts +14 -14
  96. package/src/plugin/types-impl.ts +121 -0
@@ -20,10 +20,10 @@ import { DisposableCollection, Disposable, UntitledResourceResolver } from '@the
20
20
  import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
21
21
  import { RPCProtocol } from '../../common/rpc-protocol';
22
22
  import { EditorModelService } from './text-editor-model-service';
23
- import { EditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser';
23
+ import { EditorOpenerOptions } from '@theia/editor/lib/browser';
24
24
  import URI from '@theia/core/lib/common/uri';
25
25
  import { URI as CodeURI } from '@theia/core/shared/vscode-uri';
26
- import { ApplicationShell, Saveable } from '@theia/core/lib/browser';
26
+ import { ApplicationShell } from '@theia/core/lib/browser';
27
27
  import { TextDocumentShowOptions } from '../../common/plugin-api-rpc-model';
28
28
  import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
29
29
  import { OpenerService } from '@theia/core/lib/browser/opener-service';
@@ -94,7 +94,6 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
94
94
  notebookDocuments: NotebookDocumentsMainImpl,
95
95
  private readonly modelService: EditorModelService,
96
96
  rpc: RPCProtocol,
97
- private editorManager: EditorManager,
98
97
  private openerService: OpenerService,
99
98
  private shell: ApplicationShell,
100
99
  private untitledResourceResolver: UntitledResourceResolver,
@@ -206,13 +205,7 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
206
205
  }
207
206
 
208
207
  async $trySaveDocument(uri: UriComponents): Promise<boolean> {
209
- const widget = await this.editorManager.getByUri(new URI(CodeURI.revive(uri)));
210
- if (widget) {
211
- await Saveable.save(widget);
212
- return true;
213
- }
214
-
215
- return false;
208
+ return this.modelService.save(new URI(CodeURI.revive(uri)));
216
209
  }
217
210
 
218
211
  async $tryOpenDocument(uri: UriComponents): Promise<boolean> {
@@ -226,17 +219,6 @@ export class DocumentsMainImpl implements DocumentsMain, Disposable {
226
219
  }
227
220
  }
228
221
 
229
- async $tryCloseDocument(uri: UriComponents): Promise<boolean> {
230
- const widget = await this.editorManager.getByUri(new URI(CodeURI.revive(uri)));
231
- if (widget) {
232
- await Saveable.save(widget);
233
- widget.close();
234
- return true;
235
- }
236
-
237
- return false;
238
- }
239
-
240
222
  static toEditorOpenerOptions(shell: ApplicationShell, options?: TextDocumentShowOptions): EditorOpenerOptions | undefined {
241
223
  if (!options) {
242
224
  return undefined;
@@ -33,6 +33,7 @@ import { TextEditorMain } from './text-editor-main';
33
33
  import { DisposableCollection, Emitter, URI } from '@theia/core';
34
34
  import { EditorManager, EditorWidget } from '@theia/editor/lib/browser';
35
35
  import { SaveableService } from '@theia/core/lib/browser/saveable-service';
36
+ import { TabsMainImpl } from './tabs/tabs-main';
36
37
 
37
38
  export class EditorsAndDocumentsMain implements Disposable {
38
39
 
@@ -59,14 +60,14 @@ export class EditorsAndDocumentsMain implements Disposable {
59
60
  Disposable.create(() => this.textEditors.clear())
60
61
  );
61
62
 
62
- constructor(rpc: RPCProtocol, container: interfaces.Container) {
63
+ constructor(rpc: RPCProtocol, container: interfaces.Container, tabsMain: TabsMainImpl) {
63
64
  this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.EDITORS_AND_DOCUMENTS_EXT);
64
65
 
65
66
  this.editorManager = container.get(EditorManager);
66
67
  this.modelService = container.get(EditorModelService);
67
68
  this.saveResourceService = container.get(SaveableService);
68
69
 
69
- this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService);
70
+ this.stateComputer = new EditorAndDocumentStateComputer(d => this.onDelta(d), this.editorManager, this.modelService, tabsMain);
70
71
  this.toDispose.push(this.stateComputer);
71
72
  this.toDispose.push(this.onTextEditorAddEmitter);
72
73
  this.toDispose.push(this.onTextEditorRemoveEmitter);
@@ -217,18 +218,25 @@ class EditorAndDocumentStateComputer implements Disposable {
217
218
  constructor(
218
219
  private callback: (delta: EditorAndDocumentStateDelta) => void,
219
220
  private readonly editorService: EditorManager,
220
- private readonly modelService: EditorModelService
221
+ private readonly modelService: EditorModelService,
222
+ private readonly tabsMain: TabsMainImpl
221
223
  ) { }
222
224
 
223
225
  listen(): void {
224
226
  if (this.toDispose.disposed) {
225
227
  return;
226
228
  }
227
- this.toDispose.push(this.editorService.onCreated(widget => {
229
+ this.toDispose.push(this.editorService.onCreated(async widget => {
230
+ await this.tabsMain.waitForWidget(widget);
228
231
  this.onTextEditorAdd(widget);
229
232
  this.update();
230
233
  }));
231
- this.toDispose.push(this.editorService.onCurrentEditorChanged(() => this.update()));
234
+ this.toDispose.push(this.editorService.onCurrentEditorChanged(async widget => {
235
+ if (widget) {
236
+ await this.tabsMain.waitForWidget(widget);
237
+ }
238
+ this.update();
239
+ }));
232
240
  this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this));
233
241
  this.toDispose.push(this.modelService.onModelRemoved(() => this.update()));
234
242
 
@@ -40,7 +40,6 @@ import { DecorationsMainImpl } from './decorations/decorations-main';
40
40
  import { ClipboardMainImpl } from './clipboard-main';
41
41
  import { DocumentsMainImpl } from './documents-main';
42
42
  import { TextEditorsMainImpl } from './text-editors-main';
43
- import { EditorManager } from '@theia/editor/lib/browser';
44
43
  import { EditorModelService } from './text-editor-model-service';
45
44
  import { OpenerService } from '@theia/core/lib/browser/opener-service';
46
45
  import { ApplicationShell } from '@theia/core/lib/browser/shell/application-shell';
@@ -88,19 +87,21 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
88
87
  const preferenceRegistryMain = new PreferenceRegistryMainImpl(rpc, container);
89
88
  rpc.set(PLUGIN_RPC_CONTEXT.PREFERENCE_REGISTRY_MAIN, preferenceRegistryMain);
90
89
 
91
- const editorsAndDocuments = new EditorsAndDocumentsMain(rpc, container);
90
+ const tabsMain = new TabsMainImpl(rpc, container);
91
+ rpc.set(PLUGIN_RPC_CONTEXT.TABS_MAIN, tabsMain);
92
+
93
+ const editorsAndDocuments = new EditorsAndDocumentsMain(rpc, container, tabsMain);
92
94
 
93
95
  const notebookDocumentsMain = new NotebookDocumentsMainImpl(rpc, container);
94
96
  rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOK_DOCUMENTS_MAIN, notebookDocumentsMain);
95
97
 
96
98
  const modelService = container.get(EditorModelService);
97
- const editorManager = container.get(EditorManager);
98
99
  const openerService = container.get<OpenerService>(OpenerService);
99
100
  const shell = container.get(ApplicationShell);
100
101
  const untitledResourceResolver = container.get(UntitledResourceResolver);
101
102
  const languageService = container.get(MonacoLanguages);
102
103
  const documentsMain = new DocumentsMainImpl(editorsAndDocuments, notebookDocumentsMain, modelService, rpc,
103
- editorManager, openerService, shell, untitledResourceResolver, languageService);
104
+ openerService, shell, untitledResourceResolver, languageService);
104
105
  rpc.set(PLUGIN_RPC_CONTEXT.DOCUMENTS_MAIN, documentsMain);
105
106
 
106
107
  rpc.set(PLUGIN_RPC_CONTEXT.NOTEBOOKS_MAIN, new NotebooksMainImpl(rpc, container, commandRegistryMain));
@@ -200,9 +201,6 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
200
201
  const commentsMain = new CommentsMainImp(rpc, container);
201
202
  rpc.set(PLUGIN_RPC_CONTEXT.COMMENTS_MAIN, commentsMain);
202
203
 
203
- const tabsMain = new TabsMainImpl(rpc, container);
204
- rpc.set(PLUGIN_RPC_CONTEXT.TABS_MAIN, tabsMain);
205
-
206
204
  const localizationMain = new LocalizationMainImpl(container);
207
205
  rpc.set(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN, localizationMain);
208
206
  }
@@ -35,6 +35,7 @@ import { CellUri } from '@theia/notebook/lib/common';
35
35
  import { Disposable, DisposableCollection, nls, QuickPickService } from '@theia/core';
36
36
  import { NotebookCellOutputModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-output-model';
37
37
  import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model';
38
+ import { NotebookOptionsService, NotebookOutputOptions } from '@theia/notebook/lib/browser/service/notebook-options';
38
39
 
39
40
  const CellModel = Symbol('CellModel');
40
41
  const Notebook = Symbol('NotebookModel');
@@ -147,6 +148,11 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
147
148
  @inject(AdditionalNotebookCellOutputCss)
148
149
  protected readonly additionalOutputCss: string;
149
150
 
151
+ @inject(NotebookOptionsService)
152
+ protected readonly notebookOptionsService: NotebookOptionsService;
153
+
154
+ protected options: NotebookOutputOptions;
155
+
150
156
  readonly id = generateUuid();
151
157
 
152
158
  protected editor: NotebookEditorWidget | undefined;
@@ -161,6 +167,11 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
161
167
  @postConstruct()
162
168
  protected async init(): Promise<void> {
163
169
  this.editor = this.notebookEditorWidgetService.getNotebookEditor(NOTEBOOK_EDITOR_ID_PREFIX + CellUri.parse(this.cell.uri)?.notebook);
170
+ this.options = this.notebookOptionsService.computeOutputOptions();
171
+ this.toDispose.push(this.notebookOptionsService.onDidChangeOutputOptions(options => {
172
+ this.options = options;
173
+ this.updateStyles();
174
+ }));
164
175
 
165
176
  this.toDispose.push(this.cell.onDidChangeOutputs(outputChange => this.updateOutput(outputChange)));
166
177
  this.toDispose.push(this.cell.onDidChangeOutputItems(output => {
@@ -187,6 +198,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
187
198
  }
188
199
 
189
200
  this.webviewWidget = await this.widgetManager.getOrCreateWidget(WebviewWidget.FACTORY_ID, { id: this.id });
201
+ this.webviewWidget.parent = this.editor ?? null;
190
202
  this.webviewWidget.setContentOptions({
191
203
  allowScripts: true,
192
204
  // eslint-disable-next-line max-len
@@ -271,6 +283,7 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
271
283
  switch (message.type) {
272
284
  case 'initialized':
273
285
  this.updateOutput({ newOutputs: this.cell.outputs, start: 0, deleteCount: 0 });
286
+ this.updateStyles();
274
287
  break;
275
288
  case 'customRendererMessage':
276
289
  // console.log('from webview customRendererMessage ', message.rendererId, '', JSON.stringify(message.message));
@@ -301,6 +314,22 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
301
314
  return kernelPreloads.concat(staticPreloads);
302
315
  }
303
316
 
317
+ protected updateStyles(): void {
318
+ this.webviewWidget.sendMessage({
319
+ type: 'notebookStyles',
320
+ styles: this.generateStyles()
321
+ });
322
+ }
323
+
324
+ protected generateStyles(): { [key: string]: string } {
325
+ return {
326
+ 'notebook-cell-output-font-size': `${this.options.outputFontSize || this.options.fontSize}px`,
327
+ 'notebook-cell-output-line-height': `${this.options.outputLineHeight}px`,
328
+ 'notebook-cell-output-max-height': `${this.options.outputLineHeight * this.options.outputLineLimit}px`,
329
+ 'notebook-cell-output-font-family': this.options.outputFontFamily || this.options.fontFamily,
330
+ };
331
+ }
332
+
304
333
  private async createWebviewContent(): Promise<string> {
305
334
  const isWorkspaceTrusted = await this.workspaceTrustService.getWorkspaceTrust();
306
335
  const preloads = this.preloadsScriptString(isWorkspaceTrusted);
@@ -324,10 +353,10 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
324
353
  const ctx: PreloadContext = {
325
354
  isWorkspaceTrusted,
326
355
  rendererData: this.notebookRendererRegistry.notebookRenderers,
327
- renderOptions: { // TODO these should be changeable in the settings
328
- lineLimit: 30,
329
- outputScrolling: false,
330
- outputWordWrap: false,
356
+ renderOptions: {
357
+ lineLimit: this.options.outputLineLimit,
358
+ outputScrolling: this.options.outputScrolling,
359
+ outputWordWrap: this.options.outputWordWrap,
331
360
  },
332
361
  staticPreloadsData: this.getPreloads()
333
362
  };
@@ -569,6 +569,24 @@ export async function outputWebviewPreload(ctx: PreloadContext): Promise<void> {
569
569
  }
570
570
  break;
571
571
  }
572
+ case 'notebookStyles': {
573
+ const documentStyle = window.document.documentElement.style;
574
+
575
+ for (let i = documentStyle.length - 1; i >= 0; i--) {
576
+ const property = documentStyle[i];
577
+
578
+ // Don't remove properties that the webview might have added separately
579
+ if (property && property.startsWith('--notebook-')) {
580
+ documentStyle.removeProperty(property);
581
+ }
582
+ }
583
+
584
+ // Re-add new properties
585
+ for (const [name, value] of Object.entries(event.data.styles)) {
586
+ documentStyle.setProperty(`--${name}`, value);
587
+ }
588
+ break;
589
+ }
572
590
  }
573
591
  });
574
592
  window.addEventListener('wheel', handleWheel);
@@ -59,7 +59,18 @@ export interface PreloadMessage {
59
59
  readonly resources: string[];
60
60
  }
61
61
 
62
- export type ToWebviewMessage = UpdateRenderersMessage | OutputChangedMessage | ChangePreferredMimetypeMessage | CustomRendererMessage | KernelMessage | PreloadMessage;
62
+ export interface notebookStylesMessage {
63
+ readonly type: 'notebookStyles';
64
+ styles: Record<string, string>;
65
+ }
66
+
67
+ export type ToWebviewMessage = UpdateRenderersMessage
68
+ | OutputChangedMessage
69
+ | ChangePreferredMimetypeMessage
70
+ | CustomRendererMessage
71
+ | KernelMessage
72
+ | PreloadMessage
73
+ | notebookStylesMessage;
63
74
 
64
75
  export interface WebviewInitialized {
65
76
  readonly type: 'initialized';
@@ -25,7 +25,7 @@ import { toUriComponents } from '../hierarchy/hierarchy-types-converters';
25
25
  import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
26
26
  import { DisposableCollection } from '@theia/core';
27
27
  import { NotebookEditorWidget } from '@theia/notebook/lib/browser';
28
- import { ViewColumnService } from '@theia/core/lib/browser/shell/view-column-service';
28
+ import { Deferred } from '@theia/core/lib/common/promise-util';
29
29
 
30
30
  interface TabInfo {
31
31
  tab: TabDto;
@@ -38,17 +38,17 @@ export class TabsMainImpl implements TabsMain, Disposable {
38
38
  private readonly proxy: TabsExt;
39
39
  private tabGroupModel = new Map<TabBar<Widget>, TabGroupDto>();
40
40
  private tabInfoLookup = new Map<Title<Widget>, TabInfo>();
41
+ private waitQueue = new Map<Widget, Deferred>();
41
42
 
42
43
  private applicationShell: ApplicationShell;
43
44
 
44
45
  private disposableTabBarListeners: DisposableCollection = new DisposableCollection();
45
46
  private toDisposeOnDestroy: DisposableCollection = new DisposableCollection();
46
47
 
47
- private groupIdCounter = 0;
48
+ private groupIdCounter = 1;
48
49
  private currentActiveGroup: TabGroupDto;
49
50
 
50
51
  private tabGroupChanged: boolean = false;
51
- private viewColumnService: ViewColumnService;
52
52
 
53
53
  private readonly defaultTabGroup: TabGroupDto = {
54
54
  groupId: 0,
@@ -64,11 +64,10 @@ export class TabsMainImpl implements TabsMain, Disposable {
64
64
  this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TABS_EXT);
65
65
 
66
66
  this.applicationShell = container.get(ApplicationShell);
67
- this.viewColumnService = container.get(ViewColumnService);
68
67
  this.createTabsModel();
69
68
 
70
69
  const tabBars = this.applicationShell.mainPanel.tabBars();
71
- for (let tabBar; tabBar = tabBars.next();) {
70
+ for (let tabBar: TabBar<Widget> | undefined; tabBar = tabBars.next();) {
72
71
  this.attachListenersToTabBar(tabBar);
73
72
  }
74
73
 
@@ -110,6 +109,21 @@ export class TabsMainImpl implements TabsMain, Disposable {
110
109
  });
111
110
  }
112
111
 
112
+ waitForWidget(widget: Widget): Promise<void> {
113
+ const deferred = new Deferred<void>();
114
+ this.waitQueue.set(widget, deferred);
115
+
116
+ const timeout = setTimeout(() => {
117
+ deferred.resolve(); // resolve to unblock the event
118
+ }, 1000);
119
+
120
+ deferred.promise.then(() => {
121
+ clearTimeout(timeout);
122
+ });
123
+
124
+ return deferred.promise;
125
+ }
126
+
113
127
  protected createTabsModel(): void {
114
128
  if (this.applicationShell.mainAreaTabBars.length === 0) {
115
129
  this.proxy.$acceptEditorTabModel([this.defaultTabGroup]);
@@ -119,9 +133,9 @@ export class TabsMainImpl implements TabsMain, Disposable {
119
133
  this.tabInfoLookup.clear();
120
134
  this.disposableTabBarListeners.dispose();
121
135
  this.applicationShell.mainAreaTabBars
122
- .forEach((tabBar, i) => {
136
+ .forEach(tabBar => {
123
137
  this.attachListenersToTabBar(tabBar);
124
- const groupDto = this.createTabGroupDto(tabBar, i);
138
+ const groupDto = this.createTabGroupDto(tabBar);
125
139
  tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index }));
126
140
  newTabGroupModel.set(tabBar, groupDto);
127
141
  });
@@ -131,31 +145,38 @@ export class TabsMainImpl implements TabsMain, Disposable {
131
145
  }
132
146
  this.tabGroupModel = newTabGroupModel;
133
147
  this.proxy.$acceptEditorTabModel(Array.from(this.tabGroupModel.values()));
148
+ // Resolve all waiting widget promises
149
+ this.waitQueue.forEach(deferred => deferred.resolve());
150
+ this.waitQueue.clear();
134
151
  }
135
152
 
136
- protected createTabDto(tabTitle: Title<Widget>, groupId: number): TabDto {
153
+ protected createTabDto(tabTitle: Title<Widget>, groupId: number, newTab = false): TabDto {
137
154
  const widget = tabTitle.owner;
155
+ const active = newTab || this.getTabBar(tabTitle)?.currentTitle === tabTitle;
138
156
  return {
139
157
  id: this.createTabId(tabTitle, groupId),
140
158
  label: tabTitle.label,
141
159
  input: this.evaluateTabDtoInput(widget),
142
- isActive: tabTitle.owner.isVisible,
160
+ isActive: active,
143
161
  isPinned: tabTitle.className.includes(PINNED_CLASS),
144
162
  isDirty: Saveable.isDirty(widget),
145
163
  isPreview: widget instanceof EditorPreviewWidget && widget.isPreview
146
164
  };
147
165
  }
148
166
 
167
+ protected getTabBar(tabTitle: Title<Widget>): TabBar<Widget> | undefined {
168
+ return this.applicationShell.mainPanel.findTabBar(tabTitle);
169
+ }
170
+
149
171
  protected createTabId(tabTitle: Title<Widget>, groupId: number): string {
150
172
  return `${groupId}~${tabTitle.owner.id}`;
151
173
  }
152
174
 
153
- protected createTabGroupDto(tabBar: TabBar<Widget>, index: number): TabGroupDto {
154
- const oldDto = index === 0 ? this.defaultTabGroup : this.tabGroupModel.get(tabBar);
175
+ protected createTabGroupDto(tabBar: TabBar<Widget>): TabGroupDto {
176
+ const oldDto = this.tabGroupModel.get(tabBar);
155
177
  const groupId = oldDto?.groupId ?? this.groupIdCounter++;
156
178
  const tabs = tabBar.titles.map(title => this.createTabDto(title, groupId));
157
- const viewColumn = this.viewColumnService.getViewColumn(tabBar.id)
158
- ?? this.applicationShell.allTabBars.indexOf(tabBar);
179
+ const viewColumn = 0; // TODO: Implement correct viewColumn handling
159
180
  return {
160
181
  groupId,
161
182
  tabs,
@@ -190,7 +211,6 @@ export class TabsMainImpl implements TabsMain, Disposable {
190
211
  uri: toUriComponents(widget.editor.uri.toString())
191
212
  };
192
213
  }
193
- // TODO notebook support when implemented
194
214
  } else if (widget instanceof ViewContainer) {
195
215
  return {
196
216
  kind: TabInputKind.WebviewEditorInput,
@@ -238,7 +258,7 @@ export class TabsMainImpl implements TabsMain, Disposable {
238
258
  private onTabCreated(tabBar: TabBar<Widget>, args: TabBar.ITabActivateRequestedArgs<Widget>): void {
239
259
  const group = this.getOrRebuildModel(this.tabGroupModel, tabBar);
240
260
  this.connectToSignal(this.disposableTabBarListeners, args.title.changed, this.onTabTitleChanged);
241
- const tabDto = this.createTabDto(args.title, group.groupId);
261
+ const tabDto = this.createTabDto(args.title, group.groupId, true);
242
262
  this.tabInfoLookup.set(args.title, { group, tab: tabDto, tabIndex: args.index });
243
263
  group.tabs.splice(args.index, 0, tabDto);
244
264
  this.proxy.$acceptTabOperation({
@@ -247,6 +267,8 @@ export class TabsMainImpl implements TabsMain, Disposable {
247
267
  tabDto,
248
268
  groupId: group.groupId
249
269
  });
270
+ this.waitQueue.get(args.title.owner)?.resolve();
271
+ this.waitQueue.delete(args.title.owner);
250
272
  }
251
273
 
252
274
  private onTabTitleChanged(title: Title<Widget>): void {
@@ -275,6 +297,8 @@ export class TabsMainImpl implements TabsMain, Disposable {
275
297
  groupId: tabInfo.group.groupId
276
298
  });
277
299
  }
300
+ this.waitQueue.get(title.owner)?.resolve();
301
+ this.waitQueue.delete(title.owner);
278
302
  }
279
303
 
280
304
  private onTabClosed(tabInfo: TabInfo, title: Title<Widget>): void {
@@ -18,6 +18,7 @@ import { SimpleObservableCollection, TreeCollection, observableProperty } from '
18
18
  import {
19
19
  TestController, TestItem, TestOutputItem, TestRun, TestRunProfile, TestService, TestState, TestStateChangedEvent
20
20
  } from '@theia/test/lib/browser/test-service';
21
+ import { TestExecutionProgressService } from '@theia/test/lib/browser/test-execution-progress-service';
21
22
  import { AccumulatingTreeDeltaEmitter, CollectionDelta, DeltaKind, TreeDelta, TreeDeltaBuilder } from '@theia/test/lib/common/tree-delta';
22
23
  import { Emitter, Location, Range } from '@theia/core/shared/vscode-languageserver-protocol';
23
24
  import { Range as PluginRange, Location as PluginLocation } from '../../common/plugin-api-rpc-model';
@@ -245,13 +246,14 @@ class TestRunProfileImpl implements TestRunProfile {
245
246
  this.proxy.$onConfigureRunProfile(this.controllerId, this.id);
246
247
  }
247
248
 
248
- run(name: string, included: TestItem[], excluded: TestItem[]): void {
249
+ run(name: string, included: TestItem[], excluded: TestItem[], preserveFocus: boolean): void {
249
250
  this.proxy.$onRunControllerTests([{
250
251
  controllerId: this.controllerId,
251
252
  name,
252
253
  profileId: this.id,
253
254
  includedTests: included.map(item => itemToPath(item)),
254
- excludedTests: excluded.map(item => itemToPath(item))
255
+ excludedTests: excluded.map(item => itemToPath(item)),
256
+ preserveFocus
255
257
  }]);
256
258
  }
257
259
  }
@@ -551,12 +553,14 @@ class TestControllerImpl implements TestController {
551
553
 
552
554
  export class TestingMainImpl implements TestingMain {
553
555
  private testService: TestService;
556
+ private testExecutionProgressService: TestExecutionProgressService;
554
557
  private controllerRegistrations = new Map<string, [TestControllerImpl, Disposable]>();
555
558
  private proxy: TestingExt;
556
559
  canRefresh: boolean;
557
560
 
558
561
  constructor(rpc: RPCProtocol, container: interfaces.Container, commandRegistry: CommandRegistryMainImpl) {
559
562
  this.testService = container.get(TestService);
563
+ this.testExecutionProgressService = container.get(TestExecutionProgressService);
560
564
  this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TESTING_EXT);
561
565
  commandRegistry.registerArgumentProcessor({
562
566
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -614,7 +618,8 @@ export class TestingMainImpl implements TestingMain {
614
618
  $removeTestRunProfile(controllerId: string, profileId: string): void {
615
619
  this.withController(controllerId).removeProfile(profileId);
616
620
  }
617
- $notifyTestRunCreated(controllerId: string, run: TestRunDTO): void {
621
+ $notifyTestRunCreated(controllerId: string, run: TestRunDTO, preserveFocus: boolean): void {
622
+ this.testExecutionProgressService.onTestRunRequested(preserveFocus);
618
623
  this.withController(controllerId).addRun(run.id, run.name, run.isRunning);
619
624
  }
620
625
  $notifyTestStateChanged(controllerId: string, runId: string, stateChanges: TestStateChangeDTO[], outputChanges: TestOutputDTO[]): void {
@@ -76,6 +76,15 @@ export class EditorModelService {
76
76
  return this.monacoModelService.models;
77
77
  }
78
78
 
79
+ async save(uri: URI): Promise<boolean> {
80
+ const model = this.monacoModelService.get(uri.toString());
81
+ if (model) {
82
+ await model.save();
83
+ return true;
84
+ }
85
+ return false;
86
+ }
87
+
79
88
  async saveAll(includeUntitled?: boolean): Promise<boolean> {
80
89
  const saves = [];
81
90
  for (const model of this.monacoModelService.models) {
@@ -24,7 +24,6 @@ import {
24
24
  CompositeTreeNode,
25
25
  WidgetManager
26
26
  } from '@theia/core/lib/browser';
27
- import { ViewContextKeyService } from './view-context-key-service';
28
27
  import { Disposable, DisposableCollection } from '@theia/core';
29
28
  import { TreeViewWidget, TreeViewNode, PluginTreeModel, TreeViewWidgetOptions } from './tree-view-widget';
30
29
  import { PluginViewWidget } from './plugin-view-widget';
@@ -36,7 +35,6 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
36
35
 
37
36
  private readonly proxy: TreeViewsExt;
38
37
  private readonly viewRegistry: PluginViewRegistry;
39
- private readonly contextKeys: ViewContextKeyService;
40
38
  private readonly widgetManager: WidgetManager;
41
39
  private readonly fileContentStore: DnDFileContentStore;
42
40
 
@@ -50,7 +48,6 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
50
48
  this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TREE_VIEWS_EXT);
51
49
  this.viewRegistry = container.get(PluginViewRegistry);
52
50
 
53
- this.contextKeys = this.container.get(ViewContextKeyService);
54
51
  this.widgetManager = this.container.get(WidgetManager);
55
52
  this.fileContentStore = this.container.get(DnDFileContentStore);
56
53
  }
@@ -197,7 +194,6 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
197
194
  }));
198
195
 
199
196
  this.toDispose.push(treeViewWidget.model.onSelectionChanged(event => {
200
- this.contextKeys.view.set(treeViewId);
201
197
  this.proxy.$setSelection(treeViewId, event.map((node: TreeViewNode) => node.id));
202
198
  }));
203
199
 
@@ -19,11 +19,6 @@ import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-k
19
19
 
20
20
  @injectable()
21
21
  export class ViewContextKeyService {
22
- protected _view: ContextKey<string>;
23
- get view(): ContextKey<string> {
24
- return this._view;
25
- }
26
-
27
22
  protected _viewItem: ContextKey<string>;
28
23
  get viewItem(): ContextKey<string> {
29
24
  return this._viewItem;
@@ -56,7 +51,6 @@ export class ViewContextKeyService {
56
51
 
57
52
  @postConstruct()
58
53
  protected init(): void {
59
- this._view = this.contextKeyService.createKey('view', '');
60
54
  this._viewItem = this.contextKeyService.createKey('viewItem', '');
61
55
  this._activeViewlet = this.contextKeyService.createKey('activeViewlet', '');
62
56
  this._activePanel = this.contextKeyService.createKey('activePanel', '');
@@ -69,7 +69,7 @@ export class WindowStateMain implements WindowMain, Disposable {
69
69
  const uri = URI.revive(uriComponent);
70
70
  const url = new CoreURI(encodeURI(uri.toString(true)));
71
71
  try {
72
- await open(this.openerService, url);
72
+ await open(this.openerService, url, { openExternalApp: true });
73
73
  return true;
74
74
  } catch (e) {
75
75
  return false;