diff --git a/src/vs/workbench/api/common/extHostLanguages.ts b/src/vs/workbench/api/common/extHostLanguages.ts index cf2ced19fe0..9aa3dda450c 100644 --- a/src/vs/workbench/api/common/extHostLanguages.ts +++ b/src/vs/workbench/api/common/extHostLanguages.ts @@ -87,8 +87,8 @@ export class ExtHostLanguages { this._proxy.$setLanguageStatus(handle, { source: extension.displayName ?? extension.name, selector: data.selector, - text: data.text, - message: data.detail, + label: data.text, + detail: data.detail, severity: data.severity === LanguageStatusSeverity.Error ? Severity.Error : data.severity === LanguageStatusSeverity.Warning ? Severity.Warning : Severity.Info }); }, 0); diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts index 681099725fd..933a235d418 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts @@ -3,24 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./media/languageStatus'; +import * as dom from 'vs/base/browser/dom'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import Severity from 'vs/base/common/severity'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { localize } from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ThemeColor, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { registerThemingParticipant, ThemeColor, themeColorFromId, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_ERROR_ITEM_FOREGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; -import { LanguageStatusDetailsWidget } from 'vs/workbench/contrib/languageStatus/browser/languageStatusList'; +import { NOTIFICATIONS_BORDER, STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_ERROR_ITEM_FOREGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_FOREGROUND } from 'vs/workbench/common/theme'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar'; +import { parseLinkedText } from 'vs/base/common/linkedText'; +import { Link } from 'vs/platform/opener/browser/link'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; +import { Codicon } from 'vs/base/common/codicons'; class EditorStatusContribution implements IWorkbenchContribution { @@ -30,17 +33,13 @@ class EditorStatusContribution implements IWorkbenchContribution { private readonly _disposables = new DisposableStore(); private _status: ILanguageStatus[] = []; - private _showingDetails: boolean = false; constructor( @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService, @IStatusbarService private readonly _statusBarService: IStatusbarService, @IEditorService private readonly _editorService: IEditorService, - @IContextViewService private readonly _contextViewService: IContextViewService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IOpenerService private readonly _openerService: IOpenerService, ) { - this._disposables.add(CommandsRegistry.registerCommand(EditorStatusContribution._id, () => this._toggleDetails())); - _languageStatusService.onDidChange(this._update, this, this._disposables); _editorService.onDidActiveEditorChange(this._update, this, this._disposables); this._update(); @@ -79,12 +78,9 @@ class EditorStatusContribution implements IWorkbenchContribution { color = themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND); } - // todo@jrieken is is NOT OK because all md-strings are now trusted even though - // they are from different extensions - const tooltip = new MarkdownString('', { isTrusted: true, supportThemeIcons: true }); - for (let status of this._status) { - tooltip.appendMarkdown(status.message); - tooltip.appendMarkdown('\n\n---\n\n'); + const element = document.createElement('div'); + for (const status of this._status) { + element.appendChild(this._renderStatus(status)); } const props: IStatusbarEntry = { @@ -93,9 +89,7 @@ class EditorStatusContribution implements IWorkbenchContribution { ariaLabel: localize('status.editor.status', "Language Status"), backgroundColor, color, - // command: EditorStatusContribution._id, - showBeak: this._showingDetails, - tooltip + tooltip: element }; if (!this._entry.value) { @@ -105,44 +99,54 @@ class EditorStatusContribution implements IWorkbenchContribution { } } - private _toggleDetails(): void { - if (!this._entry.value) { - return; - } + private _renderStatus(status: ILanguageStatus): HTMLElement { - if (this._showingDetails) { - this._contextViewService.hideContextView(); - // this._showingDetails = false; // happens in onHide - return; - } + const node = document.createElement('div'); + node.classList.add('hover-language-status-element'); - const anchor = document.getElementById(EditorStatusContribution._id); - if (!anchor) { - return; - } + const left = document.createElement('div'); + node.appendChild(left); - let widget: LanguageStatusDetailsWidget | undefined; + const detail = document.createElement('div'); + detail.classList.add('detail'); + this._renderTextPlus(detail, status.detail); + left.appendChild(detail); - this._contextViewService.showContextView({ - getAnchor: () => anchor, - render: container => { - widget = this._instantiationService.createInstance(LanguageStatusDetailsWidget, this._status, container); - return toDisposable(() => { - widget?.dispose(); - widget = undefined; - }); + const label = document.createElement('div'); + label.classList.add('label'); + this._renderTextPlus(label, status.label); + left.appendChild(label); - }, - onHide: () => { - this._showingDetails = false; - this._update(); + const right = document.createElement('div'); + node.appendChild(right); + + const actions = new ActionBar(right, {}); + actions.push(new Action( + 'pin', + localize('label.pin', 'Pin'), + ThemeIcon.asClassName(Codicon.pin), + true, + () => { + console.log(status); } - }); - this._showingDetails = true; - this._update(); + ), { icon: true, label: false }); + return node; + } + + private _renderTextPlus(target: HTMLElement, text: string): void { + for (let node of parseLinkedText(text).nodes) { + if (typeof node === 'string') { + const parts = renderLabelWithIcons(node); + dom.append(target, ...parts); + } else { + dom.append(target, new Link(node, undefined, this._openerService).el); + } + } } } - +registerThemingParticipant((theme, collector) => { + collector.addRule(`:root { --code-notifications-border: ${theme.getColor(NOTIFICATIONS_BORDER)}}`); +}); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(EditorStatusContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatusList.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatusList.ts deleted file mode 100644 index e2fbb7a0a48..00000000000 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatusList.ts +++ /dev/null @@ -1,195 +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 'vs/css!./media/languageStatus'; -import * as dom from 'vs/base/browser/dom'; -import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { localize } from 'vs/nls'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { NOTIFICATIONS_BACKGROUND, NOTIFICATIONS_BORDER } from 'vs/workbench/common/theme'; -import { ILanguageStatus } from 'vs/workbench/services/languageStatus/common/languageStatusService'; -import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { widgetShadow } from 'vs/platform/theme/common/colorRegistry'; -import { parseLinkedText } from 'vs/base/common/linkedText'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { Link } from 'vs/platform/opener/browser/link'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { Action } from 'vs/base/common/actions'; -import { Codicon } from 'vs/base/common/codicons'; -// import Severity from 'vs/base/common/severity'; - -class LanguageStatusTemplate { - - readonly text: HTMLDivElement; - readonly toolbar: ToolBar; - - constructor( - readonly container: HTMLElement, - @IContextMenuService contextMenuService: IContextMenuService, - @IOpenerService private readonly _openerService: IOpenerService, - ) { - container.classList.add('status-element'); - - this.text = document.createElement('div'); - container.appendChild(this.text); - - const toolbarContainer = document.createElement('div'); - container.appendChild(toolbarContainer); - - this.toolbar = new ToolBar(toolbarContainer, contextMenuService, {}); - } - - dispose() { - this.toolbar.dispose(); - } - - set(element: ILanguageStatus): void { - // message - // source - dom.clearNode(this.text); - - // switch (element.severity) { - // case Severity.Error: - // dom.append(this.text, ...renderLabelWithIcons('$(error)')); - // break; - // case Severity.Warning: - // dom.append(this.text, ...renderLabelWithIcons('$(warning)')); - // break; - // case Severity.Info: - // default: - // dom.append(this.text, ...renderLabelWithIcons('$(info)')); - // break; - // } - - for (let node of parseLinkedText(element.message).nodes) { - if (typeof node === 'string') { - const parts = renderLabelWithIcons(node); - dom.append(this.text, ...parts); - } else { - dom.append(this.text, new Link(node, undefined, this._openerService).el); - } - } - - this.toolbar.setActions([new Action( - 'dd', - localize('label.pin', 'Pin'), - ThemeIcon.asClassName(Codicon.pin), - true, - () => { - console.log(element); - } - )]); - } -} - -class Renderer implements IListRenderer, IListVirtualDelegate { - - static templateId: string = 'languageStatus'; - - constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { } - - // delegate - - getHeight(element: ILanguageStatus): number { - return 42; - } - getTemplateId(element: ILanguageStatus): string { - return Renderer.templateId; - } - - // renderer - - templateId: string = Renderer.templateId; - - renderTemplate(container: HTMLElement): LanguageStatusTemplate { - return this._instantiationService.createInstance(LanguageStatusTemplate, container); - } - renderElement(element: ILanguageStatus, index: number, templateData: LanguageStatusTemplate, height: number | undefined): void { - templateData.set(element); - } - disposeTemplate(templateData: LanguageStatusTemplate): void { - templateData.dispose(); - } - -} - -export class LanguageStatusDetailsWidget { - - private readonly _disposables = new DisposableStore(); - - private readonly _container: HTMLDivElement; - private readonly _list: WorkbenchList; - - constructor( - status: ILanguageStatus[], - parent: HTMLElement, - @IThemeService themeService: IThemeService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ILayoutService private readonly _layoutService: ILayoutService, - ) { - parent.style.setProperty('--code-widget-shadow', themeService.getColorTheme().getColor(widgetShadow, true)?.toString() ?? 'inherit'); - parent.style.setProperty('--code-notifications-border', themeService.getColorTheme().getColor(NOTIFICATIONS_BORDER, true)?.toString() ?? 'inherit'); - parent.classList.add('language-status'); - - this._container = document.createElement('div'); - this._container.classList.add('list-container'); - parent.appendChild(this._container); - - const renderer = _instantiationService.createInstance(Renderer); - this._list = >this._instantiationService.createInstance( - WorkbenchList, - 'LanguageStatusList', - this._container, - renderer, - [renderer], - { - // ...this.options, - setRowLineHeight: false, - horizontalScrolling: false, - overrideStyles: { - listBackground: NOTIFICATIONS_BACKGROUND, - // listInactiveSelectionBackground: NOTIFICATIONS_BACKGROUND, - // listActiveSelectionBackground: NOTIFICATIONS_BACKGROUND, - }, - accessibilityProvider: { - getAriaLabel(element: ILanguageStatus): string { - return element.message; - }, - getWidgetAriaLabel(): string { - return localize('language status', "Language Status List"); - }, - getRole(): string { - return 'dialog'; // https://github.com/microsoft/vscode/issues/82728 - } - } - } - ); - - // no selections - this._disposables.add(this._list.onDidChangeSelection(e => { - if (e.indexes.length > 0) { - this._list.setSelection([]); - } - })); - - this._list.splice(0, this._list.length, status); - this.layout(); - } - - dispose(): void { - this._list.dispose(); - } - - layout() { - const width = Math.min(460, Math.max(200, this._layoutService.dimension.width * 0.6)); - this._container.style.width = `${width}px`; - this._list.layout(); - } -} diff --git a/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css b/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css index b3a4baf0505..ba4ab75e7ad 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css +++ b/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css @@ -3,18 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .language-status { - margin: 7px; - cursor: default; - box-shadow: var(--code-widget-shadow) 0 0 8px 2px; -} - -.monaco-workbench .language-status .list-container .status-element { - padding: 10px 5px; +.monaco-workbench .hover-language-status-element { display: flex; justify-content: space-between; + padding: 4px 8px; } -.monaco-workbench .language-status .list-container .monaco-list-row[data-last-element="false"].status-element { +.monaco-workbench .hover-language-status-element:not(:last-child) { border-bottom: 1px solid var(--code-notifications-border); } + +.monaco-workbench .hover-language-status-element .monaco-action-bar { + padding-left: 8px; +} diff --git a/src/vs/workbench/services/languageStatus/common/languageStatusService.ts b/src/vs/workbench/services/languageStatus/common/languageStatusService.ts index 6a709fb59c0..2fc2a423646 100644 --- a/src/vs/workbench/services/languageStatus/common/languageStatusService.ts +++ b/src/vs/workbench/services/languageStatus/common/languageStatusService.ts @@ -17,8 +17,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export interface ILanguageStatus { selector: LanguageSelector, severity: Severity; - text: string; - message: string; + label: string; + detail: string; source: string; }