@theia/terminal-manager 1.68.0-next.9 → 1.68.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/lib/browser/terminal-manager-frontend-contribution.d.ts +9 -1
  2. package/lib/browser/terminal-manager-frontend-contribution.d.ts.map +1 -1
  3. package/lib/browser/terminal-manager-frontend-contribution.js +45 -5
  4. package/lib/browser/terminal-manager-frontend-contribution.js.map +1 -1
  5. package/lib/browser/terminal-manager-frontend-view-contribution.d.ts +6 -1
  6. package/lib/browser/terminal-manager-frontend-view-contribution.d.ts.map +1 -1
  7. package/lib/browser/terminal-manager-frontend-view-contribution.js +42 -17
  8. package/lib/browser/terminal-manager-frontend-view-contribution.js.map +1 -1
  9. package/lib/browser/terminal-manager-tree-model.d.ts +3 -1
  10. package/lib/browser/terminal-manager-tree-model.d.ts.map +1 -1
  11. package/lib/browser/terminal-manager-tree-model.js +45 -34
  12. package/lib/browser/terminal-manager-tree-model.js.map +1 -1
  13. package/lib/browser/terminal-manager-tree-widget.d.ts +0 -1
  14. package/lib/browser/terminal-manager-tree-widget.d.ts.map +1 -1
  15. package/lib/browser/terminal-manager-tree-widget.js +6 -6
  16. package/lib/browser/terminal-manager-tree-widget.js.map +1 -1
  17. package/lib/browser/terminal-manager-types.d.ts +6 -5
  18. package/lib/browser/terminal-manager-types.d.ts.map +1 -1
  19. package/lib/browser/terminal-manager-types.js.map +1 -1
  20. package/lib/browser/terminal-manager-widget.d.ts +13 -1
  21. package/lib/browser/terminal-manager-widget.d.ts.map +1 -1
  22. package/lib/browser/terminal-manager-widget.js +107 -71
  23. package/lib/browser/terminal-manager-widget.js.map +1 -1
  24. package/package.json +5 -5
  25. package/src/browser/terminal-manager-frontend-contribution.ts +51 -2
  26. package/src/browser/terminal-manager-frontend-view-contribution.ts +40 -6
  27. package/src/browser/terminal-manager-tree-model.ts +35 -31
  28. package/src/browser/terminal-manager-types.ts +2 -0
  29. package/src/browser/terminal-manager-widget.ts +90 -44
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { injectable } from '@theia/core/shared/inversify';
17
+ import { injectable, inject } from '@theia/core/shared/inversify';
18
18
  import {
19
19
  AbstractViewContribution,
20
20
  codicon,
@@ -23,18 +23,24 @@ import {
23
23
  MAXIMIZED_CLASS,
24
24
  Widget,
25
25
  } from '@theia/core/lib/browser';
26
- import { CommandRegistry, Event, MenuModelRegistry, nls } from '@theia/core';
26
+ import { CommandRegistry, Disposable, Event, MenuModelRegistry, nls } from '@theia/core';
27
27
  import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
28
28
  import { BOTTOM_AREA_ID } from '@theia/core/lib/browser/shell/theia-dock-panel';
29
29
  import { TerminalManagerCommands, TerminalManagerTreeTypes, TERMINAL_MANAGER_TREE_CONTEXT_MENU } from './terminal-manager-types';
30
30
  import { TerminalManagerWidget } from './terminal-manager-widget';
31
31
  import { TerminalManagerTreeWidget } from './terminal-manager-tree-widget';
32
32
  import { ConfirmDialog, Dialog } from '@theia/core/lib/browser/dialogs';
33
+ import { TerminalManagerPreferences } from './terminal-manager-preferences';
33
34
 
34
35
  @injectable()
35
36
  export class TerminalManagerFrontendViewContribution extends AbstractViewContribution<TerminalManagerWidget>
36
37
  implements TabBarToolbarContribution, KeybindingContribution {
37
38
 
39
+ @inject(TerminalManagerPreferences)
40
+ protected readonly preferences: TerminalManagerPreferences;
41
+
42
+ protected quickViewDisposable: Disposable | undefined;
43
+
38
44
  constructor() {
39
45
  super({
40
46
  widgetId: TerminalManagerWidget.ID,
@@ -46,7 +52,17 @@ export class TerminalManagerFrontendViewContribution extends AbstractViewContrib
46
52
  }
47
53
 
48
54
  override registerCommands(commands: CommandRegistry): void {
49
- super.registerCommands(commands);
55
+ // Don't call super.registerCommands() - we manage quick view registration manually
56
+ // based on the terminal.grouping.mode preference
57
+
58
+ this.preferences.ready.then(() => {
59
+ this.updateQuickViewRegistration();
60
+ this.preferences.onPreferenceChanged(change => {
61
+ if (change.preferenceName === 'terminal.grouping.mode') {
62
+ this.updateQuickViewRegistration();
63
+ }
64
+ });
65
+ });
50
66
 
51
67
  commands.registerCommand(TerminalManagerCommands.MANAGER_NEW_TERMINAL_GROUP, {
52
68
  execute: (
@@ -152,15 +168,15 @@ export class TerminalManagerFrontendViewContribution extends AbstractViewContrib
152
168
  primaryButtonText: PRIMARY_BUTTON,
153
169
  });
154
170
  if (dialogResponse === PRIMARY_BUTTON) {
155
- for (const id of widget.pagePanels.keys()) {
156
- widget.deletePage(id);
157
- }
171
+ widget.resetView();
158
172
  }
159
173
  }
160
174
  },
161
175
  });
162
176
  commands.registerCommand(TerminalManagerCommands.MANAGER_OPEN_VIEW, {
163
177
  execute: () => this.openView({ activate: true }),
178
+ isEnabled: () => this.isTreeMode(),
179
+ isVisible: () => this.isTreeMode(),
164
180
  });
165
181
  commands.registerCommand(TerminalManagerCommands.MANAGER_CLOSE_VIEW, {
166
182
  isVisible: () => Boolean(this.tryGetWidget()),
@@ -169,6 +185,24 @@ export class TerminalManagerFrontendViewContribution extends AbstractViewContrib
169
185
  });
170
186
  }
171
187
 
188
+ protected isTreeMode(): boolean {
189
+ return this.preferences.get('terminal.grouping.mode') === 'tree';
190
+ }
191
+
192
+ protected updateQuickViewRegistration(): void {
193
+ if (this.isTreeMode()) {
194
+ if (!this.quickViewDisposable) {
195
+ this.quickViewDisposable = this.quickView?.registerItem({
196
+ label: this.viewLabel,
197
+ open: () => this.openView({ activate: true })
198
+ });
199
+ }
200
+ } else {
201
+ this.quickViewDisposable?.dispose();
202
+ this.quickViewDisposable = undefined;
203
+ }
204
+ }
205
+
172
206
  protected async confirmUserAction(options: { title: string, message: string, primaryButtonText: string }): Promise<string | undefined> {
173
207
  const dialog = new ConfirmDialog({
174
208
  title: options.title,
@@ -15,7 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable, postConstruct } from '@theia/core/shared/inversify';
18
- import { TreeModelImpl, CompositeTreeNode, SelectableTreeNode, DepthFirstTreeIterator } from '@theia/core/lib/browser';
18
+ import { TreeModelImpl, CompositeTreeNode, SelectableTreeNode, DepthFirstTreeIterator, TreeNode } from '@theia/core/lib/browser';
19
19
  import { Emitter, nls } from '@theia/core';
20
20
  import { TerminalManagerTreeTypes } from './terminal-manager-types';
21
21
 
@@ -112,14 +112,10 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
112
112
  deleteTerminalPage(pageId: TerminalManagerTreeTypes.PageId): void {
113
113
  const pageNode = this.getNode(pageId);
114
114
  if (TerminalManagerTreeTypes.isPageNode(pageNode) && CompositeTreeNode.is(this.root)) {
115
- while (pageNode.children.length > 0) {
116
- const groupNode = pageNode.children[pageNode.children.length - 1];
117
- this.doDeleteTerminalGroup(groupNode, pageNode);
118
- }
115
+ const isActive = this.activePageNode === pageNode;
119
116
  this.onDidDeletePageEmitter.fire(pageNode.id);
120
117
  CompositeTreeNode.removeChild(this.root, pageNode);
121
- setTimeout(() => this.selectPrevNode());
122
- this.refresh();
118
+ this.refreshWithSelection(this.root, undefined, isActive ? pageNode : undefined);
123
119
  }
124
120
  }
125
121
 
@@ -135,10 +131,7 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
135
131
  this.onDidAddTerminalGroupEmitter.fire({ groupId: groupNode.id, pageId, terminalKey });
136
132
  CompositeTreeNode.addChild(groupNode, terminalNode);
137
133
  CompositeTreeNode.addChild(pageNode, groupNode);
138
- this.refresh();
139
- setTimeout(() => {
140
- this.selectionService.addSelection(terminalNode);
141
- });
134
+ this.refreshWithSelection(pageNode, terminalNode);
142
135
  }
143
136
  }
144
137
 
@@ -176,17 +169,14 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
176
169
  if (parentPageNode.children.length === 1) {
177
170
  this.deleteTerminalPage(parentPageNode.id);
178
171
  } else {
172
+ const isActive = this.activeGroupNode === groupNode;
179
173
  this.doDeleteTerminalGroup(groupNode, parentPageNode);
180
- this.refresh();
174
+ this.refreshWithSelection(parentPageNode, undefined, isActive ? groupNode : undefined);
181
175
  }
182
176
  }
