From 3edb33fc0c3c0c79c54576c589cf81fb8bc9d64d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Sun, 4 Apr 2021 20:32:49 -0700 Subject: [PATCH 01/49] push terminalTabsWidget --- .../terminal/browser/terminalTabsWidget.ts | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts new file mode 100644 index 0000000000000..506208695797e --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; +import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { ITreeElement, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { localize } from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; + +class TerminalTabsDelegate implements IListVirtualDelegate { + getHeight(element: any): number { + return 24; + } + getTemplateId(element: any): string { + return 'terminal.tabs'; + } +} +class TerminalTabsIdentityProvider implements IIdentityProvider { + constructor() { + } + getId(element: TerminalTab): { toString(): string; } { + // to do - fix this won't work + return element ? element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; + } + +} +class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(tab: TerminalTab) { + return tab ? tab?.terminalInstances.length > 1 ? `Terminals (${tab?.terminalInstances.length})` : tab?.terminalInstances[0].title : ''; + } + + getWidgetAriaLabel() { + return localize('terminalTabs', "TerminalTabs"); + } +} +export class TerminalTabsWidget extends WorkbenchObjectTree { + constructor( + container: HTMLElement, + renderers: ITreeRenderer[], + @IContextKeyService contextKeyService: IContextKeyService, + @IListService listService: IListService, + @IThemeService themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IKeybindingService keybindingService: IKeybindingService, + @IAccessibilityService accessibilityService: IAccessibilityService, + @ITerminalService terminalService: ITerminalService + ) { + super('TerminalTabsTree', container, + new TerminalTabsDelegate(), + renderers, + { + horizontalScrolling: false, + supportDynamicHeights: true, + identityProvider: new TerminalTabsIdentityProvider(), + accessibilityProvider: new TerminalTabsAccessibilityProvider(), + styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), + filter: undefined, + smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), + multipleSelectionSupport: false, + }, + contextKeyService, + listService, + themeService, + configurationService, + keybindingService, + accessibilityService, + ); + this.setChildren(null, createTerminalTabsIterator(terminalService.terminalTabs)); + } +} + +function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { + return tabs.map(tab => { + const hasChildren = tab.terminalInstances.length > 1; + return { + element: tab, + collapsed: true, + collapsible: hasChildren, + // children: hasChildren ? tab.terminalInstances : undefined // TODO + children: undefined + }; + }); +} From 027f62931073d7870b4f5bd16d730ad00102778f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Sun, 4 Apr 2021 20:47:46 -0700 Subject: [PATCH 02/49] add to terminal view --- .../terminal/browser/terminalTabsWidget.ts | 35 +++++++++++++++++-- .../contrib/terminal/browser/terminalView.ts | 7 +++- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 506208695797e..5ecd829181afa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -5,7 +5,7 @@ import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; -import { ITreeElement, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -17,6 +17,9 @@ import { ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/br import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; + +const $ = DOM.$; + class TerminalTabsDelegate implements IListVirtualDelegate { getHeight(element: any): number { return 24; @@ -46,7 +49,6 @@ class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { constructor( container: HTMLElement, - renderers: ITreeRenderer[], @IContextKeyService contextKeyService: IContextKeyService, @IListService listService: IListService, @IThemeService themeService: IThemeService, @@ -57,7 +59,7 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { ) { super('TerminalTabsTree', container, new TerminalTabsDelegate(), - renderers, + [new TerminalTabsRenderer()], { horizontalScrolling: false, supportDynamicHeights: true, @@ -91,3 +93,30 @@ function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable { + + templateId = 'terminal.tab'; + + renderTemplate(container: HTMLElement): ITerminalTabEntryTemplate { + return { + labelElement: DOM.append(container, $('.terminal-tabs-entry')), + }; + } + + renderElement(node: ITreeNode, index: number, template: ITerminalTabEntryTemplate): void { + const element = node.element; + const label = element ? element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; + + template.labelElement.textContent = label; + template.labelElement.title = label; + } + + disposeTemplate(templateData: ITerminalTabEntryTemplate): void { + } +} + +interface ITerminalTabEntryTemplate { + labelElement: HTMLElement; +} + diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 5015ca7a63e70..6a5e6ac7a585e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -36,6 +36,7 @@ import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -47,6 +48,7 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; + private _tabsWidget: TerminalTabsWidget | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -102,11 +104,14 @@ export class TerminalViewPane extends ViewPane { this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - + this._tabsWidget = this.instantiationService.createInstance(TerminalTabsWidget, this._parentDomElement); this._parentDomElement.appendChild(this._fontStyleElement); this._parentDomElement.appendChild(this._terminalContainer); this._parentDomElement.appendChild(this._findWidget.getDomNode()); + + + this._attachEventListeners(this._parentDomElement, this._terminalContainer); this._terminalService.setContainers(container, this._terminalContainer); From 76acf1ef644c5a26b797a4e9352c934e9ce995c2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Sun, 4 Apr 2021 21:57:48 -0700 Subject: [PATCH 03/49] working pretty well --- src/vs/base/browser/ui/list/listView.ts | 2 +- .../terminal/browser/media/terminal.css | 10 +++++ .../terminal/browser/terminalService.ts | 7 ++++ .../terminal/browser/terminalTabsWidget.ts | 10 +++-- .../contrib/terminal/browser/terminalView.ts | 40 ++++++++++++++----- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index ee8fd156e23aa..90c5fbfbdc1f7 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -756,7 +756,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer.appendChild(item.row.domNode); } } - + console.log('renderering', item, index); this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 21e23a00a3918..6d7cf97e6fa53 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -3,6 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +.tabs-widget-container { + width: 20%; + float: left; + height: 100%; +} + .monaco-workbench .pane-body.integrated-terminal { align-content: flex-start; align-items: baseline; @@ -19,10 +26,13 @@ width: 100%; box-sizing: border-box; overflow: hidden; + float:right; } .monaco-workbench .pane-body.integrated-terminal .terminal-tab { height: 100%; + width: 80%; + float: right; } .monaco-workbench .pane-body.integrated-terminal .terminal-wrapper { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 6e6c2f4ac92f1..1d9e3c75baedf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -159,6 +159,13 @@ export class TerminalService implements ITerminalService { } }); + this.onInstancesChanged(() => { + const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; + if (pane) { + pane.renderBody(); + } + }); + const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; const conn = this._remoteAgentService.getConnection(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 5ecd829181afa..3c34c6efe1c3a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -43,7 +43,7 @@ class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { @@ -77,12 +77,13 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { keybindingService, accessibilityService, ); + this.setChildren(null, undefined); this.setChildren(null, createTerminalTabsIterator(terminalService.terminalTabs)); } } function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { - return tabs.map(tab => { + const result = tabs.map(tab => { const hasChildren = tab.terminalInstances.length > 1; return { element: tab, @@ -92,11 +93,12 @@ function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable { - templateId = 'terminal.tab'; + templateId = 'terminal.tabs'; renderTemplate(container: HTMLElement): ITerminalTabEntryTemplate { return { @@ -106,7 +108,7 @@ class TerminalTabsRenderer implements ITreeRenderer, index: number, template: ITerminalTabEntryTemplate): void { const element = node.element; - const label = element ? element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; + const label = element ? element.terminalInstances.length === 0 ? 'Starting...' : element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; template.labelElement.textContent = label; template.labelElement.title = label; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 6a5e6ac7a585e..e2dbe23d74cc9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -48,7 +48,7 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; - private _tabsWidget: TerminalTabsWidget | undefined; + private _tabsWidgetContainer: HTMLElement | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -91,27 +91,45 @@ export class TerminalViewPane extends ViewPane { }); } - protected renderBody(container: HTMLElement): void { + public renderBody(container?: HTMLElement): void { + if (!container && !this._parentDomElement) { + return; + } else if (!container) { + container = this._parentDomElement; + } + if (!container) { + return; + } super.renderBody(container); this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); this._fontStyleElement = document.createElement('style'); - this._terminalContainer = document.createElement('div'); - this._terminalContainer.classList.add('terminal-outer-container'); - this._terminalContainer.style.display = this.shouldShowWelcome() ? 'none' : 'block'; + if (!this._terminalContainer) { + this._terminalContainer = document.createElement('div'); + this._terminalContainer.classList.add('terminal-outer-container'); + this._terminalContainer.style.display = this.shouldShowWelcome() ? 'none' : 'block'; + } + if (!this._findWidget) { + this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); + this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); + } + if (this._tabsWidgetContainer) { + this._terminalContainer.removeChild(this._tabsWidgetContainer); + } + this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); + dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { + 'role': 'navigation', + 'aria-label': nls.localize('terminal-tabs-widget', "Terminal Tabs"), + })); + + this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); - this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); - this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - this._tabsWidget = this.instantiationService.createInstance(TerminalTabsWidget, this._parentDomElement); this._parentDomElement.appendChild(this._fontStyleElement); this._parentDomElement.appendChild(this._terminalContainer); this._parentDomElement.appendChild(this._findWidget.getDomNode()); - - - this._attachEventListeners(this._parentDomElement, this._terminalContainer); this._terminalService.setContainers(container, this._terminalContainer); From 218e702bbb447618c5591c525f5824b64b280757 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 08:56:48 -0700 Subject: [PATCH 04/49] children now work --- .../terminal/browser/terminalTabsWidget.ts | 89 +++++++++++++++---- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 3c34c6efe1c3a..40f9dddb28584 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -13,7 +13,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; @@ -28,25 +28,32 @@ class TerminalTabsDelegate implements IListVirtualDelegate { return 'terminal.tabs'; } } -class TerminalTabsIdentityProvider implements IIdentityProvider { +class TerminalTabsIdentityProvider implements IIdentityProvider { constructor() { } - getId(element: TerminalTab): { toString(): string; } { - // to do - fix this won't work - return element ? element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; + getId(element: TabTreeNode): { toString(): string; } { + if ('children' in element) { + return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; + } else { + return element.instance.title; + } } } -class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { - getAriaLabel(tab: TerminalTab) { - return tab ? tab?.terminalInstances.length > 1 ? `Terminals (${tab?.terminalInstances.length})` : tab?.terminalInstances[0].title : ''; +class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(element: TabTreeNode) { + if ('children' in element) { + return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; + } else { + return element.instance.title; + } } getWidgetAriaLabel() { return localize('terminal.tabs', "TerminalTabs"); } } -export class TerminalTabsWidget extends WorkbenchObjectTree { +export class TerminalTabsWidget extends WorkbenchObjectTree { constructor( container: HTMLElement, @IContextKeyService contextKeyService: IContextKeyService, @@ -82,21 +89,27 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } -function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { +function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { const result = tabs.map(tab => { const hasChildren = tab.terminalInstances.length > 1; + const elt = new TabTreeElement(tab); return { - element: tab, + element: elt, collapsed: true, collapsible: hasChildren, - // children: hasChildren ? tab.terminalInstances : undefined // TODO - children: undefined + children: elt.children.map(child => { + return { + element: child, + collapsed: true, + collapsible: false + }; + }) }; }); return result; } -class TerminalTabsRenderer implements ITreeRenderer { +class TerminalTabsRenderer implements ITreeRenderer { templateId = 'terminal.tabs'; @@ -106,10 +119,14 @@ class TerminalTabsRenderer implements ITreeRenderer, index: number, template: ITerminalTabEntryTemplate): void { - const element = node.element; - const label = element ? element.terminalInstances.length === 0 ? 'Starting...' : element?.terminalInstances.length > 1 ? `Terminals (${element?.terminalInstances.length})` : element?.terminalInstances[0].title : ''; - + renderElement(node: ITreeNode, index: number, template: ITerminalTabEntryTemplate): void { + let label = ''; + let item = node.element; + if ('children' in item) { + label = item ? item.children.length === 0 ? 'Starting...' : item?.children.length > 1 ? `Terminals (${item.children.length})` : item.children[0].instance.title : ''; + } else if ('instance' in item) { + label = item.instance.title; + } template.labelElement.textContent = label; template.labelElement.title = label; } @@ -122,3 +139,39 @@ interface ITerminalTabEntryTemplate { labelElement: HTMLElement; } +type TabTreeNode = TabTreeElement | TabTreeChild; + +class TabTreeElement { + private _tab: ITerminalTab; + private _children: TabTreeChild[]; + constructor(tab: ITerminalTab) { + this._tab = tab; + this._children = this._tab.terminalInstances.map(i => new TabTreeChild(i, this._tab)); + } + get tab(): ITerminalTab { + return this._tab; + } + + get children(): TabTreeChild[] { + return this._children; + } + + set children(newChildren: TabTreeChild[]) { + this._children = newChildren; + } +} + +class TabTreeChild { + private _instance: ITerminalInstance; + private _tab: ITerminalTab; + constructor(instance: ITerminalInstance, tab: ITerminalTab) { + this._instance = instance; + this._tab = tab; + } + get instance(): ITerminalInstance { + return this._instance; + } + get parent(): ITerminalTab { + return this._tab; + } +} From 76736a879b3cf83bfc1bf66b0ce9e2313612a622 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 10:25:34 -0700 Subject: [PATCH 05/49] add showTabs setting --- src/vs/base/browser/ui/list/listView.ts | 1 - .../terminal/browser/terminalService.ts | 9 ++++++--- .../contrib/terminal/browser/terminalView.ts | 18 +++++++++++------- .../contrib/terminal/common/terminal.ts | 1 + .../terminal/common/terminalConfiguration.ts | 5 +++++ 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 90c5fbfbdc1f7..863309fe651f8 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -756,7 +756,6 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer.appendChild(item.row.domNode); } } - console.log('renderering', item, index); this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 1d9e3c75baedf..cafb270fa9deb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -156,13 +156,16 @@ export class TerminalService implements ITerminalService { e.affectsConfiguration('terminal.integrated.profiles.linux') || e.affectsConfiguration('terminal.integrated.useWslProfiles')) { this._updateAvailableProfilesNow(); + } else if (e.affectsConfiguration('terminal.integrated.showTabs')) { + const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; + pane?.renderBody(); } }); this.onInstancesChanged(() => { - const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; - if (pane) { - pane.renderBody(); + if (this._configHelper.config.showTabs) { + const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; + pane?.renderBody(); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index e2dbe23d74cc9..44382b15b76ab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -115,16 +115,20 @@ export class TerminalViewPane extends ViewPane { this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); } - if (this._tabsWidgetContainer) { + + if ((!this._terminalService.configHelper.config.showTabs && this._tabsWidgetContainer) || this._tabsWidgetContainer) { this._terminalContainer.removeChild(this._tabsWidgetContainer); + this._tabsWidgetContainer = undefined; } - this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); - dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { - 'role': 'navigation', - 'aria-label': nls.localize('terminal-tabs-widget', "Terminal Tabs"), - })); - this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); + if (this._terminalService.configHelper.config.showTabs) { + this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); + dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { + 'role': 'navigation', + 'aria-label': nls.localize('terminal-tabs-widget', "Terminal Tabs"), + })); + this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); + } this._parentDomElement.appendChild(this._fontStyleElement); this._parentDomElement.appendChild(this._terminalContainer); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index dd85328417a86..e6cbec010f7d7 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -103,6 +103,7 @@ export interface ITerminalConfiguration { }; profiles: ITerminalProfiles; useWslProfiles: boolean; + showTabs: boolean; altClickMovesCursor: boolean; macOptionIsMeta: boolean; macOptionClickForcesSelection: boolean; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 92ca57ec539dc..0886d383a63e8 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -222,6 +222,11 @@ export const terminalConfiguration: IConfigurationNode = { type: 'boolean', default: true }, + 'terminal.integrated.showTabs': { + description: localize('terminal.integrated.showTabs', 'Controls whether or not the terminal tabs widget is shown'), + type: 'boolean', + default: false + }, 'terminal.integrated.macOptionIsMeta': { description: localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), type: 'boolean', From 59c22b335301274f87f70d82cc8102d48d22d9ba Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 11:31:01 -0700 Subject: [PATCH 06/49] more improvements --- .../contrib/terminal/browser/terminalTab.ts | 6 +++- .../terminal/browser/terminalTabsWidget.ts | 32 ++++++++++++++----- .../contrib/terminal/browser/terminalView.ts | 3 ++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 95599ea105f5a..caba3dab11e8f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; const SPLIT_PANE_MIN_SIZE = 120; -class SplitPaneContainer extends Disposable { +export class SplitPaneContainer extends Disposable { private _height: number; private _width: number; private _splitView!: SplitView; @@ -38,6 +38,10 @@ class SplitPaneContainer extends Disposable { this._splitView.layout(this.orientation === Orientation.HORIZONTAL ? this._width : this._height); } + public get splitView(): SplitView { + return this._splitView; + } + private _createSplitView(): void { this._splitView = new SplitView(this._container, { orientation: this.orientation }); this._splitViewDisposables.clear(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 40f9dddb28584..8557fce4726f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -17,7 +17,6 @@ import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; - const $ = DOM.$; class TerminalTabsDelegate implements IListVirtualDelegate { @@ -86,6 +85,16 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { ); this.setChildren(null, undefined); this.setChildren(null, createTerminalTabsIterator(terminalService.terminalTabs)); + + this.onDidChangeSelection(e => { + if (e.elements && e.elements[0]) { + if ('_instance' in e.elements[0]) { + terminalService.setActiveInstance(e.elements[0].instance); + } else { + terminalService.setActiveTabByIndex(terminalService.terminalTabs.indexOf(e.elements[0].tab)); + } + } + }); } } @@ -97,18 +106,25 @@ function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable { - return { - element: child, - collapsed: true, - collapsible: false - }; - }) + children: getChildren(elt) }; }); return result; } +function getChildren(elt: TabTreeElement): Iterable> | undefined { + if (elt.children.length > 1) { + return elt.children.map(child => { + return { + element: child, + collapsed: true, + collapsible: false + }; + }); + } + return undefined; +} + class TerminalTabsRenderer implements ITreeRenderer { templateId = 'terminal.tabs'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 44382b15b76ab..76430202d5784 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -122,6 +122,9 @@ export class TerminalViewPane extends ViewPane { } if (this._terminalService.configHelper.config.showTabs) { + // this._tabsWidgetContainer = this._instantiationService.createInstance(SplitPaneContainer, this._terminalContainer, Orientation.HORIZONTAL).splitView.el; + // this._tabsWidgetContainer.classList.add('tabs-widget-container'); + // this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { 'role': 'navigation', From 92ff8a92093f311bd105524b1f7f227f2cc25aea Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 11:54:21 -0700 Subject: [PATCH 07/49] rearrange code --- .../terminal/browser/terminalTabsWidget.ts | 118 +++++++++--------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 8557fce4726f1..bd9adf07eec9c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -19,39 +19,6 @@ import * as DOM from 'vs/base/browser/dom'; const $ = DOM.$; -class TerminalTabsDelegate implements IListVirtualDelegate { - getHeight(element: any): number { - return 24; - } - getTemplateId(element: any): string { - return 'terminal.tabs'; - } -} -class TerminalTabsIdentityProvider implements IIdentityProvider { - constructor() { - } - getId(element: TabTreeNode): { toString(): string; } { - if ('children' in element) { - return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; - } else { - return element.instance.title; - } - } - -} -class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { - getAriaLabel(element: TabTreeNode) { - if ('children' in element) { - return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; - } else { - return element.instance.title; - } - } - - getWidgetAriaLabel() { - return localize('terminal.tabs', "TerminalTabs"); - } -} export class TerminalTabsWidget extends WorkbenchObjectTree { constructor( container: HTMLElement, @@ -84,7 +51,8 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { accessibilityService, ); this.setChildren(null, undefined); - this.setChildren(null, createTerminalTabsIterator(terminalService.terminalTabs)); + const children = createTerminalTabsIterator(terminalService.terminalTabs); + this.setChildren(null, children); this.onDidChangeSelection(e => { if (e.elements && e.elements[0]) { @@ -98,31 +66,39 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } -function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { - const result = tabs.map(tab => { - const hasChildren = tab.terminalInstances.length > 1; - const elt = new TabTreeElement(tab); - return { - element: elt, - collapsed: true, - collapsible: hasChildren, - children: getChildren(elt) - }; - }); - return result; + +class TerminalTabsDelegate implements IListVirtualDelegate { + getHeight(element: any): number { + return 24; + } + getTemplateId(element: any): string { + return 'terminal.tabs'; + } } +class TerminalTabsIdentityProvider implements IIdentityProvider { + constructor() { + } + getId(element: TabTreeNode): { toString(): string; } { + if ('tab' in element) { + return element.tab.title; + } else { + return element.instance.instanceId; + } + } -function getChildren(elt: TabTreeElement): Iterable> | undefined { - if (elt.children.length > 1) { - return elt.children.map(child => { - return { - element: child, - collapsed: true, - collapsible: false - }; - }); +} +class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(element: TabTreeNode) { + if ('tab' in element) { + return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; + } else { + return element.instance.title; + } + } + + getWidgetAriaLabel() { + return localize('terminal.tabs', "TerminalTabs"); } - return undefined; } class TerminalTabsRenderer implements ITreeRenderer { @@ -167,11 +143,9 @@ class TabTreeElement { get tab(): ITerminalTab { return this._tab; } - get children(): TabTreeChild[] { return this._children; } - set children(newChildren: TabTreeChild[]) { this._children = newChildren; } @@ -191,3 +165,31 @@ class TabTreeChild { return this._tab; } } + + +function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { + const result = tabs.map(tab => { + const hasChildren = tab.terminalInstances.length > 1; + const elt = new TabTreeElement(tab); + return { + element: elt, + collapsed: true, + collapsible: hasChildren, + children: getChildren(elt) + }; + }); + return result; +} + +function getChildren(elt: TabTreeElement): Iterable> | undefined { + if (elt.children.length > 1) { + return elt.children.map(child => { + return { + element: child, + collapsed: true, + collapsible: false + }; + }); + } + return undefined; +} From 0f696464e69a0cf6d37a4f53e3079914eec7c68c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 13:19:53 -0700 Subject: [PATCH 08/49] get instances to focus on click --- .../contrib/terminal/browser/terminalTabsWidget.ts | 9 +++++---- .../workbench/contrib/terminal/browser/terminalView.ts | 3 --- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index bd9adf07eec9c..82a805c0bfd92 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -16,6 +16,7 @@ import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; const $ = DOM.$; @@ -28,7 +29,8 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService, @IAccessibilityService accessibilityService: IAccessibilityService, - @ITerminalService terminalService: ITerminalService + @ITerminalService terminalService: ITerminalService, + @IInstantiationService _instantiationService: IInstantiationService ) { super('TerminalTabsTree', container, new TerminalTabsDelegate(), @@ -42,6 +44,7 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { filter: undefined, smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), multipleSelectionSupport: false, + expandOnlyOnTwistieClick: true }, contextKeyService, listService, @@ -53,11 +56,10 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { this.setChildren(null, undefined); const children = createTerminalTabsIterator(terminalService.terminalTabs); this.setChildren(null, children); - this.onDidChangeSelection(e => { if (e.elements && e.elements[0]) { if ('_instance' in e.elements[0]) { - terminalService.setActiveInstance(e.elements[0].instance); + e.elements[0].instance.focus(true); } else { terminalService.setActiveTabByIndex(terminalService.terminalTabs.indexOf(e.elements[0].tab)); } @@ -66,7 +68,6 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } - class TerminalTabsDelegate implements IListVirtualDelegate { getHeight(element: any): number { return 24; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 76430202d5784..44382b15b76ab 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -122,9 +122,6 @@ export class TerminalViewPane extends ViewPane { } if (this._terminalService.configHelper.config.showTabs) { - // this._tabsWidgetContainer = this._instantiationService.createInstance(SplitPaneContainer, this._terminalContainer, Orientation.HORIZONTAL).splitView.el; - // this._tabsWidgetContainer.classList.add('tabs-widget-container'); - // this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { 'role': 'navigation', From 617b7c0067bf05dc7dcf9a9a2e316959301b05e5 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 14:43:14 -0700 Subject: [PATCH 09/49] take a break --- .../contrib/terminal/browser/tabsView.ts | 34 +++++++++++++++++++ .../contrib/terminal/browser/terminalView.ts | 21 +++--------- 2 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/tabsView.ts diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts new file mode 100644 index 0000000000000..cadd71f98b949 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; + + +export class TabsView { + private _splitView: SplitView | undefined; + private _widget: WorkbenchObjectTree | undefined; + + constructor( + context: string, + container: HTMLElement, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ITerminalService private readonly _terminalService: ITerminalService, + + ) { + if (context === 'terminal') { + this._splitView = new SplitView(container, { orientation: Orientation.HORIZONTAL }); + this._widget = _instantiationService.createInstance(TerminalTabsWidget, this._splitView.el); + } + } + + public layout(width: number, height: number): void { + this._splitView?.layout(width); + this._terminalService.terminalTabs.forEach(t => t.layout(width - 20, height)); + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 44382b15b76ab..b0ca4d2f42f5e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -36,7 +36,7 @@ import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; +import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -48,7 +48,7 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; - private _tabsWidgetContainer: HTMLElement | undefined; + private _tabsView: TabsView | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -115,19 +115,8 @@ export class TerminalViewPane extends ViewPane { this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); } - - if ((!this._terminalService.configHelper.config.showTabs && this._tabsWidgetContainer) || this._tabsWidgetContainer) { - this._terminalContainer.removeChild(this._tabsWidgetContainer); - this._tabsWidgetContainer = undefined; - } - - if (this._terminalService.configHelper.config.showTabs) { - this._tabsWidgetContainer = dom.append(this._terminalContainer, dom.$('.tabs-widget-container')); - dom.append(this._tabsWidgetContainer, dom.$('.tabs-widget-wrapper', { - 'role': 'navigation', - 'aria-label': nls.localize('terminal-tabs-widget', "Terminal Tabs"), - })); - this.instantiationService.createInstance(TerminalTabsWidget, this._tabsWidgetContainer); + if (!this._tabsView && this._terminalService.configHelper.config.showTabs) { + this._tabsView = this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer); } this._parentDomElement.appendChild(this._fontStyleElement); @@ -192,7 +181,7 @@ export class TerminalViewPane extends ViewPane { this._bodyDimensions.width = width; this._bodyDimensions.height = height; - this._terminalService.terminalTabs.forEach(t => t.layout(width, height)); + this._tabsView?.layout(width, height); } public getActionViewItem(action: Action): IActionViewItem | undefined { From e757e4e25adcceaa082ef63efc6e481be46256fc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 15:14:43 -0700 Subject: [PATCH 10/49] still not working --- src/vs/workbench/contrib/terminal/browser/tabsView.ts | 4 ++-- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index cadd71f98b949..d39b0f40965d3 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -28,7 +28,7 @@ export class TabsView { } public layout(width: number, height: number): void { - this._splitView?.layout(width); - this._terminalService.terminalTabs.forEach(t => t.layout(width - 20, height)); + this._splitView?.layout(100); + this._terminalService.terminalTabs.forEach(t => t.layout(width - 100, height)); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index b0ca4d2f42f5e..d97a7f3a5c0a6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -181,7 +181,11 @@ export class TerminalViewPane extends ViewPane { this._bodyDimensions.width = width; this._bodyDimensions.height = height; - this._tabsView?.layout(width, height); + if (this._tabsView) { + this._tabsView?.layout(width, height); + } else { + this._terminalService.terminalTabs.forEach(t => t.layout(width, height)); + } } public getActionViewItem(action: Action): IActionViewItem | undefined { From 224127ce54eec60e4abd159444b361ce0c969408 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 5 Apr 2021 20:48:42 -0700 Subject: [PATCH 11/49] add splitTabsPane --- .../terminal/browser/media/terminal.css | 9 +- .../contrib/terminal/browser/tabsView.ts | 107 ++++++++++++++++-- .../contrib/terminal/browser/terminalTab.ts | 2 +- .../terminal/browser/terminalTabsWidget.ts | 7 +- .../contrib/terminal/browser/terminalView.ts | 2 +- 5 files changed, 112 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 6d7cf97e6fa53..9148909dbf57c 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -9,7 +9,6 @@ float: left; height: 100%; } - .monaco-workbench .pane-body.integrated-terminal { align-content: flex-start; align-items: baseline; @@ -112,6 +111,14 @@ cursor: default; } +.monaco-list-row { + padding-left: 4px; /* align top level twistie with `Explorer` title label */ +} + +.monaco-list .monaco-tl-twistie { + opacity: 0.3; +} + .monaco-workbench .pane-body.integrated-terminal .xterm { position: absolute; bottom: 0; diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index d39b0f40965d3..84749cc971305 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -5,30 +5,115 @@ import { Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { Event } from 'vs/base/common/event'; +import { IView } from 'vs/workbench/common/views'; +import * as dom from 'vs/base/browser/dom'; +import { IProgressIndicator } from 'vs/platform/progress/common/progress'; +import { Sizing } from 'vs/base/browser/ui/grid/grid'; +export class TabsView extends Disposable { + private _height: number; + private _width: number; + private _widget: HTMLElement; + private _splitView!: SplitView; + private readonly _splitViewDisposables = this._register(new DisposableStore()); + private _children: SplitTabsPane[] = []; -export class TabsView { - private _splitView: SplitView | undefined; - private _widget: WorkbenchObjectTree | undefined; + private _onDidChange: Event = Event.None; + public get onDidChange(): Event { return this._onDidChange; } constructor( context: string, - container: HTMLElement, + private _container: HTMLElement, + private _parentContainer: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITerminalService private readonly _terminalService: ITerminalService, - + @ITerminalService private readonly _terminalService: ITerminalService ) { + super(); if (context === 'terminal') { - this._splitView = new SplitView(container, { orientation: Orientation.HORIZONTAL }); - this._widget = _instantiationService.createInstance(TerminalTabsWidget, this._splitView.el); + const div = document.createElement('div'); + _instantiationService.createInstance(TerminalTabsWidget, div); + this._widget = div; } + this._createSplitView(); + this._width = _parentContainer.offsetWidth; + this._height = _parentContainer.offsetHeight; + this._splitView.layout(this._width); + } + + public get splitView(): SplitView { + return this._splitView; + } + + private _createSplitView(): void { + this._splitView = new SplitView(this._parentContainer, { orientation: Orientation.HORIZONTAL }); + this._splitViewDisposables.clear(); + this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); + this._splitView.addView(new SplitTabsPane(this._widget, 140), 140, 0); + this._splitView.addView(new SplitTabsPane(this._container, 300), 300, 1); } public layout(width: number, height: number): void { - this._splitView?.layout(100); - this._terminalService.terminalTabs.forEach(t => t.layout(width - 100, height)); + this._width = width; + this._height = height; + this._children.forEach(c => c.orthogonalLayout(width)); + this._splitView.layout(width); + } +} +class SplitTabsPane implements IView { + public minimumSize: number = 120; + public maximumSize: number = Number.MAX_VALUE; + + public orientation: Orientation | undefined; + + private _onDidChange: Event = Event.None; + public get onDidChange(): Event { return this._onDidChange; } + + readonly element: HTMLElement; + + constructor( + readonly item: HTMLElement, + public orthogonalSize: number + ) { + this.element = document.createElement('div'); + this.element.className = 'terminal-tabs-split-pane'; + console.log(item); + console.log(this.element); + this.element.appendChild(item); + console.log('after', this.element); + + } + id: string = 'split-tabs-view'; + focus(): void { + throw new Error('Method not implemented.'); + } + isVisible(): boolean { + throw new Error('Method not implemented.'); + } + isBodyVisible(): boolean { + throw new Error('Method not implemented.'); + } + setExpanded(expanded: boolean): boolean { + throw new Error('Method not implemented.'); + } + getProgressIndicator(): IProgressIndicator | undefined { + throw new Error('Method not implemented.'); + } + public layout(size: number): void { + // Only layout when both sizes are known + if (!size || !this.orthogonalSize) { + return; + } + + + } + + public orthogonalLayout(size: number): void { + this.orthogonalSize = size; } } + diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index caba3dab11e8f..8e76b7f2ba94e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -193,7 +193,7 @@ export class SplitPaneContainer extends Disposable { } } -class SplitPane implements IView { +export class SplitPane implements IView { public minimumSize: number = SPLIT_PANE_MIN_SIZE; public maximumSize: number = Number.MAX_VALUE; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 82a805c0bfd92..6a6c7b1bacd8a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -17,6 +17,7 @@ import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Codicon } from 'vs/base/common/codicons'; const $ = DOM.$; @@ -114,14 +115,17 @@ class TerminalTabsRenderer implements ITreeRenderer, index: number, template: ITerminalTabEntryTemplate): void { let label = ''; + let icon; let item = node.element; if ('children' in item) { label = item ? item.children.length === 0 ? 'Starting...' : item?.children.length > 1 ? `Terminals (${item.children.length})` : item.children[0].instance.title : ''; } else if ('instance' in item) { label = item.instance.title; + icon = item.instance.icon; } template.labelElement.textContent = label; template.labelElement.title = label; + template.icon = icon; } disposeTemplate(templateData: ITerminalTabEntryTemplate): void { @@ -130,6 +134,7 @@ class TerminalTabsRenderer implements ITreeRenderer> return { element: child, collapsed: true, - collapsible: false + collapsible: false, }; }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index d97a7f3a5c0a6..19a7be11a08c2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -116,7 +116,7 @@ export class TerminalViewPane extends ViewPane { this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); } if (!this._tabsView && this._terminalService.configHelper.config.showTabs) { - this._tabsView = this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer); + this._tabsView = this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer, this._parentDomElement); } this._parentDomElement.appendChild(this._fontStyleElement); From be2e72af102a4211602fa48cb949713142a71e7d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 08:14:24 -0700 Subject: [PATCH 12/49] clean up --- src/vs/base/browser/ui/list/listView.ts | 1 + .../terminal/browser/media/terminal.css | 18 ------------------ .../contrib/terminal/browser/terminalTab.ts | 4 ++-- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 863309fe651f8..ee8fd156e23aa 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -756,6 +756,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer.appendChild(item.row.domNode); } } + this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 9148909dbf57c..09f46ac51e214 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -3,12 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -.tabs-widget-container { - width: 20%; - float: left; - height: 100%; -} .monaco-workbench .pane-body.integrated-terminal { align-content: flex-start; align-items: baseline; @@ -25,13 +19,10 @@ width: 100%; box-sizing: border-box; overflow: hidden; - float:right; } .monaco-workbench .pane-body.integrated-terminal .terminal-tab { height: 100%; - width: 80%; - float: right; } .monaco-workbench .pane-body.integrated-terminal .terminal-wrapper { @@ -111,14 +102,6 @@ cursor: default; } -.monaco-list-row { - padding-left: 4px; /* align top level twistie with `Explorer` title label */ -} - -.monaco-list .monaco-tl-twistie { - opacity: 0.3; -} - .monaco-workbench .pane-body.integrated-terminal .xterm { position: absolute; bottom: 0; @@ -213,4 +196,3 @@ .monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal > .monaco-select-box { padding: 0 22px 0 6px; } - diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 8e76b7f2ba94e..17a6b52ca1021 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -16,7 +16,7 @@ import { localize } from 'vs/nls'; const SPLIT_PANE_MIN_SIZE = 120; -export class SplitPaneContainer extends Disposable { +class SplitPaneContainer extends Disposable { private _height: number; private _width: number; private _splitView!: SplitView; @@ -193,7 +193,7 @@ export class SplitPaneContainer extends Disposable { } } -export class SplitPane implements IView { +class SplitPane implements IView { public minimumSize: number = SPLIT_PANE_MIN_SIZE; public maximumSize: number = Number.MAX_VALUE; From d52fbadeb20c6fd2dc00670a51524a71cc7bc194 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 08:15:19 -0700 Subject: [PATCH 13/49] more cleanup --- src/vs/workbench/contrib/terminal/browser/tabsView.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index 84749cc971305..a6ade5a8bda7f 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -8,12 +8,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { Event } from 'vs/base/common/event'; import { IView } from 'vs/workbench/common/views'; -import * as dom from 'vs/base/browser/dom'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; -import { Sizing } from 'vs/base/browser/ui/grid/grid'; export class TabsView extends Disposable { private _height: number; From 99d9bf269744ef0a15b3b7561ce66276ff3ed7c2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 08:34:57 -0700 Subject: [PATCH 14/49] use this. --- .../contrib/terminal/browser/tabsView.ts | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index a6ade5a8bda7f..fc1bff3307fb7 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -13,9 +13,9 @@ import { IView } from 'vs/workbench/common/views'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; export class TabsView extends Disposable { - private _height: number; - private _width: number; - private _widget: HTMLElement; + private height: number; + private width: number; + private _widget!: HTMLElement; private _splitView!: SplitView; private readonly _splitViewDisposables = this._register(new DisposableStore()); private _children: SplitTabsPane[] = []; @@ -25,21 +25,22 @@ export class TabsView extends Disposable { constructor( context: string, - private _container: HTMLElement, - private _parentContainer: HTMLElement, + private container: HTMLElement, + private parentContainer: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITerminalService private readonly _terminalService: ITerminalService ) { super(); if (context === 'terminal') { const div = document.createElement('div'); - _instantiationService.createInstance(TerminalTabsWidget, div); + div.classList.add('tabs-widget'); + this._instantiationService.createInstance(TerminalTabsWidget, div); this._widget = div; } this._createSplitView(); - this._width = _parentContainer.offsetWidth; - this._height = _parentContainer.offsetHeight; - this._splitView.layout(this._width); + this.width = parentContainer.offsetWidth; + this.height = parentContainer.offsetHeight; + this._splitView.layout(this.width); } public get splitView(): SplitView { @@ -47,16 +48,19 @@ export class TabsView extends Disposable { } private _createSplitView(): void { - this._splitView = new SplitView(this._parentContainer, { orientation: Orientation.HORIZONTAL }); + this._splitView = new SplitView(this.parentContainer, { orientation: Orientation.HORIZONTAL }); this._splitViewDisposables.clear(); this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - this._splitView.addView(new SplitTabsPane(this._widget, 140), 140, 0); - this._splitView.addView(new SplitTabsPane(this._container, 300), 300, 1); + const widgetWidth = 140; + // this._splitView.addView(new SplitTabsPane(this._widget, widgetWidth, this._terminalService), widgetWidth, 0); + // this._splitView.addView(new SplitTabsPane(this._container, this._width - widgetWidth, this._terminalService), this._width - widgetWidth, 1); + this._children.push(new SplitTabsPane(this._widget, widgetWidth, this._terminalService)); + this._children.push(new SplitTabsPane(this.container, this.width - widgetWidth, this._terminalService)); } public layout(width: number, height: number): void { - this._width = width; - this._height = height; + this.width = width; + this.height = height; this._children.forEach(c => c.orthogonalLayout(width)); this._splitView.layout(width); } @@ -72,9 +76,14 @@ class SplitTabsPane implements IView { readonly element: HTMLElement; + private readonly _item: HTMLElement; + + private readonly _terminalService: ITerminalService; + constructor( readonly item: HTMLElement, - public orthogonalSize: number + public height: number, + @ITerminalService terminalService: ITerminalService ) { this.element = document.createElement('div'); this.element.className = 'terminal-tabs-split-pane'; @@ -82,6 +91,8 @@ class SplitTabsPane implements IView { console.log(this.element); this.element.appendChild(item); console.log('after', this.element); + this._item = item; + this._terminalService = terminalService; } id: string = 'split-tabs-view'; @@ -100,17 +111,22 @@ class SplitTabsPane implements IView { getProgressIndicator(): IProgressIndicator | undefined { throw new Error('Method not implemented.'); } + public layout(size: number): void { // Only layout when both sizes are known - if (!size || !this.orthogonalSize) { + if (!size || !this.height) { return; } + if (this._item.classList.contains('tabs-widget')) { + } else { + this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); + } } public orthogonalLayout(size: number): void { - this.orthogonalSize = size; + this.height = size; } } From 76ec94285b9ceb98a7f64288615afdda3f1cd126 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 09:46:01 -0700 Subject: [PATCH 15/49] a little closer --- .../contrib/terminal/browser/tabsView.ts | 32 +++++++++++-------- .../terminal/browser/terminalService.ts | 1 - .../contrib/terminal/browser/terminalTab.ts | 2 +- .../contrib/terminal/browser/terminalView.ts | 23 +++++++++---- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index fc1bff3307fb7..7f25b56851483 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -16,10 +16,11 @@ export class TabsView extends Disposable { private height: number; private width: number; private _widget!: HTMLElement; + private _terminalService: ITerminalService; private _splitView!: SplitView; private readonly _splitViewDisposables = this._register(new DisposableStore()); private _children: SplitTabsPane[] = []; - + private _container: HTMLElement; private _onDidChange: Event = Event.None; public get onDidChange(): Event { return this._onDidChange; } @@ -28,18 +29,20 @@ export class TabsView extends Disposable { private container: HTMLElement, private parentContainer: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITerminalService private readonly _terminalService: ITerminalService + @ITerminalService private readonly terminalService: ITerminalService ) { super(); - if (context === 'terminal') { + if (context === 'terminal' && !this._widget) { const div = document.createElement('div'); div.classList.add('tabs-widget'); this._instantiationService.createInstance(TerminalTabsWidget, div); this._widget = div; } - this._createSplitView(); this.width = parentContainer.offsetWidth; this.height = parentContainer.offsetHeight; + this._terminalService = this.terminalService; + this._container = container; + this._createSplitView(); this._splitView.layout(this.width); } @@ -48,20 +51,21 @@ export class TabsView extends Disposable { } private _createSplitView(): void { - this._splitView = new SplitView(this.parentContainer, { orientation: Orientation.HORIZONTAL }); - this._splitViewDisposables.clear(); - this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - const widgetWidth = 140; - // this._splitView.addView(new SplitTabsPane(this._widget, widgetWidth, this._terminalService), widgetWidth, 0); - // this._splitView.addView(new SplitTabsPane(this._container, this._width - widgetWidth, this._terminalService), this._width - widgetWidth, 1); - this._children.push(new SplitTabsPane(this._widget, widgetWidth, this._terminalService)); - this._children.push(new SplitTabsPane(this.container, this.width - widgetWidth, this._terminalService)); + if (!this._splitView) { + this._splitView = new SplitView(this.parentContainer, { orientation: Orientation.HORIZONTAL }); + this._splitViewDisposables.clear(); + this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); + const widgetWidth = 200; + this._splitView.addView(new SplitTabsPane(this._widget, widgetWidth, this._terminalService), widgetWidth, 0); + this._splitView.addView(new SplitTabsPane(this._container, this.width - widgetWidth, this._terminalService), this.width - widgetWidth, 1); + } } public layout(width: number, height: number): void { this.width = width; this.height = height; - this._children.forEach(c => c.orthogonalLayout(width)); + this._children.forEach(c => c.orthogonalLayout(height)); + this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(this._container)); this._splitView.layout(width); } } @@ -121,7 +125,7 @@ class SplitTabsPane implements IView { if (this._item.classList.contains('tabs-widget')) { } else { - this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); + // this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 48148cab9635b..98c3291c97e6f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1082,7 +1082,6 @@ export class TerminalService implements ITerminalService { public async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { this._configHelper.panelContainer = panelContainer; this._terminalContainer = terminalContainer; - this._terminalTabs.forEach(tab => tab.attachToElement(terminalContainer)); } public hidePanel(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 17a6b52ca1021..cc3ce5c5638bf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -264,7 +264,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { } this._activeInstanceIndex = 0; if (this._container) { - this.attachToElement(this._container); + // this.attachToElement(this._container); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 19a7be11a08c2..58df9cd7c59c1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -37,6 +37,7 @@ import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; +import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; const FIND_FOCUS_CLASS = 'find-focused'; @@ -48,7 +49,8 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; - private _tabsView: TabsView | undefined; + private _tabsViewWrapper: HTMLElement | undefined; + private _tabsView: TerminalTabsWidget | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -115,17 +117,26 @@ export class TerminalViewPane extends ViewPane { this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); } - if (!this._tabsView && this._terminalService.configHelper.config.showTabs) { - this._tabsView = this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer, this._parentDomElement); + + if (this._tabsViewWrapper) { + this._parentDomElement.removeChild(this._tabsViewWrapper); + this._tabsViewWrapper = undefined; + } + if (!this._tabsViewWrapper && this._terminalService.configHelper.config.showTabs) { + this._tabsViewWrapper = document.createElement('div'); + this._tabsViewWrapper.classList.add('tabs-view-wrapper'); + this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer, this._parentDomElement); + this._parentDomElement.append(this._tabsViewWrapper); } this._parentDomElement.appendChild(this._fontStyleElement); - this._parentDomElement.appendChild(this._terminalContainer); - this._parentDomElement.appendChild(this._findWidget.getDomNode()); + this._parentDomElement.appendChild( + // this._parentDomElement.appendChild(this._terminalContainer); + this._parentDomElement.appendChild(this._findWidget.getDomNode())); this._attachEventListeners(this._parentDomElement, this._terminalContainer); - this._terminalService.setContainers(container, this._terminalContainer); + this._terminalService.setContainers(container, this._parentDomElement); this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._register(this.configurationService.onDidChangeConfiguration(e => { From 383a22e924a2a67c5e94f18ea8adabb87646db47 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 09:49:49 -0700 Subject: [PATCH 16/49] try more stuff --- src/vs/workbench/contrib/terminal/browser/tabsView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index 7f25b56851483..f46a5319f5508 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -64,8 +64,8 @@ export class TabsView extends Disposable { public layout(width: number, height: number): void { this.width = width; this.height = height; - this._children.forEach(c => c.orthogonalLayout(height)); - this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(this._container)); + this._children.forEach(c => c.orthogonalLayout(width)); + this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(this._children[1].element)); this._splitView.layout(width); } } From 56b96bc997b5a78f8ceccf9c4f64f131edd1e9df Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 11:17:53 -0700 Subject: [PATCH 17/49] The Great Migration of code from terminalView to tabsView --- .../contrib/terminal/browser/tabsView.ts | 216 ++++++++++++++---- .../contrib/terminal/browser/terminalView.ts | 186 ++------------- 2 files changed, 195 insertions(+), 207 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index f46a5319f5508..e63a0bb06abc0 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -11,39 +11,56 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IView } from 'vs/workbench/common/views'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; +import * as dom from 'vs/base/browser/dom'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IAction } from 'vs/base/common/actions'; +import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { DataTransfers } from 'vs/base/browser/dnd'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { URI } from 'vs/base/common/uri'; export class TabsView extends Disposable { - private height: number; - private width: number; - private _widget!: HTMLElement; - private _terminalService: ITerminalService; + private _menu: IMenu; + private _width: number; + private _height: number; + private _cancelContextMenu: boolean = false; + private _tabsElement: HTMLElement; private _splitView!: SplitView; private readonly _splitViewDisposables = this._register(new DisposableStore()); private _children: SplitTabsPane[] = []; - private _container: HTMLElement; + private _terminalContainer: HTMLElement; private _onDidChange: Event = Event.None; public get onDidChange(): Event { return this._onDidChange; } constructor( - context: string, - private container: HTMLElement, - private parentContainer: HTMLElement, + private _parentDomElement: HTMLElement, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITerminalService private readonly terminalService: ITerminalService + @ITerminalService private readonly _terminalService: ITerminalService, + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IContextKeyService _contextKeyService: IContextKeyService, + @IMenuService _menuService: IMenuService, + @INotificationService private readonly _notificationService: INotificationService ) { super(); - if (context === 'terminal' && !this._widget) { - const div = document.createElement('div'); - div.classList.add('tabs-widget'); - this._instantiationService.createInstance(TerminalTabsWidget, div); - this._widget = div; - } - this.width = parentContainer.offsetWidth; - this.height = parentContainer.offsetHeight; - this._terminalService = this.terminalService; - this._container = container; + this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); + this._tabsElement = document.createElement('div'); + this._tabsElement.classList.add('tabs-widget'); + this._instantiationService.createInstance(TerminalTabsWidget, this._tabsElement); + + this._width = _parentDomElement.offsetWidth; + this._height = _parentDomElement.offsetHeight; + this._createSplitView(); - this._splitView.layout(this.width); + + this._terminalContainer = document.createElement('div'); + this._terminalContainer.classList.add('terminal-outer-container'); + + this._attachEventListeners(this._parentDomElement, this._terminalContainer); } public get splitView(): SplitView { @@ -51,23 +68,141 @@ export class TabsView extends Disposable { } private _createSplitView(): void { - if (!this._splitView) { - this._splitView = new SplitView(this.parentContainer, { orientation: Orientation.HORIZONTAL }); - this._splitViewDisposables.clear(); - this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - const widgetWidth = 200; - this._splitView.addView(new SplitTabsPane(this._widget, widgetWidth, this._terminalService), widgetWidth, 0); - this._splitView.addView(new SplitTabsPane(this._container, this.width - widgetWidth, this._terminalService), this.width - widgetWidth, 1); + if (this._splitView) { + return; } + this._splitView = new SplitView(this._parentDomElement, { orientation: Orientation.HORIZONTAL }); + this._splitViewDisposables.clear(); + this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); + const tabsWidgetWidth = 200; + if (this._terminalService.configHelper.config.showTabs) { + this._splitView.addView(new SplitTabsPane(this._tabsElement, tabsWidgetWidth, this._terminalService), tabsWidgetWidth, 0); + } + const tabContainer = new SplitTabsPane(this._terminalContainer, this._width - tabsWidgetWidth, this._terminalService); + this._splitView.addView(tabContainer, this._width - tabsWidgetWidth, 1); + this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(tabContainer.element)); } public layout(width: number, height: number): void { - this.width = width; - this.height = height; + this._width = width; + this._height = height; this._children.forEach(c => c.orthogonalLayout(width)); - this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(this._children[1].element)); this._splitView.layout(width); } + + private _attachEventListeners(parentDomElement: HTMLElement, terminalContainer: HTMLElement): void { + this._register(dom.addDisposableListener(parentDomElement, 'mousedown', async (event: MouseEvent) => { + if (this._terminalService.terminalInstances.length === 0) { + return; + } + + if (event.which === 2 && isLinux) { + // Drop selection and focus terminal on Linux to enable middle button paste when click + // occurs on the selection itself. + const terminal = this._terminalService.getActiveInstance(); + if (terminal) { + terminal.focus(); + } + } else if (event.which === 3) { + const rightClickBehavior = this._terminalService.configHelper.config.rightClickBehavior; + if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') { + const terminal = this._terminalService.getActiveInstance(); + if (!terminal) { + return; + } + + // copyPaste: Shift+right click should open context menu + if (rightClickBehavior === 'copyPaste' && event.shiftKey) { + this._openContextMenu(event); + return; + } + + if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) { + await terminal.copySelection(); + terminal.clearSelection(); + } else { + if (BrowserFeatures.clipboard.readText) { + terminal.paste(); + } else { + this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); + } + } + // Clear selection after all click event bubbling is finished on Mac to prevent + // right-click selecting a word which is seemed cannot be disabled. There is a + // flicker when pasting but this appears to give the best experience if the + // setting is enabled. + if (isMacintosh) { + setTimeout(() => { + terminal.clearSelection(); + }, 0); + } + this._cancelContextMenu = true; + } + } + })); + this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => { + if (!this._cancelContextMenu) { + this._openContextMenu(event); + } + event.preventDefault(); + event.stopImmediatePropagation(); + this._cancelContextMenu = false; + })); + this._register(dom.addDisposableListener(document, 'keydown', (event: KeyboardEvent) => { + terminalContainer.classList.toggle('alt-active', !!event.altKey); + })); + this._register(dom.addDisposableListener(document, 'keyup', (event: KeyboardEvent) => { + terminalContainer.classList.toggle('alt-active', !!event.altKey); + })); + this._register(dom.addDisposableListener(parentDomElement, 'keyup', (event: KeyboardEvent) => { + if (event.keyCode === 27) { + // Keep terminal open on escape + event.stopPropagation(); + } + })); + this._register(dom.addDisposableListener(parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { + if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, parentDomElement)) { + if (!e.dataTransfer) { + return; + } + + // Check if files were dragged from the tree explorer + let path: string | undefined; + const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); + if (resources) { + path = URI.parse(JSON.parse(resources)[0]).fsPath; + } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { + // Check if the file was dragged from the filesystem + path = URI.file(e.dataTransfer.files[0].path).fsPath; + } + + if (!path) { + return; + } + + const terminal = this._terminalService.getActiveInstance(); + if (terminal) { + const preparedPath = await this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title, terminal.shellType); + terminal.sendText(preparedPath, false); + terminal.focus(); + } + } + })); + } + private _openContextMenu(event: MouseEvent): void { + const standardEvent = new StandardMouseEvent(event); + const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; + + const actions: IAction[] = []; + const actionsDisposable = createAndFillInContextMenuActions(this._menu, undefined, actions); + + this._contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => actions, + getActionsContext: () => this._parentDomElement, + onHide: () => actionsDisposable.dispose() + }); + } } class SplitTabsPane implements IView { public minimumSize: number = 120; @@ -80,24 +215,14 @@ class SplitTabsPane implements IView { readonly element: HTMLElement; - private readonly _item: HTMLElement; - - private readonly _terminalService: ITerminalService; - constructor( readonly item: HTMLElement, public height: number, - @ITerminalService terminalService: ITerminalService + @ITerminalService _terminalService: ITerminalService ) { this.element = document.createElement('div'); this.element.className = 'terminal-tabs-split-pane'; - console.log(item); - console.log(this.element); this.element.appendChild(item); - console.log('after', this.element); - this._item = item; - this._terminalService = terminalService; - } id: string = 'split-tabs-view'; focus(): void { @@ -122,15 +247,14 @@ class SplitTabsPane implements IView { return; } - if (this._item.classList.contains('tabs-widget')) { + // if (this._item.classList.contains('tabs-widget')) { - } else { - // this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); - } + // } else { + // // this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); + // } } public orthogonalLayout(size: number): void { this.height = size; } } - diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 58df9cd7c59c1..3987493afc234 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; -import * as platform from 'vs/base/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -14,10 +12,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, IColorTheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { configureTerminalSettingsTitle, selectDefaultProfileTitle, switchTerminalActionViewItemSeparator } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { URI } from 'vs/base/common/uri'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { DataTransfers } from 'vs/base/browser/dnd'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; import { ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; @@ -26,31 +21,26 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService } from 'vs/platform/actions/common/actions'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { ITerminalContributionService } from 'vs/workbench/contrib/terminal/common/terminalExtensionPoints'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; -import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; const FIND_FOCUS_CLASS = 'find-focused'; export class TerminalViewPane extends ViewPane { - private _menu: IMenu; private _actions: IAction[] | undefined; - private _cancelContextMenu: boolean = false; private _fontStyleElement: HTMLElement | undefined; private _parentDomElement: HTMLElement | undefined; private _terminalContainer: HTMLElement | undefined; private _findWidget: TerminalFindWidget | undefined; private _tabsViewWrapper: HTMLElement | undefined; - private _tabsView: TerminalTabsWidget | undefined; + private _tabsView: TabsView | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -61,7 +51,7 @@ export class TerminalViewPane extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IConfigurationService configurationService: IConfigurationService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IContextMenuService _contextMenuService: IContextMenuService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITerminalService private readonly _terminalService: ITerminalService, @IThemeService protected readonly themeService: IThemeService, @@ -71,7 +61,6 @@ export class TerminalViewPane extends ViewPane { @IMenuService menuService: IMenuService, ) { super(options, keybindingService, _contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instantiationService, openerService, themeService, telemetryService); - this._menu = this._register(menuService.createMenu(MenuId.TerminalContext, contextKeyService)); this._terminalService.onDidRegisterProcessSupport(() => { if (this._actions) { for (const action of this._actions) { @@ -90,52 +79,35 @@ export class TerminalViewPane extends ViewPane { this._terminalContainer.style.display = 'block'; this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth); } + if (!this._tabsView && this._parentDomElement) { + this._createTabsView(); + this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); + } }); } public renderBody(container?: HTMLElement): void { - if (!container && !this._parentDomElement) { - return; - } else if (!container) { - container = this._parentDomElement; - } - if (!container) { + if (!container || !this._parentDomElement) { return; } + super.renderBody(container); this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); this._fontStyleElement = document.createElement('style'); - if (!this._terminalContainer) { - this._terminalContainer = document.createElement('div'); - this._terminalContainer.classList.add('terminal-outer-container'); - this._terminalContainer.style.display = this.shouldShowWelcome() ? 'none' : 'block'; - } - if (!this._findWidget) { - this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); - this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - } + this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); + this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - if (this._tabsViewWrapper) { - this._parentDomElement.removeChild(this._tabsViewWrapper); - this._tabsViewWrapper = undefined; - } - if (!this._tabsViewWrapper && this._terminalService.configHelper.config.showTabs) { - this._tabsViewWrapper = document.createElement('div'); - this._tabsViewWrapper.classList.add('tabs-view-wrapper'); - this.instantiationService.createInstance(TabsView, 'terminal', this._terminalContainer, this._parentDomElement); - this._parentDomElement.append(this._tabsViewWrapper); + if (!this.shouldShowWelcome()) { + this._createTabsView(); } this._parentDomElement.appendChild(this._fontStyleElement); this._parentDomElement.appendChild( - // this._parentDomElement.appendChild(this._terminalContainer); this._parentDomElement.appendChild(this._findWidget.getDomNode())); - this._attachEventListeners(this._parentDomElement, this._terminalContainer); - this._terminalService.setContainers(container, this._parentDomElement); this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); @@ -182,9 +154,18 @@ export class TerminalViewPane extends ViewPane { }); } })); + this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); + } - // Force another layout (first is setContainers) since config has changed - this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth); + private _createTabsView(): void { + if (!this._parentDomElement) { + return; + } + this._tabsViewWrapper = document.createElement('div'); + this._tabsViewWrapper.classList.add('tabs-view-wrapper'); + this.instantiationService.createInstance(TabsView, this._parentDomElement); + // make helper function + this._parentDomElement.append(this._tabsViewWrapper); } protected layoutBody(height: number, width: number): void { @@ -193,9 +174,7 @@ export class TerminalViewPane extends ViewPane { this._bodyDimensions.width = width; this._bodyDimensions.height = height; if (this._tabsView) { - this._tabsView?.layout(width, height); - } else { - this._terminalService.terminalTabs.forEach(t => t.layout(width, height)); + this._tabsView.layout(width, height); } } @@ -255,121 +234,6 @@ export class TerminalViewPane extends ViewPane { return this._findWidget!; } - private _attachEventListeners(parentDomElement: HTMLElement, terminalContainer: HTMLElement): void { - this._register(dom.addDisposableListener(parentDomElement, 'mousedown', async (event: MouseEvent) => { - if (this._terminalService.terminalInstances.length === 0) { - return; - } - - if (event.which === 2 && platform.isLinux) { - // Drop selection and focus terminal on Linux to enable middle button paste when click - // occurs on the selection itself. - const terminal = this._terminalService.getActiveInstance(); - if (terminal) { - terminal.focus(); - } - } else if (event.which === 3) { - const rightClickBehavior = this._terminalService.configHelper.config.rightClickBehavior; - if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') { - const terminal = this._terminalService.getActiveInstance(); - if (!terminal) { - return; - } - - // copyPaste: Shift+right click should open context menu - if (rightClickBehavior === 'copyPaste' && event.shiftKey) { - this._openContextMenu(event); - return; - } - - if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) { - await terminal.copySelection(); - terminal.clearSelection(); - } else { - if (BrowserFeatures.clipboard.readText) { - terminal.paste(); - } else { - this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${platform.isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); - } - } - // Clear selection after all click event bubbling is finished on Mac to prevent - // right-click selecting a word which is seemed cannot be disabled. There is a - // flicker when pasting but this appears to give the best experience if the - // setting is enabled. - if (platform.isMacintosh) { - setTimeout(() => { - terminal.clearSelection(); - }, 0); - } - this._cancelContextMenu = true; - } - } - })); - this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => { - if (!this._cancelContextMenu) { - this._openContextMenu(event); - } - event.preventDefault(); - event.stopImmediatePropagation(); - this._cancelContextMenu = false; - })); - this._register(dom.addDisposableListener(document, 'keydown', (event: KeyboardEvent) => { - terminalContainer.classList.toggle('alt-active', !!event.altKey); - })); - this._register(dom.addDisposableListener(document, 'keyup', (event: KeyboardEvent) => { - terminalContainer.classList.toggle('alt-active', !!event.altKey); - })); - this._register(dom.addDisposableListener(parentDomElement, 'keyup', (event: KeyboardEvent) => { - if (event.keyCode === 27) { - // Keep terminal open on escape - event.stopPropagation(); - } - })); - this._register(dom.addDisposableListener(parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { - if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, parentDomElement)) { - if (!e.dataTransfer) { - return; - } - - // Check if files were dragged from the tree explorer - let path: string | undefined; - const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); - if (resources) { - path = URI.parse(JSON.parse(resources)[0]).fsPath; - } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { - // Check if the file was dragged from the filesystem - path = URI.file(e.dataTransfer.files[0].path).fsPath; - } - - if (!path) { - return; - } - - const terminal = this._terminalService.getActiveInstance(); - if (terminal) { - const preparedPath = await this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title, terminal.shellType); - terminal.sendText(preparedPath, false); - terminal.focus(); - } - } - })); - } - - private _openContextMenu(event: MouseEvent): void { - const standardEvent = new StandardMouseEvent(event); - const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; - - const actions: IAction[] = []; - const actionsDisposable = createAndFillInContextMenuActions(this._menu, undefined, actions); - - this._contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => actions, - getActionsContext: () => this._parentDomElement, - onHide: () => actionsDisposable.dispose() - }); - } - private _updateTheme(theme?: IColorTheme): void { if (!theme) { theme = this.themeService.getColorTheme(); From ab4f455f85087ae18430aede7f40da802c05e2f0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 11:42:30 -0700 Subject: [PATCH 18/49] more migration --- .../contrib/terminal/browser/tabsView.ts | 17 +++++++++++------ .../contrib/terminal/browser/terminalService.ts | 3 --- .../contrib/terminal/browser/terminalView.ts | 15 ++------------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index e63a0bb06abc0..e879a885b3a85 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -23,11 +23,14 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { URI } from 'vs/base/common/uri'; +import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; + +const FIND_FOCUS_CLASS = 'find-focused'; export class TabsView extends Disposable { private _menu: IMenu; - private _width: number; - private _height: number; + _width: number; + _height: number; private _cancelContextMenu: boolean = false; private _tabsElement: HTMLElement; private _splitView!: SplitView; @@ -39,12 +42,13 @@ export class TabsView extends Disposable { constructor( private _parentDomElement: HTMLElement, + _findWidget: TerminalFindWidget, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITerminalService private readonly _terminalService: ITerminalService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IContextKeyService _contextKeyService: IContextKeyService, @IMenuService _menuService: IMenuService, - @INotificationService private readonly _notificationService: INotificationService + @INotificationService private readonly _notificationService: INotificationService, ) { super(); this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); @@ -55,12 +59,13 @@ export class TabsView extends Disposable { this._width = _parentDomElement.offsetWidth; this._height = _parentDomElement.offsetHeight; - this._createSplitView(); - this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); - + this._terminalContainer.style.display = 'block'; + this._terminalService.setContainers(this._terminalContainer, this._parentDomElement); this._attachEventListeners(this._parentDomElement, this._terminalContainer); + this._createSplitView(); + _findWidget?.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); } public get splitView(): SplitView { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 98c3291c97e6f..8e1b6b822d018 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -161,9 +161,6 @@ export class TerminalService implements ITerminalService { e.affectsConfiguration('terminal.integrated.profiles.linux') || e.affectsConfiguration('terminal.integrated.useWslProfiles')) { this._updateAvailableProfilesNow(); - } else if (e.affectsConfiguration('terminal.integrated.showTabs')) { - const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; - pane?.renderBody(); } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 3987493afc234..19d86e1c5063f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -31,14 +31,11 @@ import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; -const FIND_FOCUS_CLASS = 'find-focused'; - export class TerminalViewPane extends ViewPane { private _actions: IAction[] | undefined; private _fontStyleElement: HTMLElement | undefined; private _parentDomElement: HTMLElement | undefined; - private _terminalContainer: HTMLElement | undefined; - private _findWidget: TerminalFindWidget | undefined; + private _findWidget: TerminalFindWidget; private _tabsViewWrapper: HTMLElement | undefined; private _tabsView: TabsView | undefined; private _terminalsInitialized = false; @@ -75,10 +72,6 @@ export class TerminalViewPane extends ViewPane { } this._isWelcomeShowing = true; this._onDidChangeViewWelcomeState.fire(); - if (this._terminalContainer) { - this._terminalContainer.style.display = 'block'; - this.layoutBody(this._terminalContainer.offsetHeight, this._terminalContainer.offsetWidth); - } if (!this._tabsView && this._parentDomElement) { this._createTabsView(); this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); @@ -96,9 +89,7 @@ export class TerminalViewPane extends ViewPane { this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); this._fontStyleElement = document.createElement('style'); - this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); - this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); if (!this.shouldShowWelcome()) { this._createTabsView(); @@ -108,8 +99,6 @@ export class TerminalViewPane extends ViewPane { this._parentDomElement.appendChild( this._parentDomElement.appendChild(this._findWidget.getDomNode())); - this._terminalService.setContainers(container, this._parentDomElement); - this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.fontFamily') || e.affectsConfiguration('editor.fontFamily')) { @@ -163,7 +152,7 @@ export class TerminalViewPane extends ViewPane { } this._tabsViewWrapper = document.createElement('div'); this._tabsViewWrapper.classList.add('tabs-view-wrapper'); - this.instantiationService.createInstance(TabsView, this._parentDomElement); + this.instantiationService.createInstance(TabsView, this._parentDomElement, this._findWidget); // make helper function this._parentDomElement.append(this._tabsViewWrapper); } From 6b1d3bfb949b0fed7d6acfc0c1411ab65827d1c6 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 12:04:37 -0700 Subject: [PATCH 19/49] delete comment --- src/vs/workbench/contrib/terminal/browser/terminalView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 19d86e1c5063f..cd159e2294341 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -153,7 +153,6 @@ export class TerminalViewPane extends ViewPane { this._tabsViewWrapper = document.createElement('div'); this._tabsViewWrapper.classList.add('tabs-view-wrapper'); this.instantiationService.createInstance(TabsView, this._parentDomElement, this._findWidget); - // make helper function this._parentDomElement.append(this._tabsViewWrapper); } From 070e70870944519514ff881feb7a1b811d35e484 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 12:25:38 -0700 Subject: [PATCH 20/49] revert some changes --- src/vs/workbench/contrib/terminal/browser/tabsView.ts | 11 ++++++++--- .../contrib/terminal/browser/terminalView.ts | 6 +----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index e879a885b3a85..7ea44c3536489 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -65,7 +65,11 @@ export class TabsView extends Disposable { this._terminalService.setContainers(this._terminalContainer, this._parentDomElement); this._attachEventListeners(this._parentDomElement, this._terminalContainer); this._createSplitView(); - _findWidget?.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); + _findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); + // this._terminalService.onInstancesChanged(() => { + // this._splitView.dispose(); + // this._createSplitView(); + // }); } public get splitView(): SplitView { @@ -79,8 +83,9 @@ export class TabsView extends Disposable { this._splitView = new SplitView(this._parentDomElement, { orientation: Orientation.HORIZONTAL }); this._splitViewDisposables.clear(); this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - const tabsWidgetWidth = 200; - if (this._terminalService.configHelper.config.showTabs) { + const showTabs = this._terminalService.configHelper.config.showTabs; + const tabsWidgetWidth = showTabs ? 200 : 0; + if (showTabs) { this._splitView.addView(new SplitTabsPane(this._tabsElement, tabsWidgetWidth, this._terminalService), tabsWidgetWidth, 0); } const tabContainer = new SplitTabsPane(this._terminalContainer, this._width - tabsWidgetWidth, this._terminalService); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index cd159e2294341..68e168c45bd78 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -79,11 +79,7 @@ export class TerminalViewPane extends ViewPane { }); } - public renderBody(container?: HTMLElement): void { - if (!container || !this._parentDomElement) { - return; - } - + public renderBody(container: HTMLElement): void { super.renderBody(container); this._parentDomElement = container; From 1b70c818a17b1207ab2806dd40044c8e51057823 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 12:30:44 -0700 Subject: [PATCH 21/49] fix error, sign off for now --- .../workbench/contrib/terminal/browser/terminalService.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 8e1b6b822d018..eede1e6e7952f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -164,13 +164,6 @@ export class TerminalService implements ITerminalService { } }); - this.onInstancesChanged(() => { - if (this._configHelper.config.showTabs) { - const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; - pane?.renderBody(); - } - }); - const enableTerminalReconnection = this.configHelper.config.enablePersistentSessions; const conn = this._remoteAgentService.getConnection(); From 3546e73c6826a2ea27b0abdd27db3c42025da49b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 16:24:23 -0700 Subject: [PATCH 22/49] take 2, sash still disabled but otherwise working --- .vscode/settings.json | 1 - .../contrib/terminal/browser/tabsView.ts | 4 -- .../terminal/browser/terminalService.ts | 18 ++--- .../terminal/browser/terminalTabbedView.ts | 65 +++++++++++++++++++ .../contrib/terminal/browser/terminalView.ts | 52 +++------------ 5 files changed, 83 insertions(+), 57 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 7eede8c30a989..eabef1693e988 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -86,5 +86,4 @@ }, "typescript.tsc.autoDetect": "off", "notebook.experimental.useMarkdownRenderer": true, - "testing.autoRun.mode": "onlyPreviouslyRun", } diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts index 7ea44c3536489..ee41e2112b5e7 100644 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ b/src/vs/workbench/contrib/terminal/browser/tabsView.ts @@ -66,10 +66,6 @@ export class TabsView extends Disposable { this._attachEventListeners(this._parentDomElement, this._terminalContainer); this._createSplitView(); _findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - // this._terminalService.onInstancesChanged(() => { - // this._splitView.dispose(); - // this._createSplitView(); - // }); } public get splitView(): SplitView { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index eede1e6e7952f..c348b16431237 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1039,33 +1039,33 @@ export class TerminalService implements ITerminalService { public async focusFindWidget(): Promise { await this.showPanel(false); - const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; - pane.focusFindWidget(); + // const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; + // pane.focusFindWidget(); this._findWidgetVisible.set(true); } public hideFindWidget(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - pane.hideFindWidget(); - this._findWidgetVisible.reset(); - pane.focus(); + // pane.hideFindWidget(); + // this._findWidgetVisible.reset(); + // pane.focus(); } } public findNext(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - pane.showFindWidget(); - pane.getFindWidget().find(false); + // pane.showFindWidget(); + // pane.getFindWidget().find(false); } } public findPrevious(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - pane.showFindWidget(); - pane.getFindWidget().find(true); + // pane.showFindWidget(); + // pane.getFindWidget().find(true); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts new file mode 100644 index 0000000000000..adc6d3c84d163 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; + +export class TerminalTabbedView extends Disposable { + private _splitView!: SplitView; + private _parentElement: HTMLElement; + private _terminalContainer: HTMLElement | undefined; + private _terminalTabTree: HTMLElement | undefined; + private _displayTabs: boolean; + constructor( + parentElement: HTMLElement, + @ITerminalService _terminalService: ITerminalService, + @IConfigurationService _configurationService: IConfigurationService + ) { + super(); + this._parentElement = parentElement; + this._displayTabs = _terminalService.configHelper.config.showTabs; + this._createSplitView(); + + _configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('terminal.integrated.showTabs')) { + this._displayTabs = _terminalService.configHelper.config.showTabs; + } + }); + } + + private _createSplitView(): void { + if (this._splitView) { + return; + } + this._splitView = new SplitView(this._parentElement, { orientation: Orientation.HORIZONTAL }); + + this._terminalContainer = document.createElement('div'); + this._terminalContainer.innerText = 'Hi'; + this._terminalTabTree = document.createElement('div'); + this._terminalTabTree.innerText = 'Hello'; + + if (this._displayTabs) { + this._splitView.addView({ + element: this._terminalTabTree, + layout: size => this._layout(size), + minimumSize: 220, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: () => Disposable.None + }, Sizing.Distribute); + } + this._splitView.addView({ + element: this._terminalContainer, + layout: size => this._layout(size), + minimumSize: 220, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: () => Disposable.None + }, Sizing.Distribute); + } + private _layout(size: number): void { + + } +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 68e168c45bd78..c50f37414dd27 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -10,7 +10,6 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, IColorTheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; -import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { configureTerminalSettingsTitle, selectDefaultProfileTitle, switchTerminalActionViewItemSeparator } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TERMINAL_BACKGROUND_COLOR, TERMINAL_BORDER_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; @@ -30,12 +29,13 @@ import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; +import { TerminalTabbedView } from 'vs/workbench/contrib/terminal/browser/terminalTabbedView'; export class TerminalViewPane extends ViewPane { private _actions: IAction[] | undefined; private _fontStyleElement: HTMLElement | undefined; private _parentDomElement: HTMLElement | undefined; - private _findWidget: TerminalFindWidget; + // private _findWidget: TerminalFindWidget; private _tabsViewWrapper: HTMLElement | undefined; private _tabsView: TabsView | undefined; private _terminalsInitialized = false; @@ -85,17 +85,17 @@ export class TerminalViewPane extends ViewPane { this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); this._fontStyleElement = document.createElement('style'); - this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); + // this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); if (!this.shouldShowWelcome()) { this._createTabsView(); } this._parentDomElement.appendChild(this._fontStyleElement); - this._parentDomElement.appendChild( - this._parentDomElement.appendChild(this._findWidget.getDomNode())); + // this._parentDomElement.appendChild( + // this._parentDomElement.appendChild(this._findWidget.getDomNode())); - this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); + // this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.fontFamily') || e.affectsConfiguration('editor.fontFamily')) { const configHelper = this._terminalService.configHelper; @@ -108,7 +108,7 @@ export class TerminalViewPane extends ViewPane { } } })); - this._updateTheme(); + // this._updateTheme(); this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { @@ -124,7 +124,7 @@ export class TerminalViewPane extends ViewPane { } } - this._updateTheme(); + // this._updateTheme(); if (hadTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } else { @@ -148,7 +148,7 @@ export class TerminalViewPane extends ViewPane { } this._tabsViewWrapper = document.createElement('div'); this._tabsViewWrapper.classList.add('tabs-view-wrapper'); - this.instantiationService.createInstance(TabsView, this._parentDomElement, this._findWidget); + this.instantiationService.createInstance(TerminalTabbedView, this._parentDomElement); this._parentDomElement.append(this._tabsViewWrapper); } @@ -192,40 +192,6 @@ export class TerminalViewPane extends ViewPane { this._terminalService.getActiveInstance()?.focusWhenReady(); } - public focusFindWidget() { - const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { - this._findWidget!.reveal(activeInstance.selection); - } else { - this._findWidget!.reveal(); - } - } - - public hideFindWidget() { - this._findWidget!.hide(); - } - - public showFindWidget() { - const activeInstance = this._terminalService.getActiveInstance(); - if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { - this._findWidget!.show(activeInstance.selection); - } else { - this._findWidget!.show(); - } - } - - public getFindWidget(): TerminalFindWidget { - return this._findWidget!; - } - - private _updateTheme(theme?: IColorTheme): void { - if (!theme) { - theme = this.themeService.getColorTheme(); - } - - this._findWidget?.updateTheme(theme); - } - shouldShowWelcome(): boolean { this._isWelcomeShowing = !this._terminalService.isProcessSupportRegistered && this._terminalService.terminalInstances.length === 0; return this._isWelcomeShowing; From 0e15648f9fc0db167ff986e3bb2bd5ed318fa54e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 16:33:01 -0700 Subject: [PATCH 23/49] clean up a bit --- .../terminal/browser/terminalTabbedView.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index adc6d3c84d163..2585fe5d9af65 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -35,23 +35,27 @@ export class TerminalTabbedView extends Disposable { if (this._splitView) { return; } + this._splitView = new SplitView(this._parentElement, { orientation: Orientation.HORIZONTAL }); + this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); this._terminalContainer = document.createElement('div'); - this._terminalContainer.innerText = 'Hi'; this._terminalTabTree = document.createElement('div'); - this._terminalTabTree.innerText = 'Hello'; + this._configureViews(); if (this._displayTabs) { + // show tab tree this._splitView.addView({ element: this._terminalTabTree, layout: size => this._layout(size), - minimumSize: 220, - maximumSize: Number.POSITIVE_INFINITY, + minimumSize: 100, + maximumSize: 220, onDidChange: () => Disposable.None }, Sizing.Distribute); } + this._splitView.addView({ + // always show terminals element: this._terminalContainer, layout: size => this._layout(size), minimumSize: 220, @@ -59,6 +63,12 @@ export class TerminalTabbedView extends Disposable { onDidChange: () => Disposable.None }, Sizing.Distribute); } + + private _configureViews(): void { + this._terminalContainer!.innerText = 'Hi'; + this._terminalTabTree!.innerText = 'Hello'; + } + private _layout(size: number): void { } From 6beb1b43af51720f17a1ead6a84ed9f266504501 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 6 Apr 2021 19:57:34 -0700 Subject: [PATCH 24/49] more splitView work --- .../contrib/terminal/browser/terminalTabbedView.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 2585fe5d9af65..2ac3cecb8e0f7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -10,6 +10,7 @@ import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal export class TerminalTabbedView extends Disposable { private _splitView!: SplitView; + private _width: number = 0; private _parentElement: HTMLElement; private _terminalContainer: HTMLElement | undefined; private _terminalTabTree: HTMLElement | undefined; @@ -22,6 +23,7 @@ export class TerminalTabbedView extends Disposable { super(); this._parentElement = parentElement; this._displayTabs = _terminalService.configHelper.config.showTabs; + this._width = this._parentElement.clientWidth; this._createSplitView(); _configurationService.onDidChangeConfiguration(e => { @@ -35,7 +37,6 @@ export class TerminalTabbedView extends Disposable { if (this._splitView) { return; } - this._splitView = new SplitView(this._parentElement, { orientation: Orientation.HORIZONTAL }); this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); @@ -48,8 +49,8 @@ export class TerminalTabbedView extends Disposable { this._splitView.addView({ element: this._terminalTabTree, layout: size => this._layout(size), - minimumSize: 100, - maximumSize: 220, + minimumSize: 600, + maximumSize: 800, onDidChange: () => Disposable.None }, Sizing.Distribute); } @@ -62,6 +63,8 @@ export class TerminalTabbedView extends Disposable { maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None }, Sizing.Distribute); + + this._splitView.layout(this._width); } private _configureViews(): void { From 08508fd2d51d2698e8a24e9d4360308c8588ad2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 7 Apr 2021 15:36:23 +0200 Subject: [PATCH 25/49] call TerminalTabbedView.layout --- .../terminal/browser/terminalTabbedView.ts | 24 ++++++++----------- .../contrib/terminal/browser/terminalView.ts | 4 +++- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 2ac3cecb8e0f7..b799cac9c1510 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -9,22 +9,20 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; export class TerminalTabbedView extends Disposable { + private _splitView!: SplitView; - private _width: number = 0; - private _parentElement: HTMLElement; private _terminalContainer: HTMLElement | undefined; private _terminalTabTree: HTMLElement | undefined; private _displayTabs: boolean; + constructor( parentElement: HTMLElement, @ITerminalService _terminalService: ITerminalService, @IConfigurationService _configurationService: IConfigurationService ) { super(); - this._parentElement = parentElement; this._displayTabs = _terminalService.configHelper.config.showTabs; - this._width = this._parentElement.clientWidth; - this._createSplitView(); + this._createSplitView(parentElement); _configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { @@ -33,11 +31,11 @@ export class TerminalTabbedView extends Disposable { }); } - private _createSplitView(): void { + private _createSplitView(parentElement: HTMLElement): void { if (this._splitView) { return; } - this._splitView = new SplitView(this._parentElement, { orientation: Orientation.HORIZONTAL }); + this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); this._terminalContainer = document.createElement('div'); @@ -48,9 +46,9 @@ export class TerminalTabbedView extends Disposable { // show tab tree this._splitView.addView({ element: this._terminalTabTree, - layout: size => this._layout(size), + layout: size => undefined, minimumSize: 600, - maximumSize: 800, + maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None }, Sizing.Distribute); } @@ -58,13 +56,11 @@ export class TerminalTabbedView extends Disposable { this._splitView.addView({ // always show terminals element: this._terminalContainer, - layout: size => this._layout(size), + layout: size => undefined, minimumSize: 220, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None }, Sizing.Distribute); - - this._splitView.layout(this._width); } private _configureViews(): void { @@ -72,7 +68,7 @@ export class TerminalTabbedView extends Disposable { this._terminalTabTree!.innerText = 'Hello'; } - private _layout(size: number): void { - + layout(width: number): void { + this._splitView.layout(width); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index c50f37414dd27..ecd448f564b31 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -37,6 +37,7 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; // private _findWidget: TerminalFindWidget; private _tabsViewWrapper: HTMLElement | undefined; + private _terminalTabbedView!: TerminalTabbedView; private _tabsView: TabsView | undefined; private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; @@ -148,7 +149,7 @@ export class TerminalViewPane extends ViewPane { } this._tabsViewWrapper = document.createElement('div'); this._tabsViewWrapper.classList.add('tabs-view-wrapper'); - this.instantiationService.createInstance(TerminalTabbedView, this._parentDomElement); + this._terminalTabbedView = this.instantiationService.createInstance(TerminalTabbedView, this._parentDomElement); this._parentDomElement.append(this._tabsViewWrapper); } @@ -160,6 +161,7 @@ export class TerminalViewPane extends ViewPane { if (this._tabsView) { this._tabsView.layout(width, height); } + this._terminalTabbedView.layout(width); } public getActionViewItem(action: Action): IActionViewItem | undefined { From 55b1c5bca8582e7f6d8fb812e7cfa77eeb4b131c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 07:11:45 -0700 Subject: [PATCH 26/49] sash working! --- .../terminal/browser/terminalTabbedView.ts | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index b799cac9c1510..888d5382b74c8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -13,7 +13,9 @@ export class TerminalTabbedView extends Disposable { private _splitView!: SplitView; private _terminalContainer: HTMLElement | undefined; private _terminalTabTree: HTMLElement | undefined; - private _displayTabs: boolean; + private _showTabs: boolean; + private TABS_WIDGET_INDEX: number; + private TERMINALS_INDEX: number; constructor( parentElement: HTMLElement, @@ -21,12 +23,16 @@ export class TerminalTabbedView extends Disposable { @IConfigurationService _configurationService: IConfigurationService ) { super(); - this._displayTabs = _terminalService.configHelper.config.showTabs; + this._showTabs = _terminalService.configHelper.config.showTabs; this._createSplitView(parentElement); + this.TABS_WIDGET_INDEX = 0; + this.TERMINALS_INDEX = 0; + _configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { - this._displayTabs = _terminalService.configHelper.config.showTabs; + this._showTabs = _terminalService.configHelper.config.showTabs; + this._updateVisibility(); } }); } @@ -42,19 +48,17 @@ export class TerminalTabbedView extends Disposable { this._terminalTabTree = document.createElement('div'); this._configureViews(); - if (this._displayTabs) { - // show tab tree - this._splitView.addView({ - element: this._terminalTabTree, - layout: size => undefined, - minimumSize: 600, - maximumSize: Number.POSITIVE_INFINITY, - onDidChange: () => Disposable.None - }, Sizing.Distribute); - } + this._splitView.addView({ + element: this._terminalTabTree, + layout: size => undefined, + minimumSize: 600, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: () => Disposable.None, + setVisible: () => this._showTabs + }, Sizing.Distribute); + this._splitView.addView({ - // always show terminals element: this._terminalContainer, layout: size => undefined, minimumSize: 220, @@ -63,6 +67,14 @@ export class TerminalTabbedView extends Disposable { }, Sizing.Distribute); } + private _updateVisibility() { + if (!this._splitView) { + return; + } + this._splitView.setViewVisible(this.TABS_WIDGET_INDEX, this._showTabs); + this._splitView.setViewVisible(this.TERMINALS_INDEX, true); + } + private _configureViews(): void { this._terminalContainer!.innerText = 'Hi'; this._terminalTabTree!.innerText = 'Hello'; From f8771f455c18c17ad8b78de35fbcc6a33da59c38 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 09:38:02 -0700 Subject: [PATCH 27/49] add Tabs Location --- .../terminal/browser/terminalTabbedView.ts | 36 ++++++++++--------- .../contrib/terminal/common/terminal.ts | 1 + .../terminal/common/terminalConfiguration.ts | 6 ++++ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 888d5382b74c8..091e29c6d76f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -14,8 +14,8 @@ export class TerminalTabbedView extends Disposable { private _terminalContainer: HTMLElement | undefined; private _terminalTabTree: HTMLElement | undefined; private _showTabs: boolean; - private TABS_WIDGET_INDEX: number; - private TERMINALS_INDEX: number; + private TAB_TREE_INDEX: number; + private TERMINAL_CONTAINER_INDEX: number; constructor( parentElement: HTMLElement, @@ -24,17 +24,20 @@ export class TerminalTabbedView extends Disposable { ) { super(); this._showTabs = _terminalService.configHelper.config.showTabs; - this._createSplitView(parentElement); - - this.TABS_WIDGET_INDEX = 0; - this.TERMINALS_INDEX = 0; + this.TAB_TREE_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this.TERMINAL_CONTAINER_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; _configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { this._showTabs = _terminalService.configHelper.config.showTabs; this._updateVisibility(); + } else if (e.affectsConfiguration('terminal.integrated.tabsLocation')) { + this.TAB_TREE_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this.TERMINAL_CONTAINER_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._splitView.swapViews(0, 1); } }); + this._createSplitView(parentElement); } private _createSplitView(parentElement: HTMLElement): void { @@ -51,33 +54,34 @@ export class TerminalTabbedView extends Disposable { this._splitView.addView({ element: this._terminalTabTree, layout: size => undefined, - minimumSize: 600, - maximumSize: Number.POSITIVE_INFINITY, + minimumSize: 80, + maximumSize: 300, onDidChange: () => Disposable.None, - setVisible: () => this._showTabs - }, Sizing.Distribute); + }, Sizing.Distribute, this.TAB_TREE_INDEX); this._splitView.addView({ element: this._terminalContainer, layout: size => undefined, - minimumSize: 220, + minimumSize: 800, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None - }, Sizing.Distribute); + }, Sizing.Distribute, this.TERMINAL_CONTAINER_INDEX); + + this._updateVisibility(); } private _updateVisibility() { if (!this._splitView) { return; } - this._splitView.setViewVisible(this.TABS_WIDGET_INDEX, this._showTabs); - this._splitView.setViewVisible(this.TERMINALS_INDEX, true); + this._splitView.setViewVisible(this.TAB_TREE_INDEX, this._showTabs); + this._splitView.setViewVisible(this.TERMINAL_CONTAINER_INDEX, true); } private _configureViews(): void { - this._terminalContainer!.innerText = 'Hi'; - this._terminalTabTree!.innerText = 'Hello'; + this._terminalTabTree!.innerText = 'Tab tree'; + this._terminalContainer!.innerText = 'Terminal container'; } layout(width: number): void { diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 6972444122218..3a0a3b688460e 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -104,6 +104,7 @@ export interface ITerminalConfiguration { profiles: ITerminalProfiles; useWslProfiles: boolean; showTabs: boolean; + tabsLocation: 'left' | 'right'; altClickMovesCursor: boolean; macOptionIsMeta: boolean; macOptionClickForcesSelection: boolean; diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index e786e63758b98..2ad20bbcc0072 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -235,6 +235,12 @@ export const terminalConfiguration: IConfigurationNode = { type: 'boolean', default: false }, + 'terminal.integrated.tabsLocation': { + 'type': 'string', + 'enum': ['left', 'right'], + 'default': 'left', + 'description': localize('sideBarLocation', "Controls the location of the terminal tabs, either left or right of the terminal container.") + }, 'terminal.integrated.macOptionIsMeta': { description: localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), type: 'boolean', From c2269e6f64cf6961bfefaa7771ccd993d4d15f33 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 10:54:45 -0700 Subject: [PATCH 28/49] get terminal container to show up --- .../terminal/browser/terminalService.ts | 1 + .../terminal/browser/terminalTabbedView.ts | 55 +++++++++++-------- .../contrib/terminal/browser/terminalView.ts | 6 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index c348b16431237..b03ddd0f7dc9f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1072,6 +1072,7 @@ export class TerminalService implements ITerminalService { public async setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): Promise { this._configHelper.panelContainer = panelContainer; this._terminalContainer = terminalContainer; + this._terminalTabs.forEach(tab => tab.attachToElement(terminalContainer)); } public hidePanel(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 091e29c6d76f1..0d21bc49248db 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -6,37 +6,54 @@ import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; export class TerminalTabbedView extends Disposable { private _splitView!: SplitView; - private _terminalContainer: HTMLElement | undefined; - private _terminalTabTree: HTMLElement | undefined; + private _terminalContainer: HTMLElement; + private _terminalTabTree: HTMLElement; private _showTabs: boolean; private TAB_TREE_INDEX: number; private TERMINAL_CONTAINER_INDEX: number; + private _tabsWidget: TerminalTabsWidget | undefined; + private _instantiationService: IInstantiationService; + private _terminalService: ITerminalService; + private _height: number | undefined; constructor( parentElement: HTMLElement, - @ITerminalService _terminalService: ITerminalService, - @IConfigurationService _configurationService: IConfigurationService + @ITerminalService terminalService: ITerminalService, + @IConfigurationService _configurationService: IConfigurationService, + @IInstantiationService instantiationService: IInstantiationService ) { super(); - this._showTabs = _terminalService.configHelper.config.showTabs; - this.TAB_TREE_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this.TERMINAL_CONTAINER_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._instantiationService = instantiationService; + this._showTabs = terminalService.configHelper.config.showTabs; + this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._terminalTabTree = document.createElement('div'); + this._terminalTabTree.classList.add('tabs-widget'); + this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); + this._terminalContainer = document.createElement('div'); + this._terminalContainer.classList.add('terminal-outer-container'); + this._terminalContainer.style.display = 'block'; + terminalService.onInstancesChanged(() => terminalService.setContainers(parentElement, this._terminalContainer)); + this._terminalService = terminalService; _configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { - this._showTabs = _terminalService.configHelper.config.showTabs; + this._showTabs = terminalService.configHelper.config.showTabs; this._updateVisibility(); } else if (e.affectsConfiguration('terminal.integrated.tabsLocation')) { - this.TAB_TREE_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this.TERMINAL_CONTAINER_INDEX = _terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; this._splitView.swapViews(0, 1); } }); + this._createSplitView(parentElement); } @@ -47,22 +64,19 @@ export class TerminalTabbedView extends Disposable { this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - this._terminalContainer = document.createElement('div'); - this._terminalTabTree = document.createElement('div'); - this._configureViews(); + this._tabsWidget = this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); this._splitView.addView({ element: this._terminalTabTree, - layout: size => undefined, - minimumSize: 80, + layout: width => this._tabsWidget!.layout(undefined, width), + minimumSize: 200, maximumSize: 300, onDidChange: () => Disposable.None, }, Sizing.Distribute, this.TAB_TREE_INDEX); - this._splitView.addView({ element: this._terminalContainer, - layout: size => undefined, + layout: width => this._terminalService.terminalTabs.forEach(tab => tab.layout(width, this._height || 0)), minimumSize: 800, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None @@ -79,12 +93,9 @@ export class TerminalTabbedView extends Disposable { this._splitView.setViewVisible(this.TERMINAL_CONTAINER_INDEX, true); } - private _configureViews(): void { - this._terminalTabTree!.innerText = 'Tab tree'; - this._terminalContainer!.innerText = 'Terminal container'; - } - layout(width: number): void { + layout(width: number, height: number): void { this._splitView.layout(width); + this._height = height; } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index ecd448f564b31..7a171c48f83fa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -158,10 +158,8 @@ export class TerminalViewPane extends ViewPane { this._bodyDimensions.width = width; this._bodyDimensions.height = height; - if (this._tabsView) { - this._tabsView.layout(width, height); - } - this._terminalTabbedView.layout(width); + + this._terminalTabbedView.layout(width, height); } public getActionViewItem(action: Action): IActionViewItem | undefined { From aecaf0a6ac6bc0b4f6fd58f91ca5d5aa0f9afc81 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 11:13:53 -0700 Subject: [PATCH 29/49] get widget to work --- .../contrib/terminal/browser/terminalTabbedView.ts | 13 +++++++++---- .../contrib/terminal/browser/terminalTabsWidget.ts | 8 ++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 0d21bc49248db..09a2365a3896f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -36,11 +36,17 @@ export class TerminalTabbedView extends Disposable { this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; this._terminalTabTree = document.createElement('div'); this._terminalTabTree.classList.add('tabs-widget'); - this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); + this._tabsWidget = this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); this._terminalContainer.style.display = 'block'; - terminalService.onInstancesChanged(() => terminalService.setContainers(parentElement, this._terminalContainer)); + terminalService.onInstanceCreated(() => { + this._tabsWidget?.rerender(); + }); + terminalService.onInstancesChanged(() => { + terminalService.setContainers(parentElement, this._terminalContainer); + this._tabsWidget?.rerender(); + }); this._terminalService = terminalService; _configurationService.onDidChangeConfiguration(e => { @@ -68,7 +74,7 @@ export class TerminalTabbedView extends Disposable { this._splitView.addView({ element: this._terminalTabTree, - layout: width => this._tabsWidget!.layout(undefined, width), + layout: width => this._tabsWidget!.layout(this._height, width), minimumSize: 200, maximumSize: 300, onDidChange: () => Disposable.None, @@ -93,7 +99,6 @@ export class TerminalTabbedView extends Disposable { this._splitView.setViewVisible(this.TERMINAL_CONTAINER_INDEX, true); } - layout(width: number, height: number): void { this._splitView.layout(width); this._height = height; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 6a6c7b1bacd8a..2bbb655f199e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -22,6 +22,7 @@ import { Codicon } from 'vs/base/common/codicons'; const $ = DOM.$; export class TerminalTabsWidget extends WorkbenchObjectTree { + private _terminalService: ITerminalService; constructor( container: HTMLElement, @IContextKeyService contextKeyService: IContextKeyService, @@ -66,6 +67,13 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } }); + this._terminalService = terminalService; + } + + rerender(): void { + this.setChildren(null, undefined); + const children = createTerminalTabsIterator(this._terminalService.terminalTabs); + this.setChildren(null, children); } } From c2075cd2a987b770c2fce3c496386a3c06c0232d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 11:44:14 -0700 Subject: [PATCH 30/49] revert some changes --- .vscode/settings.json | 1 + src/vs/workbench/contrib/terminal/browser/terminalTab.ts | 6 +----- .../contrib/terminal/browser/terminalTabbedView.ts | 9 ++++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index eabef1693e988..59910bd643070 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -86,4 +86,5 @@ }, "typescript.tsc.autoDetect": "off", "notebook.experimental.useMarkdownRenderer": true, + "testing.autoRun.mode": "onlyPreviouslyRun" } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index cc3ce5c5638bf..95599ea105f5a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -38,10 +38,6 @@ class SplitPaneContainer extends Disposable { this._splitView.layout(this.orientation === Orientation.HORIZONTAL ? this._width : this._height); } - public get splitView(): SplitView { - return this._splitView; - } - private _createSplitView(): void { this._splitView = new SplitView(this._container, { orientation: this.orientation }); this._splitViewDisposables.clear(); @@ -264,7 +260,7 @@ export class TerminalTab extends Disposable implements ITerminalTab { } this._activeInstanceIndex = 0; if (this._container) { - // this.attachToElement(this._container); + this.attachToElement(this._container); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 09a2365a3896f..f891038da5f61 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -40,13 +40,16 @@ export class TerminalTabbedView extends Disposable { this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); this._terminalContainer.style.display = 'block'; + + terminalService.setContainers(parentElement, this._terminalContainer); + terminalService.onInstanceCreated(() => { - this._tabsWidget?.rerender(); + this._tabsWidget!.rerender(); }); terminalService.onInstancesChanged(() => { - terminalService.setContainers(parentElement, this._terminalContainer); - this._tabsWidget?.rerender(); + this._tabsWidget!.rerender(); }); + this._terminalService = terminalService; _configurationService.onDidChangeConfiguration(e => { From ca0a583a1b42611a2835b2850c7807ddd4c179d0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 12:01:56 -0700 Subject: [PATCH 31/49] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../terminal/browser/terminalTabbedView.ts | 38 ++++++++++--------- .../terminal/browser/terminalTabsWidget.ts | 10 +++-- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index f891038da5f61..93cb7263f0e38 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -13,46 +13,50 @@ import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/termin export class TerminalTabbedView extends Disposable { private _splitView!: SplitView; + private _terminalContainer: HTMLElement; private _terminalTabTree: HTMLElement; - private _showTabs: boolean; + private _tabsWidget: TerminalTabsWidget | undefined; + private TAB_TREE_INDEX: number; private TERMINAL_CONTAINER_INDEX: number; - private _tabsWidget: TerminalTabsWidget | undefined; + + private _showTabs: boolean; + + private _height: number | undefined; + private _instantiationService: IInstantiationService; private _terminalService: ITerminalService; - private _height: number | undefined; constructor( parentElement: HTMLElement, @ITerminalService terminalService: ITerminalService, - @IConfigurationService _configurationService: IConfigurationService, + @IConfigurationService configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService ) { super(); + this._instantiationService = instantiationService; - this._showTabs = terminalService.configHelper.config.showTabs; - this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._terminalService = terminalService; + this._terminalTabTree = document.createElement('div'); this._terminalTabTree.classList.add('tabs-widget'); this._tabsWidget = this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); + this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); this._terminalContainer.style.display = 'block'; - terminalService.setContainers(parentElement, this._terminalContainer); + this._showTabs = terminalService.configHelper.config.showTabs; - terminalService.onInstanceCreated(() => { - this._tabsWidget!.rerender(); - }); - terminalService.onInstancesChanged(() => { - this._tabsWidget!.rerender(); - }); + this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; - this._terminalService = terminalService; + terminalService.setContainers(parentElement, this._terminalContainer); - _configurationService.onDidChangeConfiguration(e => { + terminalService.onInstancesChanged(() => this._tabsWidget!.rerender()); + + configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { this._showTabs = terminalService.configHelper.config.showTabs; this._updateVisibility(); @@ -73,8 +77,6 @@ export class TerminalTabbedView extends Disposable { this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - this._tabsWidget = this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); - this._splitView.addView({ element: this._terminalTabTree, layout: width => this._tabsWidget!.layout(this._height, width), diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 2bbb655f199e2..1c2a6b85f322e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -55,9 +55,6 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { keybindingService, accessibilityService, ); - this.setChildren(null, undefined); - const children = createTerminalTabsIterator(terminalService.terminalTabs); - this.setChildren(null, children); this.onDidChangeSelection(e => { if (e.elements && e.elements[0]) { if ('_instance' in e.elements[0]) { @@ -68,9 +65,14 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } }); this._terminalService = terminalService; + this._render(); + } + + public rerender(): void { + this._render(); } - rerender(): void { + private _render(): void { this.setChildren(null, undefined); const children = createTerminalTabsIterator(this._terminalService.terminalTabs); this.setChildren(null, children); From b4fe86328a96dbe1526a61ee0bde1f8ec146861f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 14:52:08 -0700 Subject: [PATCH 32/49] add some more checks --- src/vs/workbench/contrib/terminal/browser/terminalTab.ts | 3 ++- .../workbench/contrib/terminal/browser/terminalTabsWidget.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 95599ea105f5a..743a5470672bf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -343,10 +343,11 @@ export class TerminalTab extends Disposable implements ITerminalTab { } // Fire events and dispose tab if it was the last instance - this._onInstancesChanged.fire(); if (this._terminalInstances.length === 0) { this._onDisposed.fire(this); this.dispose(); + } else { + this._onInstancesChanged.fire(); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 1c2a6b85f322e..a17c8f1cedd70 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -74,6 +74,9 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { private _render(): void { this.setChildren(null, undefined); + if (!this._terminalService || this._terminalService.terminalTabs.length === 0) { + return; + } const children = createTerminalTabsIterator(this._terminalService.terminalTabs); this.setChildren(null, children); } @@ -131,7 +134,6 @@ class TerminalTabsRenderer implements ITreeRenderer 1 ? `Terminals (${item.children.length})` : item.children[0].instance.title : ''; } else if ('instance' in item) { label = item.instance.title; - icon = item.instance.icon; } template.labelElement.textContent = label; template.labelElement.title = label; From e6bf0c58615202f034e845a4efb5f844329233ba Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 15:25:24 -0700 Subject: [PATCH 33/49] feedback --- .../contrib/terminal/browser/terminalTabbedView.ts | 12 +++++------- .../contrib/terminal/common/terminalConfiguration.ts | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 93cb7263f0e38..a3175d83dfdee 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -12,7 +12,7 @@ import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/termin export class TerminalTabbedView extends Disposable { - private _splitView!: SplitView; + private _splitView: SplitView; private _terminalContainer: HTMLElement; private _terminalTabTree: HTMLElement; @@ -67,14 +67,12 @@ export class TerminalTabbedView extends Disposable { } }); - this._createSplitView(parentElement); + this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); + + this._setupSplitView(); } - private _createSplitView(parentElement: HTMLElement): void { - if (this._splitView) { - return; - } - this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); + private _setupSplitView(): void { this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); this._splitView.addView({ diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index d9710223d6414..a694c6b0876ed 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -261,7 +261,7 @@ export const terminalConfiguration: IConfigurationNode = { 'type': 'string', 'enum': ['left', 'right'], 'default': 'left', - 'description': localize('sideBarLocation', "Controls the location of the terminal tabs, either left or right of the terminal container.") + 'description': localize('tabsLocation', "Controls the location of the terminal tabs, either left or right of the terminal container.") }, 'terminal.integrated.macOptionIsMeta': { description: localize('terminal.integrated.macOptionIsMeta', "Controls whether to treat the option key as the meta key in the terminal on macOS."), From 6846fee42fabf304559007f267ec0bb818dce971 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Apr 2021 15:23:04 -0700 Subject: [PATCH 34/49] Prevent disposable store exception Co-authored-by: Megan Rogge --- .../workbench/contrib/terminal/browser/terminalTab.ts | 5 +++++ .../contrib/terminal/browser/terminalTabsWidget.ts | 10 ++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index 743a5470672bf..35f01ef3bfd19 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -404,6 +404,11 @@ export class TerminalTab extends Disposable implements ITerminalTab { } public get title(): string { + if (this._terminalInstances.length === 0) { + // Normally consumers should not call into title at all after the tab is disposed but + // this is required when the tab is used as part of a tree. + return ''; + } let title = this._titleWithConnectionStatus(this.terminalInstances[0]); for (let i = 1; i < this.terminalInstances.length; i++) { const instance = this.terminalInstances[i]; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index a17c8f1cedd70..ee13f9b13b9f2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -69,16 +69,14 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } public rerender(): void { + // TODO: Rerender isn't the best name, since ObjectTree.rerender already exists (which we + // may want to use to fix the below setChildren issue) this._render(); } private _render(): void { - this.setChildren(null, undefined); - if (!this._terminalService || this._terminalService.terminalTabs.length === 0) { - return; - } - const children = createTerminalTabsIterator(this._terminalService.terminalTabs); - this.setChildren(null, children); + // TODO: We don't want to be setting children to undefined - a terminal being killed should not remove focus in the tab view + this.setChildren(null, createTerminalTabsIterator(this._terminalService.terminalTabs)); } } From 5bcbaa2d825f950e42218f4559f2edfcb96891c2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Apr 2021 15:37:58 -0700 Subject: [PATCH 35/49] Rerender tabs on title change event Co-authored-by: Megan Rogge --- .../terminal/browser/terminalTabbedView.ts | 4 ++-- .../terminal/browser/terminalTabsWidget.ts | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index a3175d83dfdee..d5b9f27e2df2c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -54,8 +54,6 @@ export class TerminalTabbedView extends Disposable { terminalService.setContainers(parentElement, this._terminalContainer); - terminalService.onInstancesChanged(() => this._tabsWidget!.rerender()); - configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { this._showTabs = terminalService.configHelper.config.showTabs; @@ -63,6 +61,7 @@ export class TerminalTabbedView extends Disposable { } else if (e.affectsConfiguration('terminal.integrated.tabsLocation')) { this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + // TODO: if showTabs this._splitView.swapViews(0, 1); } }); @@ -98,6 +97,7 @@ export class TerminalTabbedView extends Disposable { if (!this._splitView) { return; } + // TODO: Instead of always having tab tree there, addView/removeView depending on the setting this._splitView.setViewVisible(this.TAB_TREE_INDEX, this._showTabs); this._splitView.setViewVisible(this.TERMINAL_CONTAINER_INDEX, true); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index ee13f9b13b9f2..fb1538b4dcfee 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -65,12 +65,10 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } }); this._terminalService = terminalService; - this._render(); - } - public rerender(): void { - // TODO: Rerender isn't the best name, since ObjectTree.rerender already exists (which we - // may want to use to fix the below setChildren issue) + terminalService.onInstancesChanged(() => this._render()); + terminalService.onInstanceTitleChanged(() => this._render()); + this._render(); } @@ -129,7 +127,13 @@ class TerminalTabsRenderer implements ITreeRenderer 1 ? `Terminals (${item.children.length})` : item.children[0].instance.title : ''; + label = item + ? item.children.length === 0 + ? 'Starting...' + : item?.children.length > 1 + ? `Terminals (${item.children.length})` + : item.children[0].instance.title + : ''; } else if ('instance' in item) { label = item.instance.title; } From c492147a5b23d80e4290a68b51380aec9a6454d6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Apr 2021 15:44:13 -0700 Subject: [PATCH 36/49] Simplify instance node Co-authored-by: Megan Rogge --- .../terminal/browser/terminalTabsWidget.ts | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index fb1538b4dcfee..124e27ae486c3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { IListService, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { DefaultStyleController, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; @@ -12,7 +11,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; @@ -35,7 +34,10 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { @IInstantiationService _instantiationService: IInstantiationService ) { super('TerminalTabsTree', container, - new TerminalTabsDelegate(), + { + getHeight: () => 24, + getTemplateId: () => 'terminal.tabs' + }, [new TerminalTabsRenderer()], { horizontalScrolling: false, @@ -57,10 +59,10 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { ); this.onDidChangeSelection(e => { if (e.elements && e.elements[0]) { - if ('_instance' in e.elements[0]) { - e.elements[0].instance.focus(true); - } else { + if ('tab' in e.elements[0]) { terminalService.setActiveTabByIndex(terminalService.terminalTabs.indexOf(e.elements[0].tab)); + } else { + e.elements[0].focus(true); } } }); @@ -78,14 +80,6 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } -class TerminalTabsDelegate implements IListVirtualDelegate { - getHeight(element: any): number { - return 24; - } - getTemplateId(element: any): string { - return 'terminal.tabs'; - } -} class TerminalTabsIdentityProvider implements IIdentityProvider { constructor() { } @@ -93,7 +87,7 @@ class TerminalTabsIdentityProvider implements IIdentityProvider { if ('tab' in element) { return element.tab.title; } else { - return element.instance.instanceId; + return element.instanceId; } } @@ -103,7 +97,7 @@ class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; } else { - return element.instance.title; + return element.title; } } @@ -132,10 +126,10 @@ class TerminalTabsRenderer implements ITreeRenderer 1 ? `Terminals (${item.children.length})` - : item.children[0].instance.title + : item.children[0].title : ''; - } else if ('instance' in item) { - label = item.instance.title; + } else { + label = item.title; } template.labelElement.textContent = label; template.labelElement.title = label; @@ -151,42 +145,27 @@ interface ITerminalTabEntryTemplate { icon?: Codicon; } -type TabTreeNode = TabTreeElement | TabTreeChild; +type TabTreeNode = TabTreeElement | ITerminalInstance; +// TODO: Remove in favor of ITerminalTab class TabTreeElement { private _tab: ITerminalTab; - private _children: TabTreeChild[]; + private _children: ITerminalInstance[]; constructor(tab: ITerminalTab) { this._tab = tab; - this._children = this._tab.terminalInstances.map(i => new TabTreeChild(i, this._tab)); + this._children = this._tab.terminalInstances; } get tab(): ITerminalTab { return this._tab; } - get children(): TabTreeChild[] { + get children(): ITerminalInstance[] { return this._children; } - set children(newChildren: TabTreeChild[]) { + set children(newChildren: ITerminalInstance[]) { this._children = newChildren; } } -class TabTreeChild { - private _instance: ITerminalInstance; - private _tab: ITerminalTab; - constructor(instance: ITerminalInstance, tab: ITerminalTab) { - this._instance = instance; - this._tab = tab; - } - get instance(): ITerminalInstance { - return this._instance; - } - get parent(): ITerminalTab { - return this._tab; - } -} - - function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { const result = tabs.map(tab => { const hasChildren = tab.terminalInstances.length > 1; @@ -201,7 +180,7 @@ function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> | undefined { +function getChildren(elt: TabTreeElement): Iterable> | undefined { if (elt.children.length > 1) { return elt.children.map(child => { return { From 3241e1ad249b3004396ea085cc5fe015e4087420 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Apr 2021 16:08:30 -0700 Subject: [PATCH 37/49] Improve tabbed view size ranges Co-authored-by: Megan Rogge --- .../contrib/terminal/browser/terminalTabbedView.ts | 6 +++--- .../contrib/terminal/browser/terminalTabsWidget.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index d5b9f27e2df2c..c6922c13b68e0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -77,15 +77,15 @@ export class TerminalTabbedView extends Disposable { this._splitView.addView({ element: this._terminalTabTree, layout: width => this._tabsWidget!.layout(this._height, width), - minimumSize: 200, - maximumSize: 300, + minimumSize: 40, + maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None, }, Sizing.Distribute, this.TAB_TREE_INDEX); this._splitView.addView({ element: this._terminalContainer, layout: width => this._terminalService.terminalTabs.forEach(tab => tab.layout(width, this._height || 0)), - minimumSize: 800, + minimumSize: 120, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None }, Sizing.Distribute, this.TERMINAL_CONTAINER_INDEX); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 124e27ae486c3..a7b79c1a0a2f6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -48,6 +48,7 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { filter: undefined, smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), multipleSelectionSupport: false, + // TODO: Add indent guides? expandOnlyOnTwistieClick: true }, contextKeyService, @@ -172,7 +173,7 @@ function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable Date: Wed, 7 Apr 2021 19:05:25 -0700 Subject: [PATCH 38/49] use TerminalTab --- .../terminal/browser/terminalTabsWidget.ts | 86 ++++++++----------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index a7b79c1a0a2f6..8153bf4352537 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -20,7 +20,7 @@ import { Codicon } from 'vs/base/common/codicons'; const $ = DOM.$; -export class TerminalTabsWidget extends WorkbenchObjectTree { +export class TerminalTabsWidget extends WorkbenchObjectTree { private _terminalService: ITerminalService; constructor( container: HTMLElement, @@ -60,8 +60,8 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { ); this.onDidChangeSelection(e => { if (e.elements && e.elements[0]) { - if ('tab' in e.elements[0]) { - terminalService.setActiveTabByIndex(terminalService.terminalTabs.indexOf(e.elements[0].tab)); + if ('terminalInstances' in e.elements[0]) { + terminalService.setActiveTabByIndex(terminalService.terminalTabs.indexOf(e.elements[0])); } else { e.elements[0].focus(true); } @@ -81,25 +81,31 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } } -class TerminalTabsIdentityProvider implements IIdentityProvider { +class TerminalTabsIdentityProvider implements IIdentityProvider { constructor() { } - getId(element: TabTreeNode): { toString(): string; } { - if ('tab' in element) { - return element.tab.title; + getId(element: ITabTreeNode): { toString(): string; } { + if ('terminalInstances' in element) { + return element.title; } else { return element.instanceId; } } } -class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { - getAriaLabel(element: TabTreeNode) { - if ('tab' in element) { - return element.tab ? element.tab.terminalInstances.length > 1 ? `Terminals (${element.tab.terminalInstances.length})` : element.tab.terminalInstances[0].title : ''; +class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { + getAriaLabel(node: ITabTreeNode) { + let label = ''; + if ('terminalInstances' in node) { + if (node.terminalInstances.length === 1) { + label = node.terminalInstances[0].title; + } else if (node.terminalInstances.length > 1) { + label = `Terminals (${node.terminalInstances.length})`; + } } else { - return element.title; + label = node.title; } + return label; } getWidgetAriaLabel() { @@ -107,7 +113,7 @@ class TerminalTabsAccessibilityProvider implements IListAccessibilityProvider { +class TerminalTabsRenderer implements ITreeRenderer { templateId = 'terminal.tabs'; @@ -117,24 +123,20 @@ class TerminalTabsRenderer implements ITreeRenderer, index: number, template: ITerminalTabEntryTemplate): void { + renderElement(node: ITreeNode, index: number, template: ITerminalTabEntryTemplate): void { let label = ''; - let icon; let item = node.element; - if ('children' in item) { - label = item - ? item.children.length === 0 - ? 'Starting...' - : item?.children.length > 1 - ? `Terminals (${item.children.length})` - : item.children[0].title - : ''; + if ('terminalInstances' in item) { + if (item.terminalInstances.length === 1) { + label = item.terminalInstances[0].title; + } else if (item.terminalInstances.length > 1) { + label = `Terminals (${item.terminalInstances.length})`; + } } else { label = item.title; } template.labelElement.textContent = label; template.labelElement.title = label; - template.icon = icon; } disposeTemplate(templateData: ITerminalTabEntryTemplate): void { @@ -146,46 +148,26 @@ interface ITerminalTabEntryTemplate { icon?: Codicon; } -type TabTreeNode = TabTreeElement | ITerminalInstance; - -// TODO: Remove in favor of ITerminalTab -class TabTreeElement { - private _tab: ITerminalTab; - private _children: ITerminalInstance[]; - constructor(tab: ITerminalTab) { - this._tab = tab; - this._children = this._tab.terminalInstances; - } - get tab(): ITerminalTab { - return this._tab; - } - get children(): ITerminalInstance[] { - return this._children; - } - set children(newChildren: ITerminalInstance[]) { - this._children = newChildren; - } -} +type ITabTreeNode = ITerminalTab | ITerminalInstance; -function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { +function createTerminalTabsIterator(tabs: ITerminalTab[]): Iterable> { const result = tabs.map(tab => { const hasChildren = tab.terminalInstances.length > 1; - const elt = new TabTreeElement(tab); return { - element: elt, + element: tab, collapsed: false, collapsible: hasChildren, - children: getChildren(elt) + children: getChildren(tab) }; }); return result; } -function getChildren(elt: TabTreeElement): Iterable> | undefined { - if (elt.children.length > 1) { - return elt.children.map(child => { +function getChildren(tab: ITerminalTab): Iterable> | undefined { + if (tab.terminalInstances.length > 1) { + return tab.terminalInstances.map(instance => { return { - element: child, + element: instance, collapsed: true, collapsible: false, }; From e6c32c3d74322edeae3adaa3ffffd2ebc8b1b7eb Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 19:07:20 -0700 Subject: [PATCH 39/49] create issue to track indent guides todo --- src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index 8153bf4352537..afe69c92b632f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -48,7 +48,6 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { filter: undefined, smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), multipleSelectionSupport: false, - // TODO: Add indent guides? expandOnlyOnTwistieClick: true }, contextKeyService, @@ -76,7 +75,6 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { } private _render(): void { - // TODO: We don't want to be setting children to undefined - a terminal being killed should not remove focus in the tab view this.setChildren(null, createTerminalTabsIterator(this._terminalService.terminalTabs)); } } From 2c77e72f8b36d1981fd00ddb5d4898101d603716 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 19:13:02 -0700 Subject: [PATCH 40/49] add or remove view depending on showTabs --- .../terminal/browser/terminalTabbedView.ts | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index c6922c13b68e0..6145149f56ef4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -18,8 +18,8 @@ export class TerminalTabbedView extends Disposable { private _terminalTabTree: HTMLElement; private _tabsWidget: TerminalTabsWidget | undefined; - private TAB_TREE_INDEX: number; - private TERMINAL_CONTAINER_INDEX: number; + private _tabTreeIndex: number; + private _terminalContainerIndex: number; private _showTabs: boolean; @@ -49,20 +49,31 @@ export class TerminalTabbedView extends Disposable { this._showTabs = terminalService.configHelper.config.showTabs; - this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._tabTreeIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this._terminalContainerIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; terminalService.setContainers(parentElement, this._terminalContainer); configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { this._showTabs = terminalService.configHelper.config.showTabs; - this._updateVisibility(); + if (this._showTabs) { + this._splitView.addView({ + element: this._terminalTabTree, + layout: width => this._tabsWidget!.layout(this._height, width), + minimumSize: 40, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: () => Disposable.None, + }, Sizing.Distribute, this._tabTreeIndex); + } else { + this._splitView.removeView(this._tabTreeIndex); + } } else if (e.affectsConfiguration('terminal.integrated.tabsLocation')) { - this.TAB_TREE_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this.TERMINAL_CONTAINER_INDEX = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; - // TODO: if showTabs - this._splitView.swapViews(0, 1); + this._tabTreeIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this._terminalContainerIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + if (this._showTabs) { + this._splitView.swapViews(0, 1); + } } }); @@ -74,32 +85,23 @@ export class TerminalTabbedView extends Disposable { private _setupSplitView(): void { this._register(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - this._splitView.addView({ - element: this._terminalTabTree, - layout: width => this._tabsWidget!.layout(this._height, width), - minimumSize: 40, - maximumSize: Number.POSITIVE_INFINITY, - onDidChange: () => Disposable.None, - }, Sizing.Distribute, this.TAB_TREE_INDEX); - + if (this._showTabs) { + this._splitView.addView({ + element: this._terminalTabTree, + layout: width => this._tabsWidget!.layout(this._height, width), + minimumSize: 40, + maximumSize: Number.POSITIVE_INFINITY, + onDidChange: () => Disposable.None, + }, Sizing.Distribute, this._tabTreeIndex); + } this._splitView.addView({ element: this._terminalContainer, layout: width => this._terminalService.terminalTabs.forEach(tab => tab.layout(width, this._height || 0)), minimumSize: 120, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None - }, Sizing.Distribute, this.TERMINAL_CONTAINER_INDEX); + }, Sizing.Distribute, this._terminalContainerIndex); - this._updateVisibility(); - } - - private _updateVisibility() { - if (!this._splitView) { - return; - } - // TODO: Instead of always having tab tree there, addView/removeView depending on the setting - this._splitView.setViewVisible(this.TAB_TREE_INDEX, this._showTabs); - this._splitView.setViewVisible(this.TERMINAL_CONTAINER_INDEX, true); } layout(width: number, height: number): void { From 0b89eae87f768ec20734418346deb9f6d3ec12cc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 19:35:23 -0700 Subject: [PATCH 41/49] add a bunch of stuff from terminalView --- .../terminal/browser/terminalTabbedView.ts | 168 +++++++++++++++++- .../contrib/terminal/browser/terminalView.ts | 7 - 2 files changed, 167 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 6145149f56ef4..5ca26e24de641 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -8,7 +8,22 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; +import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; +import { isLinux, isMacintosh } from 'vs/base/common/platform'; +import * as dom from 'vs/base/browser/dom'; +import { BrowserFeatures } from 'vs/base/browser/canIUse'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { DataTransfers } from 'vs/base/browser/dnd'; +import { URI } from 'vs/base/common/uri'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IAction } from 'vs/base/common/actions'; +import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +const FIND_FOCUS_CLASS = 'find-focused'; export class TerminalTabbedView extends Disposable { @@ -16,7 +31,10 @@ export class TerminalTabbedView extends Disposable { private _terminalContainer: HTMLElement; private _terminalTabTree: HTMLElement; + private _parentElement: HTMLElement; + private _tabsWidget: TerminalTabsWidget | undefined; + private _findWidget: TerminalFindWidget; private _tabTreeIndex: number; private _terminalContainerIndex: number; @@ -27,21 +45,38 @@ export class TerminalTabbedView extends Disposable { private _instantiationService: IInstantiationService; private _terminalService: ITerminalService; + private _themeService: IThemeService; + private _contextMenuService: IContextMenuService; + + private _cancelContextMenu: boolean = false; + private _menu: IMenu; constructor( parentElement: HTMLElement, @ITerminalService terminalService: ITerminalService, + @IThemeService themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @INotificationService private readonly _notificationService: INotificationService, + @IContextKeyService _contextKeyService: IContextKeyService, + @IContextKeyService contextMenuService: IContextMenuService, + @IMenuService _menuService: IMenuService ) { super(); this._instantiationService = instantiationService; this._terminalService = terminalService; + this._themeService = themeService; + this._contextMenuService = contextMenuService; + + this._parentElement = parentElement; this._terminalTabTree = document.createElement('div'); this._terminalTabTree.classList.add('tabs-widget'); + this._tabsWidget = this._instantiationService.createInstance(TerminalTabsWidget, this._terminalTabTree); + this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); + parentElement.appendChild(this._findWidget.getDomNode()); this._terminalContainer = document.createElement('div'); this._terminalContainer.classList.add('terminal-outer-container'); @@ -52,6 +87,8 @@ export class TerminalTabbedView extends Disposable { this._tabTreeIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; this._terminalContainerIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); + terminalService.setContainers(parentElement, this._terminalContainer); configurationService.onDidChangeConfiguration(e => { @@ -77,6 +114,13 @@ export class TerminalTabbedView extends Disposable { } }); + this._register(themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); + this._updateTheme(); + + this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); + + this._attachEventListeners(parentElement, this._terminalContainer); + this._splitView = new SplitView(parentElement, { orientation: Orientation.HORIZONTAL }); this._setupSplitView(); @@ -108,4 +152,126 @@ export class TerminalTabbedView extends Disposable { this._splitView.layout(width); this._height = height; } + + private _updateTheme(theme?: IColorTheme): void { + if (!theme) { + theme = this._themeService.getColorTheme(); + } + + this._findWidget?.updateTheme(theme); + } + + private _attachEventListeners(parentDomElement: HTMLElement, terminalContainer: HTMLElement): void { + this._register(dom.addDisposableListener(parentDomElement, 'mousedown', async (event: MouseEvent) => { + if (this._terminalService.terminalInstances.length === 0) { + return; + } + + if (event.which === 2 && isLinux) { + // Drop selection and focus terminal on Linux to enable middle button paste when click + // occurs on the selection itself. + const terminal = this._terminalService.getActiveInstance(); + if (terminal) { + terminal.focus(); + } + } else if (event.which === 3) { + const rightClickBehavior = this._terminalService.configHelper.config.rightClickBehavior; + if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') { + const terminal = this._terminalService.getActiveInstance(); + if (!terminal) { + return; + } + + // copyPaste: Shift+right click should open context menu + if (rightClickBehavior === 'copyPaste' && event.shiftKey) { + this._openContextMenu(event); + return; + } + + if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) { + await terminal.copySelection(); + terminal.clearSelection(); + } else { + if (BrowserFeatures.clipboard.readText) { + terminal.paste(); + } else { + this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); + } + } + // Clear selection after all click event bubbling is finished on Mac to prevent + // right-click selecting a word which is seemed cannot be disabled. There is a + // flicker when pasting but this appears to give the best experience if the + // setting is enabled. + if (isMacintosh) { + setTimeout(() => { + terminal.clearSelection(); + }, 0); + } + this._cancelContextMenu = true; + } + } + })); + this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => { + if (!this._cancelContextMenu) { + this._openContextMenu(event); + } + event.preventDefault(); + event.stopImmediatePropagation(); + this._cancelContextMenu = false; + })); + this._register(dom.addDisposableListener(document, 'keydown', (event: KeyboardEvent) => { + terminalContainer.classList.toggle('alt-active', !!event.altKey); + })); + this._register(dom.addDisposableListener(document, 'keyup', (event: KeyboardEvent) => { + terminalContainer.classList.toggle('alt-active', !!event.altKey); + })); + this._register(dom.addDisposableListener(parentDomElement, 'keyup', (event: KeyboardEvent) => { + if (event.keyCode === 27) { + // Keep terminal open on escape + event.stopPropagation(); + } + })); + this._register(dom.addDisposableListener(parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { + if (e.target === this._parentElement || dom.isAncestor(e.target as HTMLElement, parentDomElement)) { + if (!e.dataTransfer) { + return; + } + + // Check if files were dragged from the tree explorer + let path: string | undefined; + const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); + if (resources) { + path = URI.parse(JSON.parse(resources)[0]).fsPath; + } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { + // Check if the file was dragged from the filesystem + path = URI.file(e.dataTransfer.files[0].path).fsPath; + } + + if (!path) { + return; + } + + const terminal = this._terminalService.getActiveInstance(); + if (terminal) { + const preparedPath = await this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title, terminal.shellType); + terminal.sendText(preparedPath, false); + terminal.focus(); + } + } + })); + } + private _openContextMenu(event: MouseEvent): void { + const standardEvent = new StandardMouseEvent(event); + const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; + + const actions: IAction[] = []; + const actionsDisposable = createAndFillInContextMenuActions(this._menu, undefined, actions); + + this._contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => actions, + getActionsContext: () => this._parentElement, + onHide: () => actionsDisposable.dispose() + }); + } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 7a171c48f83fa..cf85a97988c89 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -35,7 +35,6 @@ export class TerminalViewPane extends ViewPane { private _actions: IAction[] | undefined; private _fontStyleElement: HTMLElement | undefined; private _parentDomElement: HTMLElement | undefined; - // private _findWidget: TerminalFindWidget; private _tabsViewWrapper: HTMLElement | undefined; private _terminalTabbedView!: TerminalTabbedView; private _tabsView: TabsView | undefined; @@ -86,17 +85,13 @@ export class TerminalViewPane extends ViewPane { this._parentDomElement = container; this._parentDomElement.classList.add('integrated-terminal'); this._fontStyleElement = document.createElement('style'); - // this._findWidget = this._instantiationService.createInstance(TerminalFindWidget, this._terminalService.getFindState()); if (!this.shouldShowWelcome()) { this._createTabsView(); } this._parentDomElement.appendChild(this._fontStyleElement); - // this._parentDomElement.appendChild( - // this._parentDomElement.appendChild(this._findWidget.getDomNode())); - // this._register(this.themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.fontFamily') || e.affectsConfiguration('editor.fontFamily')) { const configHelper = this._terminalService.configHelper; @@ -109,7 +104,6 @@ export class TerminalViewPane extends ViewPane { } } })); - // this._updateTheme(); this._register(this.onDidChangeBodyVisibility(visible => { if (visible) { @@ -125,7 +119,6 @@ export class TerminalViewPane extends ViewPane { } } - // this._updateTheme(); if (hadTerminals) { this._terminalService.getActiveTab()?.setVisible(visible); } else { From 494536b12e77459f99ddea69a28c3edf005ce70d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 19:46:25 -0700 Subject: [PATCH 42/49] find widget --- .../contrib/terminal/browser/tabsView.ts | 266 ------------------ .../terminal/browser/terminalService.ts | 21 +- .../terminal/browser/terminalTabbedView.ts | 58 +++- .../contrib/terminal/browser/terminalView.ts | 5 +- 4 files changed, 66 insertions(+), 284 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/browser/tabsView.ts diff --git a/src/vs/workbench/contrib/terminal/browser/tabsView.ts b/src/vs/workbench/contrib/terminal/browser/tabsView.ts deleted file mode 100644 index ee41e2112b5e7..0000000000000 --- a/src/vs/workbench/contrib/terminal/browser/tabsView.ts +++ /dev/null @@ -1,266 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { Event } from 'vs/base/common/event'; -import { IView } from 'vs/workbench/common/views'; -import { IProgressIndicator } from 'vs/platform/progress/common/progress'; -import * as dom from 'vs/base/browser/dom'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IAction } from 'vs/base/common/actions'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { DataTransfers } from 'vs/base/browser/dnd'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { URI } from 'vs/base/common/uri'; -import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; - -const FIND_FOCUS_CLASS = 'find-focused'; - -export class TabsView extends Disposable { - private _menu: IMenu; - _width: number; - _height: number; - private _cancelContextMenu: boolean = false; - private _tabsElement: HTMLElement; - private _splitView!: SplitView; - private readonly _splitViewDisposables = this._register(new DisposableStore()); - private _children: SplitTabsPane[] = []; - private _terminalContainer: HTMLElement; - private _onDidChange: Event = Event.None; - public get onDidChange(): Event { return this._onDidChange; } - - constructor( - private _parentDomElement: HTMLElement, - _findWidget: TerminalFindWidget, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITerminalService private readonly _terminalService: ITerminalService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IContextKeyService _contextKeyService: IContextKeyService, - @IMenuService _menuService: IMenuService, - @INotificationService private readonly _notificationService: INotificationService, - ) { - super(); - this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); - this._tabsElement = document.createElement('div'); - this._tabsElement.classList.add('tabs-widget'); - this._instantiationService.createInstance(TerminalTabsWidget, this._tabsElement); - - this._width = _parentDomElement.offsetWidth; - this._height = _parentDomElement.offsetHeight; - - this._terminalContainer = document.createElement('div'); - this._terminalContainer.classList.add('terminal-outer-container'); - this._terminalContainer.style.display = 'block'; - this._terminalService.setContainers(this._terminalContainer, this._parentDomElement); - this._attachEventListeners(this._parentDomElement, this._terminalContainer); - this._createSplitView(); - _findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS)); - } - - public get splitView(): SplitView { - return this._splitView; - } - - private _createSplitView(): void { - if (this._splitView) { - return; - } - this._splitView = new SplitView(this._parentDomElement, { orientation: Orientation.HORIZONTAL }); - this._splitViewDisposables.clear(); - this._splitViewDisposables.add(this._splitView.onDidSashReset(() => this._splitView.distributeViewSizes())); - const showTabs = this._terminalService.configHelper.config.showTabs; - const tabsWidgetWidth = showTabs ? 200 : 0; - if (showTabs) { - this._splitView.addView(new SplitTabsPane(this._tabsElement, tabsWidgetWidth, this._terminalService), tabsWidgetWidth, 0); - } - const tabContainer = new SplitTabsPane(this._terminalContainer, this._width - tabsWidgetWidth, this._terminalService); - this._splitView.addView(tabContainer, this._width - tabsWidgetWidth, 1); - this._terminalService.terminalTabs.forEach(tab => tab.attachToElement(tabContainer.element)); - } - - public layout(width: number, height: number): void { - this._width = width; - this._height = height; - this._children.forEach(c => c.orthogonalLayout(width)); - this._splitView.layout(width); - } - - private _attachEventListeners(parentDomElement: HTMLElement, terminalContainer: HTMLElement): void { - this._register(dom.addDisposableListener(parentDomElement, 'mousedown', async (event: MouseEvent) => { - if (this._terminalService.terminalInstances.length === 0) { - return; - } - - if (event.which === 2 && isLinux) { - // Drop selection and focus terminal on Linux to enable middle button paste when click - // occurs on the selection itself. - const terminal = this._terminalService.getActiveInstance(); - if (terminal) { - terminal.focus(); - } - } else if (event.which === 3) { - const rightClickBehavior = this._terminalService.configHelper.config.rightClickBehavior; - if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') { - const terminal = this._terminalService.getActiveInstance(); - if (!terminal) { - return; - } - - // copyPaste: Shift+right click should open context menu - if (rightClickBehavior === 'copyPaste' && event.shiftKey) { - this._openContextMenu(event); - return; - } - - if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) { - await terminal.copySelection(); - terminal.clearSelection(); - } else { - if (BrowserFeatures.clipboard.readText) { - terminal.paste(); - } else { - this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${isMacintosh ? '⌘' : 'Ctrl'}+V instead.`); - } - } - // Clear selection after all click event bubbling is finished on Mac to prevent - // right-click selecting a word which is seemed cannot be disabled. There is a - // flicker when pasting but this appears to give the best experience if the - // setting is enabled. - if (isMacintosh) { - setTimeout(() => { - terminal.clearSelection(); - }, 0); - } - this._cancelContextMenu = true; - } - } - })); - this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => { - if (!this._cancelContextMenu) { - this._openContextMenu(event); - } - event.preventDefault(); - event.stopImmediatePropagation(); - this._cancelContextMenu = false; - })); - this._register(dom.addDisposableListener(document, 'keydown', (event: KeyboardEvent) => { - terminalContainer.classList.toggle('alt-active', !!event.altKey); - })); - this._register(dom.addDisposableListener(document, 'keyup', (event: KeyboardEvent) => { - terminalContainer.classList.toggle('alt-active', !!event.altKey); - })); - this._register(dom.addDisposableListener(parentDomElement, 'keyup', (event: KeyboardEvent) => { - if (event.keyCode === 27) { - // Keep terminal open on escape - event.stopPropagation(); - } - })); - this._register(dom.addDisposableListener(parentDomElement, dom.EventType.DROP, async (e: DragEvent) => { - if (e.target === this._parentDomElement || dom.isAncestor(e.target as HTMLElement, parentDomElement)) { - if (!e.dataTransfer) { - return; - } - - // Check if files were dragged from the tree explorer - let path: string | undefined; - const resources = e.dataTransfer.getData(DataTransfers.RESOURCES); - if (resources) { - path = URI.parse(JSON.parse(resources)[0]).fsPath; - } else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) { - // Check if the file was dragged from the filesystem - path = URI.file(e.dataTransfer.files[0].path).fsPath; - } - - if (!path) { - return; - } - - const terminal = this._terminalService.getActiveInstance(); - if (terminal) { - const preparedPath = await this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title, terminal.shellType); - terminal.sendText(preparedPath, false); - terminal.focus(); - } - } - })); - } - private _openContextMenu(event: MouseEvent): void { - const standardEvent = new StandardMouseEvent(event); - const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; - - const actions: IAction[] = []; - const actionsDisposable = createAndFillInContextMenuActions(this._menu, undefined, actions); - - this._contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => actions, - getActionsContext: () => this._parentDomElement, - onHide: () => actionsDisposable.dispose() - }); - } -} -class SplitTabsPane implements IView { - public minimumSize: number = 120; - public maximumSize: number = Number.MAX_VALUE; - - public orientation: Orientation | undefined; - - private _onDidChange: Event = Event.None; - public get onDidChange(): Event { return this._onDidChange; } - - readonly element: HTMLElement; - - constructor( - readonly item: HTMLElement, - public height: number, - @ITerminalService _terminalService: ITerminalService - ) { - this.element = document.createElement('div'); - this.element.className = 'terminal-tabs-split-pane'; - this.element.appendChild(item); - } - id: string = 'split-tabs-view'; - focus(): void { - throw new Error('Method not implemented.'); - } - isVisible(): boolean { - throw new Error('Method not implemented.'); - } - isBodyVisible(): boolean { - throw new Error('Method not implemented.'); - } - setExpanded(expanded: boolean): boolean { - throw new Error('Method not implemented.'); - } - getProgressIndicator(): IProgressIndicator | undefined { - throw new Error('Method not implemented.'); - } - - public layout(size: number): void { - // Only layout when both sizes are known - if (!size || !this.height) { - return; - } - - // if (this._item.classList.contains('tabs-widget')) { - - // } else { - // // this._terminalService.terminalTabs.forEach(t => t.layout(size, this.height)); - // } - } - - public orthogonalLayout(size: number): void { - this.height = size; - } -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index def6b896a70f3..2dc37d78b3afd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -25,7 +25,7 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { TerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalTab } from 'vs/workbench/contrib/terminal/browser/terminalTab'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { IAvailableProfilesRequest, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID, ITerminalProfileObject, ITerminalExecutable, ITerminalProfileSource, ITerminalTypeContribution } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IAvailableProfilesRequest, IRemoteTerminalAttachTarget, ITerminalProfile, IStartExtensionTerminalRequest, ITerminalConfigHelper, ITerminalNativeWindowsDelegate, ITerminalProcessExtHostProxy, KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, KEYBINDING_CONTEXT_TERMINAL_PROCESS_SUPPORTED, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE, LinuxDistro, TERMINAL_VIEW_ID, ITerminalProfileObject, ITerminalExecutable, ITerminalProfileSource, ITerminalTypeContribution } from 'vs/workbench/contrib/terminal/common/terminal'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -50,7 +50,6 @@ export class TerminalService implements ITerminalService { private _terminalFocusContextKey: IContextKey; private _terminalShellTypeContextKey: IContextKey; private _terminalAltBufferActiveContextKey: IContextKey; - private _findWidgetVisible: IContextKey; private _terminalTabs: ITerminalTab[] = []; private _backgroundedTerminalInstances: ITerminalInstance[] = []; private get _terminalInstances(): ITerminalInstance[] { @@ -143,7 +142,6 @@ export class TerminalService implements ITerminalService { this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); this._terminalShellTypeContextKey = KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE.bindTo(this._contextKeyService); this._terminalAltBufferActiveContextKey = KEYBINDING_CONTEXT_TERMINAL_ALT_BUFFER_ACTIVE.bindTo(this._contextKeyService); - this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(this._contextKeyService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper); this.onTabDisposed(tab => this._removeTab(tab)); this.onActiveTabChanged(() => { @@ -1040,33 +1038,30 @@ export class TerminalService implements ITerminalService { public async focusFindWidget(): Promise { await this.showPanel(false); - // const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; - // pane.focusFindWidget(); - this._findWidgetVisible.set(true); + const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; + pane.terminalTabbedView.focusFindWidget(); } public hideFindWidget(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - // pane.hideFindWidget(); - // this._findWidgetVisible.reset(); - // pane.focus(); + pane.terminalTabbedView.hideFindWidget(); } } public findNext(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - // pane.showFindWidget(); - // pane.getFindWidget().find(false); + pane.terminalTabbedView.showFindWidget(); + pane.terminalTabbedView.getFindWidget().find(false); } } public findPrevious(): void { const pane = this._viewsService.getActiveViewWithId(TERMINAL_VIEW_ID) as TerminalViewPane; if (pane) { - // pane.showFindWidget(); - // pane.getFindWidget().find(true); + pane.terminalTabbedView.showFindWidget(); + pane.terminalTabbedView.getFindWidget().find(true); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 5ca26e24de641..55776650710cf 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -7,7 +7,7 @@ import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/spl import { Disposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, TerminalConnectionState } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminal/browser/terminalFindWidget'; import { TerminalTabsWidget } from 'vs/workbench/contrib/terminal/browser/terminalTabsWidget'; import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; @@ -21,8 +21,9 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IAction } from 'vs/base/common/actions'; import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE } from 'vs/workbench/contrib/terminal/common/terminal'; const FIND_FOCUS_CLASS = 'find-focused'; export class TerminalTabbedView extends Disposable { @@ -40,6 +41,7 @@ export class TerminalTabbedView extends Disposable { private _terminalContainerIndex: number; private _showTabs: boolean; + private _findWidgetVisible: IContextKey; private _height: number | undefined; @@ -89,6 +91,8 @@ export class TerminalTabbedView extends Disposable { this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); + this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(_contextKeyService); + terminalService.setContainers(parentElement, this._terminalContainer); configurationService.onDidChangeConfiguration(e => { @@ -274,4 +278,54 @@ export class TerminalTabbedView extends Disposable { onHide: () => actionsDisposable.dispose() }); } + + public focusFindWidget() { + this._findWidgetVisible.set(true); + const activeInstance = this._terminalService.getActiveInstance(); + if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { + this._findWidget!.reveal(activeInstance.selection); + } else { + this._findWidget!.reveal(); + } + } + + public hideFindWidget() { + this._findWidgetVisible.reset(); + this.focus(); + this._findWidget!.hide(); + } + + public showFindWidget() { + const activeInstance = this._terminalService.getActiveInstance(); + if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) { + this._findWidget!.show(activeInstance.selection); + } else { + this._findWidget!.show(); + } + } + + public getFindWidget(): TerminalFindWidget { + return this._findWidget!; + } + public focus() { + if (this._terminalService.connectionState === TerminalConnectionState.Connecting) { + // If the terminal is waiting to reconnect to remote terminals, then there is no TerminalInstance yet that can + // be focused. So wait for connection to finish, then focus. + const activeElement = document.activeElement; + this._register(this._terminalService.onDidChangeConnectionState(() => { + // Only focus the terminal if the activeElement has not changed since focus() was called + // TODO hack + if (document.activeElement === activeElement) { + this._focus(); + } + })); + + return; + } + this._focus(); + } + + private _focus() { + this._terminalService.getActiveInstance()?.focusWhenReady(); + } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index cf85a97988c89..91ddb5992e81a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -28,7 +28,6 @@ import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/c import { selectBorder } from 'vs/platform/theme/common/colorRegistry'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { TabsView } from 'vs/workbench/contrib/terminal/browser/tabsView'; import { TerminalTabbedView } from 'vs/workbench/contrib/terminal/browser/terminalTabbedView'; export class TerminalViewPane extends ViewPane { @@ -37,7 +36,7 @@ export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; private _tabsViewWrapper: HTMLElement | undefined; private _terminalTabbedView!: TerminalTabbedView; - private _tabsView: TabsView | undefined; + public get terminalTabbedView(): TerminalTabbedView { return this._terminalTabbedView; } private _terminalsInitialized = false; private _bodyDimensions: { width: number, height: number } = { width: 0, height: 0 }; private _isWelcomeShowing: boolean = false; @@ -72,7 +71,7 @@ export class TerminalViewPane extends ViewPane { } this._isWelcomeShowing = true; this._onDidChangeViewWelcomeState.fire(); - if (!this._tabsView && this._parentDomElement) { + if (!this._terminalTabbedView && this._parentDomElement) { this._createTabsView(); this.layoutBody(this._parentDomElement.offsetHeight, this._parentDomElement.offsetWidth); } From 77d5d2c7c2e85f2e1bee3270af0d895c4e211ec4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 20:13:13 -0700 Subject: [PATCH 43/49] revert some changes --- .vscode/settings.json | 3 +-- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 59910bd643070..02272c796dad0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -85,6 +85,5 @@ "editor.formatOnSave": true, }, "typescript.tsc.autoDetect": "off", - "notebook.experimental.useMarkdownRenderer": true, - "testing.autoRun.mode": "onlyPreviouslyRun" + "notebook.experimental.useMarkdownRenderer": true } diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 09f46ac51e214..21e23a00a3918 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -196,3 +196,4 @@ .monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal > .monaco-select-box { padding: 0 22px 0 6px; } + From 2a9e136e1b5023f66e7d3ac226b585cceee167e4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 20:14:35 -0700 Subject: [PATCH 44/49] revert more changes to settings --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 02272c796dad0..807f261665335 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -84,6 +84,5 @@ "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true, }, - "typescript.tsc.autoDetect": "off", - "notebook.experimental.useMarkdownRenderer": true + "typescript.tsc.autoDetect": "off" } From 966eea3927747b6dcc7efdfaea8909666ed760bc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 20:18:59 -0700 Subject: [PATCH 45/49] =?UTF-8?q?=F0=9F=A7=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 807f261665335..d9046d76c72eb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -83,6 +83,5 @@ "[javascript]": { "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true, - }, - "typescript.tsc.autoDetect": "off" + } } From 71d57e05dcc611b82d96d39f2cb9a69acb5a9a47 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 20:20:36 -0700 Subject: [PATCH 46/49] copy directly from master --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d9046d76c72eb..7eede8c30a989 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -83,5 +83,8 @@ "[javascript]": { "editor.defaultFormatter": "vscode.typescript-language-features", "editor.formatOnSave": true, - } + }, + "typescript.tsc.autoDetect": "off", + "notebook.experimental.useMarkdownRenderer": true, + "testing.autoRun.mode": "onlyPreviouslyRun", } From 1a141423308965d9db0066ed57cae611d3285bed Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 7 Apr 2021 20:22:51 -0700 Subject: [PATCH 47/49] remove empty line --- src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index 55776650710cf..a9111f8de550b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -149,7 +149,6 @@ export class TerminalTabbedView extends Disposable { maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None }, Sizing.Distribute, this._terminalContainerIndex); - } layout(width: number, height: number): void { From 37a44ac7dbbdbc76f1a72019d518663a16ec3e47 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 8 Apr 2021 05:24:07 -0700 Subject: [PATCH 48/49] Types, polish --- .../contrib/terminal/browser/terminalTabbedView.ts | 6 +++--- .../contrib/terminal/browser/terminalTabsWidget.ts | 8 ++++---- .../contrib/terminal/common/terminalConfiguration.ts | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index a9111f8de550b..dd60c3353ad4e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -34,7 +34,7 @@ export class TerminalTabbedView extends Disposable { private _terminalTabTree: HTMLElement; private _parentElement: HTMLElement; - private _tabsWidget: TerminalTabsWidget | undefined; + private _tabsWidget: TerminalTabsWidget; private _findWidget: TerminalFindWidget; private _tabTreeIndex: number; @@ -101,7 +101,7 @@ export class TerminalTabbedView extends Disposable { if (this._showTabs) { this._splitView.addView({ element: this._terminalTabTree, - layout: width => this._tabsWidget!.layout(this._height, width), + layout: width => this._tabsWidget.layout(this._height, width), minimumSize: 40, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None, @@ -136,7 +136,7 @@ export class TerminalTabbedView extends Disposable { if (this._showTabs) { this._splitView.addView({ element: this._terminalTabTree, - layout: width => this._tabsWidget!.layout(this._height, width), + layout: width => this._tabsWidget.layout(this._height, width), minimumSize: 40, maximumSize: Number.POSITIVE_INFINITY, onDidChange: () => Disposable.None, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts index afe69c92b632f..be19f85cc424f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsWidget.ts @@ -14,11 +14,11 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ITerminalInstance, ITerminalService, ITerminalTab } from 'vs/workbench/contrib/terminal/browser/terminal'; import { localize } from 'vs/nls'; -import * as DOM from 'vs/base/browser/dom'; +import * as dom from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; -const $ = DOM.$; +const $ = dom.$; export class TerminalTabsWidget extends WorkbenchObjectTree { private _terminalService: ITerminalService; @@ -44,7 +44,7 @@ export class TerminalTabsWidget extends WorkbenchObjectTree { supportDynamicHeights: true, identityProvider: new TerminalTabsIdentityProvider(), accessibilityProvider: new TerminalTabsAccessibilityProvider(), - styleController: id => new DefaultStyleController(DOM.createStyleSheet(container), id), + styleController: id => new DefaultStyleController(dom.createStyleSheet(container), id), filter: undefined, smoothScrolling: configurationService.getValue('workbench.list.smoothScrolling'), multipleSelectionSupport: false, @@ -117,7 +117,7 @@ class TerminalTabsRenderer implements ITreeRenderer Date: Thu, 8 Apr 2021 05:47:49 -0700 Subject: [PATCH 49/49] Fix ctx menu, clean up DI --- .../terminal/browser/terminalTabbedView.ts | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts index dd60c3353ad4e..bc0cfe45d2c1e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabbedView.ts @@ -45,32 +45,22 @@ export class TerminalTabbedView extends Disposable { private _height: number | undefined; - private _instantiationService: IInstantiationService; - private _terminalService: ITerminalService; - private _themeService: IThemeService; - private _contextMenuService: IContextMenuService; - private _cancelContextMenu: boolean = false; private _menu: IMenu; constructor( parentElement: HTMLElement, - @ITerminalService terminalService: ITerminalService, - @IThemeService themeService: IThemeService, - @IConfigurationService configurationService: IConfigurationService, - @IInstantiationService instantiationService: IInstantiationService, + @ITerminalService private readonly _terminalService: ITerminalService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, @INotificationService private readonly _notificationService: INotificationService, - @IContextKeyService _contextKeyService: IContextKeyService, - @IContextKeyService contextMenuService: IContextMenuService, - @IMenuService _menuService: IMenuService + @IContextMenuService private readonly _contextMenuService: IContextMenuService, + @IThemeService private readonly _themeService: IThemeService, + @IConfigurationService configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IMenuService menuService: IMenuService ) { super(); - this._instantiationService = instantiationService; - this._terminalService = terminalService; - this._themeService = themeService; - this._contextMenuService = contextMenuService; - this._parentElement = parentElement; this._terminalTabTree = document.createElement('div'); @@ -84,20 +74,20 @@ export class TerminalTabbedView extends Disposable { this._terminalContainer.classList.add('terminal-outer-container'); this._terminalContainer.style.display = 'block'; - this._showTabs = terminalService.configHelper.config.showTabs; + this._showTabs = this._terminalService.configHelper.config.showTabs; - this._tabTreeIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this._terminalContainerIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._tabTreeIndex = this._terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this._terminalContainerIndex = this._terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; - this._menu = this._register(_menuService.createMenu(MenuId.TerminalContext, _contextKeyService)); + this._menu = this._register(menuService.createMenu(MenuId.TerminalContext, contextKeyService)); - this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(_contextKeyService); + this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_VISIBLE.bindTo(contextKeyService); - terminalService.setContainers(parentElement, this._terminalContainer); + this._terminalService.setContainers(parentElement, this._terminalContainer); configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('terminal.integrated.showTabs')) { - this._showTabs = terminalService.configHelper.config.showTabs; + this._showTabs = this._terminalService.configHelper.config.showTabs; if (this._showTabs) { this._splitView.addView({ element: this._terminalTabTree, @@ -110,15 +100,15 @@ export class TerminalTabbedView extends Disposable { this._splitView.removeView(this._tabTreeIndex); } } else if (e.affectsConfiguration('terminal.integrated.tabsLocation')) { - this._tabTreeIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; - this._terminalContainerIndex = terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; + this._tabTreeIndex = this._terminalService.configHelper.config.tabsLocation === 'left' ? 0 : 1; + this._terminalContainerIndex = this._terminalService.configHelper.config.tabsLocation === 'left' ? 1 : 0; if (this._showTabs) { this._splitView.swapViews(0, 1); } } }); - this._register(themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); + this._register(this._themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); this._updateTheme(); this._findWidget.focusTracker.onDidFocus(() => this._terminalContainer!.classList.add(FIND_FOCUS_CLASS));