From bab13b9505905c40757c17bdfb7e95932d72b30d Mon Sep 17 00:00:00 2001 From: Johannes Faltermeier Date: Thu, 24 Aug 2023 09:40:41 +0200 Subject: [PATCH] [vscode] Support EnvironmentVariableCollection description #12696 * implement enhanced preview for terminal widget * remove terminal info toolbar item Contributed on behalf of STMicroelectronics Signed-off-by: Johannes Faltermeier --- packages/core/src/browser/shell/tab-bars.ts | 4 +- .../browser/terminal-frontend-contribution.ts | 16 +-- .../browser/terminal-info-toolbar-item.tsx | 113 ------------------ .../src/browser/terminal-widget-impl.ts | 60 +++++++++- 4 files changed, 61 insertions(+), 132 deletions(-) delete mode 100644 packages/terminal/src/browser/terminal-info-toolbar-item.tsx diff --git a/packages/core/src/browser/shell/tab-bars.ts b/packages/core/src/browser/shell/tab-bars.ts index eab9044b018a9..c43f42c73bbcf 100644 --- a/packages/core/src/browser/shell/tab-bars.ts +++ b/packages/core/src/browser/shell/tab-bars.ts @@ -170,8 +170,8 @@ export class TabBarRenderer extends TabBar.Renderer { const hover = this.tabBar && (this.tabBar.orientation === 'horizontal' && this.corePreferences?.['window.tabbar.enhancedPreview'] === 'classic') ? { title: title.caption } : { - onmouseenter: this.handleMouseEnterEvent - }; + onmouseenter: this.handleMouseEnterEvent + }; return h.li( { diff --git a/packages/terminal/src/browser/terminal-frontend-contribution.ts b/packages/terminal/src/browser/terminal-frontend-contribution.ts index dc55bf3875749..5916ab41f399e 100644 --- a/packages/terminal/src/browser/terminal-frontend-contribution.ts +++ b/packages/terminal/src/browser/terminal-frontend-contribution.ts @@ -32,7 +32,7 @@ import { import { ApplicationShell, KeybindingContribution, KeyCode, Key, WidgetManager, PreferenceService, KeybindingRegistry, LabelProvider, WidgetOpenerOptions, StorageService, QuickInputService, - codicon, CommonCommands, FrontendApplicationContribution, OnWillStopAction, Dialog, ConfirmDialog, FrontendApplication, PreferenceScope, Widget, HoverService + codicon, CommonCommands, FrontendApplicationContribution, OnWillStopAction, Dialog, ConfirmDialog, FrontendApplication, PreferenceScope, Widget } from '@theia/core/lib/browser'; import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { TERMINAL_WIDGET_FACTORY_ID, TerminalWidgetFactoryOptions, TerminalWidgetImpl } from './terminal-widget-impl'; @@ -60,8 +60,6 @@ import { nls } from '@theia/core/lib/common/nls'; import { Profiles, TerminalPreferences } from './terminal-preferences'; import { ShellTerminalProfile } from './shell-terminal-profile'; import { VariableResolverService } from '@theia/variable-resolver/lib/browser'; -import { TerminalInfoToolbarItem } from './terminal-info-toolbar-item'; -import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; export namespace TerminalMenus { export const TERMINAL = [...MAIN_MENU_BAR, '7_terminal']; @@ -218,17 +216,6 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu @inject(TerminalPreferences) protected terminalPreferences: TerminalPreferences; - @inject(HoverService) - protected readonly hoverService: HoverService; - - @inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory; - - protected _markdownRenderer: MarkdownRenderer | undefined; - protected get markdownRenderer(): MarkdownRenderer { - this._markdownRenderer ||= this.markdownRendererFactory(); - return this._markdownRenderer; - } - protected mergePreferencesPromise: Promise = Promise.resolve(); protected readonly onDidCreateTerminalEmitter = new Emitter(); @@ -744,7 +731,6 @@ export class TerminalFrontendContribution implements FrontendApplicationContribu } registerToolbarItems(toolbar: TabBarToolbarRegistry): void { - toolbar.registerItem(new TerminalInfoToolbarItem(this.hoverService, this.markdownRenderer)); toolbar.registerItem({ id: TerminalCommands.SPLIT.id, command: TerminalCommands.SPLIT.id, diff --git a/packages/terminal/src/browser/terminal-info-toolbar-item.tsx b/packages/terminal/src/browser/terminal-info-toolbar-item.tsx deleted file mode 100644 index 5d115c9d98e04..0000000000000 --- a/packages/terminal/src/browser/terminal-info-toolbar-item.tsx +++ /dev/null @@ -1,113 +0,0 @@ -// ***************************************************************************** -// Copyright (C) 2023 STMicroelectronics and others. -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License v. 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0. -// -// This Source Code may also be made available under the following Secondary -// Licenses when the conditions for such availability set forth in the Eclipse -// Public License v. 2.0 are satisfied: GNU General Public License, version 2 -// with the GNU Classpath Exception which is available at -// https://www.gnu.org/software/classpath/license.html. -// -// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 -// ***************************************************************************** -import { ReactTabBarToolbarItem } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; -import { ReactNode } from '@theia/core/shared/react'; -import React = require('@theia/core/shared/react'); -import { HoverService, Widget } from '@theia/core/lib/browser'; -import { TerminalWidget } from './base/terminal-widget'; -import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; -import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; -import { DisposableCollection } from '@theia/core'; - -export class TerminalInfoToolbarItem implements ReactTabBarToolbarItem { - readonly id = 'terminal:info'; - - constructor( - protected readonly hoverService: HoverService, - protected readonly markdownRenderer: MarkdownRenderer - ) {} - - isVisible(widget?: Widget): boolean { - return widget instanceof TerminalWidget; - } - - render(widget?: Widget): ReactNode { - const toDispose = new DisposableCollection(); - return ( -
this.onMouseEnter(e, toDispose, widget)} - onMouseLeave={e => this.onMouseLeave(toDispose)} - >
- ); - } - - protected async onMouseEnter( - event: React.MouseEvent, toDispose: DisposableCollection, currentTerminal?: Widget - ): Promise { - const currentTarget = event.currentTarget; - if (currentTerminal instanceof TerminalWidget) { - const extensions = await currentTerminal.envVarCollectionDescriptionsByExtension; - const processId = await currentTerminal.processId; - const processInfo = await currentTerminal.processInfo; - - const mainDiv = document.createElement('div'); - - const pid = document.createElement('div'); - pid.textContent = 'Process ID: ' + processId; - mainDiv.appendChild(pid); - - const commandLine = document.createElement('div'); - commandLine.textContent = - 'Command line: ' + - processInfo.executable + - ' ' + - processInfo.arguments.join(' '); - mainDiv.appendChild(commandLine); - - mainDiv.appendChild(document.createElement('hr')); - - const header = document.createElement('div'); - header.textContent = - 'The following extensions have contributed to this terminal\'s environment:'; - mainDiv.appendChild(header); - - const list = document.createElement('ul'); - mainDiv.appendChild(list); - - extensions.forEach((value, key) => { - const item = document.createElement('li'); - let markdown; - if (value === undefined) { - markdown = new MarkdownStringImpl(''); - markdown.appendText(key); - } else if (typeof value === 'string') { - markdown = new MarkdownStringImpl(''); - markdown.appendText(key + ': ' + value); - } else { - markdown = new MarkdownStringImpl('', value); - markdown.appendText(key + ': '); - markdown.appendMarkdown(value.value); - } - const result = this.markdownRenderer.render(markdown); - toDispose.push(result); - item.appendChild(result.element); - list.appendChild(item); - }); - - this.hoverService.requestHover({ - content: mainDiv, - target: currentTarget, - position: 'right', - }); - } - } - - protected async onMouseLeave(hoverResources: DisposableCollection): Promise { - hoverResources.dispose(); - } -} diff --git a/packages/terminal/src/browser/terminal-widget-impl.ts b/packages/terminal/src/browser/terminal-widget-impl.ts index 16c644c51f00f..b0a1955d3d317 100644 --- a/packages/terminal/src/browser/terminal-widget-impl.ts +++ b/packages/terminal/src/browser/terminal-widget-impl.ts @@ -43,7 +43,9 @@ import { Key } from '@theia/core/lib/browser/keys'; import { nls } from '@theia/core/lib/common/nls'; import { TerminalMenus } from './terminal-frontend-contribution'; import debounce = require('p-debounce'); -import { MarkdownString } from '@theia/core/lib/common/markdown-rendering/markdown-string'; +import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string'; +import { EnhancedPreviewWidget } from '@theia/core/lib/browser/widgets/enhanced-preview-widget'; +import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer'; export const TERMINAL_WIDGET_FACTORY_ID = 'terminal'; @@ -58,7 +60,7 @@ export interface TerminalContribution { } @injectable() -export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget { +export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget, EnhancedPreviewWidget { readonly isExtractable: boolean = true; secondaryWindow: Window | undefined; location: TerminalLocationOptions; @@ -82,6 +84,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget protected lastMousePosition: { x: number, y: number } | undefined; protected isAttachedCloseListener: boolean = false; protected shown = false; + protected enhancedPreviewNode: Node | undefined; override lastCwd = new URI(); @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -99,6 +102,13 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget @inject(TerminalThemeService) protected readonly themeService: TerminalThemeService; @inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder; @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer; + @inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory; + + protected _markdownRenderer: MarkdownRenderer | undefined; + protected get markdownRenderer(): MarkdownRenderer { + this._markdownRenderer ||= this.markdownRendererFactory(); + return this._markdownRenderer; + } protected readonly onDidOpenEmitter = new Emitter(); readonly onDidOpen: Event = this.onDidOpenEmitter.event; @@ -764,6 +774,10 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget if (this.exitStatus) { this.onTermDidClose.fire(this); } + if (this.enhancedPreviewNode) { + // don't use preview node anymore. rendered markdown will be disposed on super call + this.enhancedPreviewNode = undefined; + } super.dispose(); } @@ -867,4 +881,46 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget private disableEnterWhenAttachCloseListener(): boolean { return this.isAttachedCloseListener; } + + getEnhancedPreviewNode(): Node | undefined { + if (this.enhancedPreviewNode) { + return this.enhancedPreviewNode; + } + + this.enhancedPreviewNode = document.createElement('div'); + + Promise.all([this.envVarCollectionDescriptionsByExtension, this.processId, this.processInfo]) + .then((values: [Map, number, TerminalProcessInfo]) => { + const extensions = values[0]; + const processId = values[1]; + const processInfo = values[2]; + + const markdown = new MarkdownStringImpl(); + markdown.appendMarkdown('Process ID: ' + processId + '\\\n'); + markdown.appendMarkdown('Command line: ' + + processInfo.executable + + ' ' + + processInfo.arguments.join(' ') + + '\n\n---\n\n'); + markdown.appendMarkdown('The following extensions have contributed to this terminal\'s environment:\n'); + extensions.forEach((value, key) => { + if (value === undefined) { + markdown.appendMarkdown('* ' + key + '\n'); + } else if (typeof value === 'string') { + markdown.appendMarkdown('* ' + key + ': ' + value + '\n'); + } else { + markdown.appendMarkdown('* ' + key + ': ' + value.value + '\n'); + } + }); + + const enhancedPreviewNode = this.enhancedPreviewNode; + if (!this.isDisposed && enhancedPreviewNode) { + const result = this.markdownRenderer.render(markdown); + this.toDispose.push(result); + enhancedPreviewNode.appendChild(result.element); + } + }); + + return this.enhancedPreviewNode; + } }