183
177
  }
184
178
 
185
179
  protected doDeleteTerminalGroup(group: TerminalManagerTreeTypes.TerminalGroupNode, page: TerminalManagerTreeTypes.PageNode): void {
186
- while (group.children.length > 0) {
187
- const terminalNode = group.children[group.children.length - 1];
188
- this.doDeleteTerminalNode(terminalNode, group);
189
- }
190
180
  this.onDidDeleteTerminalGroupEmitter.fire(group.id);
191
181
  CompositeTreeNode.removeChild(page, group);
192
182
  }
@@ -197,12 +187,7 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
197
187
  const terminalNode = this.createTerminalNode(newTerminalId, groupId);
198
188
  CompositeTreeNode.addChild(groupNode, terminalNode);
199
189
  this.onDidAddTerminalToGroupEmitter.fire({ terminalId: newTerminalId, groupId });
200
- this.refresh();
201
- setTimeout(() => {
202
- if (SelectableTreeNode.is(terminalNode)) {
203
- this.selectionService.addSelection(terminalNode);
204
- }
205
- });
190
+ this.refreshWithSelection(undefined, terminalNode);
206
191
  }
207
192
  }
208
193
 
@@ -229,20 +214,19 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
229
214
  if (parentGroupNode.children.length === 1) {
230
215
  this.deleteTerminalGroup(parentGroupNode.id);
231
216
  } else {
217
+ const isActive = this.activeTerminalNode === terminalNode;
232
218
  this.doDeleteTerminalNode(terminalNode, parentGroupNode);
233
- this.refresh();
219
+ this.refreshWithSelection(parentGroupNode, undefined, isActive ? terminalNode : undefined);
234
220
  }
235
221
  }
236
222
  }
237
223
 
238
224
  protected doDeleteTerminalNode(node: TerminalManagerTreeTypes.TerminalNode, parent: TerminalManagerTreeTypes.TerminalGroupNode): void {
239
- if (TerminalManagerTreeTypes.isGroupNode(parent)) {
240
- this.onDidDeleteTerminalFromGroupEmitter.fire({
241
- terminalId: node.id,
242
- groupId: parent.id,
243
- });
244
- CompositeTreeNode.removeChild(parent, node);
245
- }
225
+ this.onDidDeleteTerminalFromGroupEmitter.fire({
226
+ terminalId: node.id,
227
+ groupId: parent.id,
228
+ });
229
+ CompositeTreeNode.removeChild(parent, node);
246
230
  }
247
231
 
248
232
  toggleRenameTerminal(entityId: TerminalManagerTreeTypes.TerminalManagerValidId): void {
@@ -268,7 +252,6 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
268
252
  let activeTerminal: TerminalManagerTreeTypes.TerminalNode | undefined = undefined;
269
253
  let activeGroup: TerminalManagerTreeTypes.TerminalGroupNode | undefined = undefined;
270
254
  let activePage: TerminalManagerTreeTypes.PageNode | undefined = undefined;
271
-
272
255
  if (TerminalManagerTreeTypes.isTerminalNode(selectedNode)) {
273
256
  activeTerminal = selectedNode;
274
257
  const { parent } = activeTerminal;
@@ -333,4 +316,25 @@ export class TerminalManagerTreeModel extends TreeModelImpl {
333
316
  this.selectNode(node);
334
317
  }
335
318
  }
319
+
320
+ protected async refreshWithSelection(refreshTarget?: CompositeTreeNode, selectionTarget?: SelectableTreeNode, selectionReferent?: TreeNode): Promise<void> {
321
+ await this.refresh(refreshTarget);
322
+ if (selectionTarget) {
323
+ return this.selectNode(selectionTarget);
324
+ }
325
+ if (selectionReferent) {
326
+ const { previousSibling, nextSibling } = selectionReferent;
327
+ const toSelect = this.findSelection(previousSibling) ?? this.findSelection(nextSibling);
328
+ if (toSelect) {
329
+ this.selectNode(toSelect);
330
+ }
331
+ }
332
+ }
333
+
334
+ protected findSelection(start?: TreeNode): SelectableTreeNode | undefined {
335
+ if (!start) { return undefined; }
336
+ if (TerminalManagerTreeTypes.isTerminalNode(start)) { return start; }
337
+ if (TerminalManagerTreeTypes.isGroupNode(start)) { return start.children.at(0); }
338
+ if (TerminalManagerTreeTypes.isPageNode(start)) { return start.children.at(0)?.children.at(0); }
339
+ }
336
340
  }
@@ -122,6 +122,7 @@ export namespace TerminalManagerTreeTypes {
122
122
  export const isGroupId = (obj: unknown): obj is GroupId => typeof obj === 'string' && obj.startsWith('group-');
123
123
  export interface GroupSplitPanel extends SplitPanel {
124
124
  id: GroupId;
125
+ widgets: readonly TerminalWidgetImpl[];
125
126
  }
126
127
  export interface TerminalGroupNode extends SelectableTreeNode, ExpandableTreeNode {
127
128
  terminalGroup: true;
@@ -137,6 +138,7 @@ export namespace TerminalManagerTreeTypes {
137
138
  export const isPageId = (obj: unknown): obj is PageId => typeof obj === 'string' && obj.startsWith('page-');
138
139
  export interface PageSplitPanel extends SplitPanel {
139
140
  id: PageId;
141
+ widgets: readonly GroupSplitPanel[];
140
142
  }
141
143
  export interface PageNode extends SelectableTreeNode, ExpandableTreeNode {
142
144
  page: true;
@@ -83,13 +83,15 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
83
83
  static LABEL = nls.localize('theia/terminal-manager/label', 'Terminals');
84
84
 
85
85
  protected panel: SplitPanel;
86
-
87
86
  protected pageAndTreeLayout: SplitLayout | undefined;
88
87
  protected stateIsSet = false;
89
88
 
90
89
  pagePanels = new Map<TerminalManagerTreeTypes.PageId, TerminalManagerTreeTypes.PageSplitPanel>();
91
90
  groupPanels = new Map<TerminalManagerTreeTypes.GroupId, TerminalManagerTreeTypes.GroupSplitPanel>();
91
+ /** By node ID: safer for state restoration. */
92
92
  terminalWidgets = new Map<TerminalManagerTreeTypes.TerminalKey, TerminalWidget>();
93
+ /** By terminal ID to work from widget to internal metadata. */
94
+ terminalWidgetIdsToNodeIds = new Map<string, TerminalManagerTreeTypes.TerminalKey>();
93
95
 
94
96
  protected readonly onDidChangeTrackableWidgetsEmitter = new Emitter<Widget[]>();
95
97
  readonly onDidChangeTrackableWidgets = this.onDidChangeTrackableWidgetsEmitter.event;
@@ -133,13 +135,23 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
133
135
  this.id = TerminalManagerWidget.ID;
134
136
  this.title.closable = true;
135
137
  this.title.label = TerminalManagerWidget.LABEL;
138
+ this.title.caption = TerminalManagerWidget.LABEL;
136
139
  this.node.tabIndex = 0;
137
140
  this.registerListeners();
138
141
  this.createPageAndTreeLayout();
139
142
  }
140
143
 
144
+ /** Yields all terminal widgets owned by this widget and then closes this widget. */
145
+ *drainWidgets(): IterableIterator<TerminalWidget> {
146
+ for (const [key, widget] of this.terminalWidgets) {
147
+ this.removeTerminalReferenceByNodeId(key);
148
+ yield widget;
149
+ }
150
+ this.close();
151
+ }
152
+
141
153
  async populateLayout(force?: boolean): Promise<void> {
142
- if (!this.stateIsSet || force) {
154
+ if ((!this.stateIsSet && this.terminalWidgets.size === 0) || force) {
143
155
  const terminalWidget = await this.createTerminalWidget();
144
156
  this.addTerminalPage(terminalWidget);
145
157
  this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
@@ -243,7 +255,7 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
243
255
  }
244
256
 
245
257
  protected override onCloseRequest(msg: Message): void {
246
- if (this.interceptCloseRequest) {
258
+ if (this.interceptCloseRequest && this.terminalWidgets.size > 0) {
247
259
  this.interceptCloseRequest = false;
248
260
  this.confirmClose()
249
261
  .then(confirmed => {
@@ -275,19 +287,42 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
275
287
  }
276
288
 
277
289
  addTerminalPage(widget: Widget): void {
290
+ this.doAddTerminalPage(widget);
291
+ }
292
+
293
+ protected doAddTerminalPage(widget: Widget): TerminalManagerTreeTypes.PageSplitPanel | undefined {
278
294
  if (widget instanceof TerminalWidgetImpl) {
279
295
  const terminalKey = TerminalManagerTreeTypes.generateTerminalKey(widget);
280
- this.registerTerminalCloseListener(widget, terminalKey);
281
- this.terminalWidgets.set(terminalKey, widget);
296
+ this.addTerminalReference(widget, terminalKey);
282
297
  this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
283
298
  const groupPanel = this.createTerminalGroupPanel();
284
299
  groupPanel.addWidget(widget);
285
300
  const pagePanel = this.createPagePanel();
286
301
  pagePanel.addWidget(groupPanel);
287
302
  this.treeWidget.model.addTerminalPage(terminalKey, groupPanel.id, pagePanel.id);
303
+ return pagePanel;
288
304
  }
289
305
  }
290
306
 
307
+ protected addTerminalReference(widget: TerminalWidget, nodeId: TerminalManagerTreeTypes.TerminalKey): void {
308
+ this.terminalWidgets.set(nodeId, widget);
309
+ this.terminalWidgetIdsToNodeIds.set(widget.id, nodeId);
310
+ }
311
+
312
+ protected removeTerminalReferenceByWidgetId(widgetId: string): boolean {
313
+ const nodeId = this.terminalWidgetIdsToNodeIds.get(widgetId);
314
+ if (nodeId === undefined) {return false; }
315
+ return this.terminalWidgets.delete(nodeId);
316
+ }
317
+
318
+ protected removeTerminalReferenceByNodeId(nodeId: TerminalManagerTreeTypes.TerminalKey): boolean {
319
+ const widget = this.terminalWidgets.get(nodeId);
320
+ if (!widget) {return false; }
321
+ this.terminalWidgets.delete(nodeId);
322
+ this.terminalWidgetIdsToNodeIds.delete(widget.id);
323
+ return true;
324
+ }
325
+
291
326
  protected createPagePanel(pageId?: TerminalManagerTreeTypes.PageId): TerminalManagerTreeTypes.PageSplitPanel {
292
327
  const newPageLayout = new ViewContainerLayout({
293
328
  renderer: SplitPanel.defaultRenderer,
@@ -332,10 +367,25 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
332
367
  }
333
368
 
334
369
  protected handlePageDeleted(pagePanelId: TerminalManagerTreeTypes.PageId): void {
335
- this.pagePanels.get(pagePanelId)?.dispose();
370
+ const panel = this.pagePanels.get(pagePanelId);
371
+ if (!panel) {
372
+ return;
373
+ }
374
+ const isLastPanel = this.pagePanels.size === 1;
375
+ if (isLastPanel) {
376
+ this.interceptCloseRequest = false;
377
+ this.close();
378
+ return;
379
+ }
380
+ this.clearGroupReferences(panel);
381
+ panel.dispose();
336
382
  this.pagePanels.delete(pagePanelId);
337
- if (this.pagePanels.size === 0) {
338
- this.populateLayout(true);
383
+ }
384
+
385
+ protected clearGroupReferences(panel: TerminalManagerTreeTypes.PageSplitPanel): void {
386
+ for (const group of panel.widgets) {
387
+ this.clearTerminalReferences(group);
388
+ this.groupPanels.delete(group.id);
339
389
  }
340
390
  }
341
391
 
@@ -345,8 +395,7 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
345
395
  }
346
396
  if (widget instanceof TerminalWidgetImpl) {
347
397
  const terminalId = TerminalManagerTreeTypes.generateTerminalKey(widget);
348
- this.registerTerminalCloseListener(widget, terminalId);
349
- this.terminalWidgets.set(terminalId, widget);
398
+ this.addTerminalReference(widget, terminalId);
350
399
  this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
351
400
  const groupPanel = this.createTerminalGroupPanel();
352
401
  groupPanel.addWidget(widget);
@@ -403,22 +452,28 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
403
452
 
404
453
  activateWidget(id: string): Widget | undefined {
405
454
  const widget = Array.from(this.terminalWidgets.values()).find(terminalWidget => terminalWidget.id === id);
406
- if (widget instanceof TerminalWidgetImpl) {
407
- widget.activate();
408
- }
455
+ widget?.activate();
409
456
  return widget;
410
457
  }
411
458
 
412
459
  protected handleTerminalGroupDeleted(groupPanelId: TerminalManagerTreeTypes.GroupId): void {
413
- this.groupPanels.get(groupPanelId)?.dispose();
460
+ const panel = this.groupPanels.get(groupPanelId);
414
461
  this.groupPanels.delete(groupPanelId);
462
+ if (!panel) { return; }
463
+ this.clearTerminalReferences(panel);
464
+ panel.dispose();
465
+ }
466
+
467
+ protected clearTerminalReferences(panel: TerminalManagerTreeTypes.GroupSplitPanel): void {
468
+ for (const terminal of panel.widgets) {
469
+ this.removeTerminalReferenceByWidgetId(terminal.id);
470
+ }
415
471
  }
416
472
 
417
473
  addWidgetToTerminalGroup(widget: Widget, groupId: TerminalManagerTreeTypes.GroupId): void {
418
474
  if (widget instanceof TerminalWidgetImpl) {
419
475
  const newTerminalId = TerminalManagerTreeTypes.generateTerminalKey(widget);
420
- this.registerTerminalCloseListener(widget, newTerminalId);
421
- this.terminalWidgets.set(newTerminalId, widget);
476
+ this.addTerminalReference(widget, newTerminalId);
422
477
  this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
423
478
  this.treeWidget.model.addTerminal(newTerminalId, groupId);
424
479
  }
@@ -449,14 +504,10 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
449
504
 
450
505
  protected handleTerminalDeleted(terminalId: TerminalManagerTreeTypes.TerminalKey): void {
451
506
  const terminalWidget = this.terminalWidgets.get(terminalId);
452
- const wasActiveWidget = this.shell.activeWidget === terminalWidget;
453
- if (!this.terminalsDeletingFromClose.has(terminalId)) {
507
+ if (!terminalWidget?.isDisposed) {
454
508
  terminalWidget?.dispose();
455
509
  }
456
- this.terminalWidgets.delete(terminalId);
457
- if (wasActiveWidget) {
458
- this.activateNextAvailableTerminal(terminalId);
459
- }
510
+ this.removeTerminalReferenceByNodeId(terminalId);
460
511
  }
461
512
 
462
513
  protected handleOnDidChangeActiveWidget(widget: Widget | null): void {
@@ -535,6 +586,16 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
535
586
  this.terminalWidgets = new Map();
536
587
  }
537
588
 
589
+ async resetView(): Promise<void> {
590
+ const terminalWidget = await this.createTerminalWidget();
591
+ const page = this.doAddTerminalPage(terminalWidget);
592
+ for (const id of this.pagePanels.keys()) {
593
+ if (id !== page?.id) {
594
+ this.deletePage(id);
595
+ }
596
+ }
597
+ }
598
+
538
599
  protected iterateAndRestoreLayoutTree(pageLayouts: TerminalManagerWidgetState.PageLayoutData[], treeWidget: TerminalManagerTreeWidget): void {
539
600
  for (const pageLayout of pageLayouts) {
540
601
  const pageId = pageLayout.id;
@@ -565,8 +626,7 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
565
626
  if (!TerminalManagerTreeTypes.isTerminalNode(widgetNode)) {
566
627
  throw TerminalManagerWidget.createRestoreError(widgetId);
567
628
  }
568
- this.terminalWidgets.set(widgetId, widget);
569
- this.registerTerminalCloseListener(widget, widgetId);
629
+ this.addTerminalReference(widget, widgetId);
570
630
  this.onDidChangeTrackableWidgetsEmitter.fire(this.getTrackableWidgets());
571
631
  groupPanel.addWidget(widget);
572
632
  }
@@ -646,26 +706,6 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
646
706
  return fullLayoutData;
647
707
  }
648
708
 
649
- protected registerTerminalCloseListener(widget: TerminalWidget, terminalKey: TerminalManagerTreeTypes.TerminalKey): void {
650
- const originalOnCloseRequest = widget['onCloseRequest'].bind(widget);
651
- widget['onCloseRequest'] = (msg: Message) => {
652
- const wasActiveWidget = this.shell.activeWidget === widget;
653
- if (wasActiveWidget) {
654
- this.activateNextAvailableTerminal(terminalKey);
655
- }
656
- originalOnCloseRequest(msg);
657
- };
658
- const disposable = widget.onTerminalDidClose(() => {
659
- this.terminalsDeletingFromClose.add(terminalKey);
660
- try {
661
- this.treeWidget.model.deleteTerminalNode(terminalKey);
662
- } finally {
663
- this.terminalsDeletingFromClose.delete(terminalKey);
664
- }
665
- });
666
- this.toDispose.push(disposable);
667
- }
668
-
669
709
  protected activateNextAvailableTerminal(excludeTerminalKey: TerminalManagerTreeTypes.TerminalKey): void {
670
710
  const remainingTerminals = Array.from(this.terminalWidgets.entries()).filter(([key]) => key !== excludeTerminalKey);
671
711
  if (remainingTerminals.length > 0) {
@@ -683,4 +723,10 @@ export class TerminalManagerWidget extends BaseWidget implements StatefulWidget,
683
723
  this.shell.activateWidget(this.id);
684
724
  }
685
725
  }
726
+
727
+ override dispose(): void {
728
+ this.toDispose.dispose();
729
+ super.dispose();
730
+ this.terminalWidgets.clear();
731
+ }
686
732
  }