Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Language status items #131264

Merged
merged 27 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8a11954
move language status service to workbench
jrieken Aug 17, 2021
5055ed7
remove language status from editorStatus.ts
jrieken Aug 17, 2021
4222c51
add fly-out UI for language status items, tweak API proposal
jrieken Aug 17, 2021
6d49535
Merge branch 'main' into joh/langStatus
jrieken Aug 18, 2021
f801025
small API extension, experiment with pin-command
jrieken Aug 18, 2021
b582cd4
use hover instead of flyout
jrieken Aug 18, 2021
5850677
Merge branch 'main' into joh/langStatus
jrieken Aug 19, 2021
d6a31ab
no hover padding when showing html element contents, fyi @Tyriar
jrieken Aug 19, 2021
222214e
WIP use html element as hover element, needs status bar adoption, pin…
jrieken Aug 19, 2021
bc4f06a
Merge branch 'main' into joh/langStatus
jrieken Aug 20, 2021
47c2a49
Merge branch 'main' into joh/langStatus
jrieken Aug 20, 2021
6ef61f0
Allow HTMLElement as status bar tooltip
jrieken Aug 20, 2021
2a79e29
Merge branch 'main' into joh/langStatus
jrieken Aug 20, 2021
1fd1444
support language status command, no more pin for now
jrieken Aug 20, 2021
3fbb432
tweak padding of button
jrieken Aug 20, 2021
9652c1a
tweak padding and position of button
jrieken Aug 20, 2021
61e2a76
render text and details, only detail supports mini-md
jrieken Aug 20, 2021
bdbcd48
Merge branch 'main' into joh/langStatus
jrieken Aug 23, 2021
4baebf9
Merge branch 'main' into joh/langStatus
jrieken Aug 25, 2021
aa4f2d5
use info, warning, error icons, no colors for now, remove padding (hack)
jrieken Aug 25, 2021
095b318
Merge branch 'main' into joh/langStatus
jrieken Aug 26, 2021
258ca70
more CSS trickery to get linked status bar items to work
jrieken Aug 26, 2021
e6bec38
Merge branch 'main' into joh/langStatus
jrieken Aug 27, 2021
f9b95de
further alignments between LanguageStatusItem and StatusBarItem, add …
jrieken Aug 27, 2021
1c8d83f
dispose things that are created on render
jrieken Aug 27, 2021
e0c5f66
Merge branch 'main' into joh/langStatus
jrieken Aug 30, 2021
5f3b7e1
persist pinned language status items
jrieken Aug 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
"name": "vs/workbench/contrib/interactive",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/languageStatus",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/keybindings",
"project": "vscode-workbench"
Expand Down
4 changes: 2 additions & 2 deletions src/vs/base/browser/ui/hover/hover.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
display: none;
}

.monaco-hover .hover-contents {
.monaco-hover .hover-contents:not(.html-hover-contents) {
padding: 4px 8px;
}

Expand Down Expand Up @@ -137,7 +137,7 @@
}

/** Spans in markdown hovers need a margin-bottom to avoid looking cramped: https://github.com/microsoft/vscode/issues/101496 **/
.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents) span {
.monaco-hover .markdown-hover .hover-contents:not(.code-hover-contents):not(.html-hover-contents) span {
margin-bottom: 4px;
display: inline-block;
}
Expand Down
2 changes: 1 addition & 1 deletion src/vs/base/browser/ui/iconLabel/iconLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface IIconLabelCreationOptions {
}

export interface IIconLabelMarkdownString {
markdown: IMarkdownString | string | undefined | ((token: CancellationToken) => Promise<IMarkdownString | string | undefined>);
markdown: IMarkdownString | string | HTMLElement | undefined | ((token: CancellationToken) => Promise<IMarkdownString | string | undefined>);
markdownNotSupportedFallback: string | undefined;
}

Expand Down
8 changes: 4 additions & 4 deletions src/vs/base/browser/ui/iconLabel/iconLabelHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function setupNativeHover(htmlElement: HTMLElement, tooltip: string | IIc
}
}

export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString | undefined): IDisposable | undefined {
export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTMLElement, markdownTooltip: string | IIconLabelMarkdownString | HTMLElement | undefined): IDisposable | undefined {
if (!markdownTooltip) {
return undefined;
}
Expand Down Expand Up @@ -79,7 +79,7 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
hoverWidget?.dispose();
hoverWidget = hoverDelegate.showHover(hoverOptions);

const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) ? markdownTooltip.markdownNotSupportedFallback : undefined);
const resolvedTooltip = (await tooltip(tokenSource.token)) ?? (!isString(markdownTooltip) && !(markdownTooltip instanceof HTMLElement) ? markdownTooltip.markdownNotSupportedFallback : undefined);

hoverWidget?.dispose();
hoverWidget = undefined;
Expand Down Expand Up @@ -119,8 +119,8 @@ export function setupCustomHover(hoverDelegate: IHoverDelegate, htmlElement: HTM
}


function getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString): (token: CancellationToken) => Promise<string | IMarkdownString | undefined> {
if (isString(markdownTooltip)) {
function getTooltipForCustom(markdownTooltip: string | IIconLabelMarkdownString | HTMLElement): (token: CancellationToken) => Promise<string | IMarkdownString | HTMLElement | undefined> {
if (isString(markdownTooltip) || markdownTooltip instanceof HTMLElement) {
return async () => markdownTooltip;
} else if (isFunction(markdownTooltip.markdown)) {
return markdownTooltip.markdown;
Expand Down
9 changes: 6 additions & 3 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2873,15 +2873,18 @@ declare module 'vscode' {
}

interface LanguageStatusItem {
readonly id: string;
selector: DocumentSelector;
text: string;
detail: string | MarkdownString
severity: LanguageStatusSeverity;
name: string | undefined;
text: string;
detail: string;
command: Command | undefined;
dispose(): void;
}

namespace languages {
export function createLanguageStatusItem(selector: DocumentSelector): LanguageStatusItem;
export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem;
}

//#endregion
Expand Down
6 changes: 3 additions & 3 deletions src/vs/workbench/api/common/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol, extHostCommands.converter);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments, extHostCommands.converter);

// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
Expand Down Expand Up @@ -507,9 +507,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostLanguageFeatures.registerTypeHierarchyProvider(extension, selector, provider);
},
createLanguageStatusItem(selector: vscode.DocumentSelector): vscode.LanguageStatusItem {
createLanguageStatusItem(id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem {
checkProposedApiEnabled(extension);
return extHostLanguages.createLanguageStatusItem(selector);
return extHostLanguages.createLanguageStatusItem(extension, id, selector);
}
};

Expand Down
56 changes: 43 additions & 13 deletions src/vs/workbench/api/common/extHostLanguages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
import { StandardTokenType, Range, Position, LanguageStatusSeverity } from 'vs/workbench/api/common/extHostTypes';
import Severity from 'vs/base/common/severity';
import { disposableTimeout } from 'vs/base/common/async';
import { IDisposable } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';

export class ExtHostLanguages {

private readonly _proxy: MainThreadLanguagesShape;
private readonly _documents: ExtHostDocuments;

constructor(
mainContext: IMainContext,
documents: ExtHostDocuments
private readonly _documents: ExtHostDocuments,
private readonly _commands: CommandsConverter
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadLanguages);
this._documents = documents;
}

getLanguages(): Promise<string[]> {
Expand Down Expand Up @@ -67,32 +68,58 @@ export class ExtHostLanguages {

private _handlePool: number = 0;

createLanguageStatusItem(selector: vscode.DocumentSelector): vscode.LanguageStatusItem {
createLanguageStatusItem(extension: IExtensionDescription, id: string, selector: vscode.DocumentSelector): vscode.LanguageStatusItem {

const handle = this._handlePool++;
const proxy = this._proxy;

const data: { selector: any, text: string, detail: string | vscode.MarkdownString, severity: vscode.LanguageStatusSeverity } = {
const data: Omit<vscode.LanguageStatusItem, 'dispose'> = {
selector,
id,
name: extension.displayName ?? extension.name,
severity: LanguageStatusSeverity.Information,
command: undefined,
text: '',
detail: '',
severity: LanguageStatusSeverity.Information,
};

let soonHandle: IDisposable | undefined;
let commandDisposables = new DisposableStore();
const updateAsync = () => {
soonHandle?.dispose();
soonHandle = disposableTimeout(() => {

commandDisposables.clear();

this._proxy.$setLanguageStatus(handle, {
id: `${extension.identifier.value}/${id}`,
name: data.name ?? extension.displayName ?? extension.name,
source: extension.displayName ?? extension.name,
selector: data.selector,
text: data.text,
message: typeof data.detail === 'string' ? data.detail : typeConvert.MarkdownString.from(data.detail),
severity: data.severity === LanguageStatusSeverity.Error ? Severity.Error : data.severity === LanguageStatusSeverity.Warning ? Severity.Warning : Severity.Info
label: data.text,
detail: data.detail,
severity: data.severity === LanguageStatusSeverity.Error ? Severity.Error : data.severity === LanguageStatusSeverity.Warning ? Severity.Warning : Severity.Info,
command: data.command && this._commands.toInternal(data.command, commandDisposables)
});
}, 0);
};

const result: vscode.LanguageStatusItem = {
dispose() {
commandDisposables.dispose();
soonHandle?.dispose();
proxy.$removeLanguageStatus(handle);
},
get id() {
return data.id;
},
get name() {
return data.name;
},
set name(value) {
data.name = value;
updateAsync();
},
get selector() {
return data.selector;
},
Expand Down Expand Up @@ -121,9 +148,12 @@ export class ExtHostLanguages {
data.severity = value;
updateAsync();
},
dispose() {
soonHandle?.dispose();
proxy.$removeLanguageStatus(handle);
get command() {
return data.command;
},
set command(value) {
data.command = value;
updateAsync();
}
};
updateAsync();
Expand Down
64 changes: 3 additions & 61 deletions src/vs/workbench/browser/parts/editor/editorStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorE
import { ConfigurationChangedEvent, IEditorOptions, EditorOption } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { deepClone, equals } from 'vs/base/common/objects';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Schemas } from 'vs/base/common/network';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
Expand All @@ -50,11 +50,10 @@ import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessi
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment, IStatusbarEntry } from 'vs/workbench/services/statusbar/browser/statusbar';
import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platform/markers/common/markers';
import { STATUS_BAR_ERROR_ITEM_BACKGROUND, STATUS_BAR_ERROR_ITEM_FOREGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND, STATUS_BAR_WARNING_ITEM_BACKGROUND, STATUS_BAR_WARNING_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
import { ThemeColor, themeColorFromId } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_FOREGROUND } from 'vs/workbench/common/theme';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
import { ILanguageStatus, ILanguageStatusService } from 'vs/workbench/services/languageStatus/common/languageStatusService';
import { AutomaticLanguageDetectionLikelyWrongClassification, AutomaticLanguageDetectionLikelyWrongId, IAutomaticLanguageDetectionLikelyWrongData, ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService';

class SideBySideEditorEncodingSupport implements IEncodingSupport {
Expand Down Expand Up @@ -182,7 +181,6 @@ class StateChange {
type StateDelta = (
{ type: 'selectionStatus'; selectionStatus: string | undefined; }
| { type: 'mode'; mode: string | undefined; }
| { type: 'languageStatus'; status: ILanguageStatus[] | undefined; }
| { type: 'encoding'; encoding: string | undefined; }
| { type: 'EOL'; EOL: string | undefined; }
| { type: 'indentation'; indentation: string | undefined; }
Expand All @@ -200,9 +198,6 @@ class State {
private _mode: string | undefined;
get mode(): string | undefined { return this._mode; }

private _status: ILanguageStatus[] | undefined;
get status(): ILanguageStatus[] | undefined { return this._status; }

private _encoding: string | undefined;
get encoding(): string | undefined { return this._encoding; }

Expand Down Expand Up @@ -248,13 +243,6 @@ class State {
}
}

if (update.type === 'languageStatus') {
if (!equals(this._status, update.status)) {
this._status = update.status;
change.languageStatus = true;
}
}

if (update.type === 'encoding') {
if (this._encoding !== update.encoding) {
this._encoding = update.encoding;
Expand Down Expand Up @@ -318,7 +306,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
private readonly encodingElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly eolElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly modeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly statusElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly metadataElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());
private readonly currentProblemStatus: ShowCurrentMarkerInStatusbarContribution = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution));

Expand All @@ -330,7 +317,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
private promptedScreenReader: boolean = false;

constructor(
@ILanguageStatusService private readonly languageStatusService: ILanguageStatusService,
@IEditorService private readonly editorService: IEditorService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IModeService private readonly modeService: IModeService,
Expand Down Expand Up @@ -559,36 +545,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.updateElement(this.modeElement, props, 'status.editor.mode', StatusbarAlignment.RIGHT, 100.1);
}

private updateStatusElement(status: ILanguageStatus[] | undefined): void {
if (!status || status.length === 0) {
this.statusElement.clear();
return;
}

const [first] = status;

let backgroundColor: ThemeColor | undefined;
let color: ThemeColor | undefined;
if (first.severity === Severity.Error) {
backgroundColor = themeColorFromId(STATUS_BAR_ERROR_ITEM_BACKGROUND);
color = themeColorFromId(STATUS_BAR_ERROR_ITEM_FOREGROUND);
} else if (first.severity === Severity.Warning) {
backgroundColor = themeColorFromId(STATUS_BAR_WARNING_ITEM_BACKGROUND);
color = themeColorFromId(STATUS_BAR_WARNING_ITEM_FOREGROUND);
}

const props: IStatusbarEntry = {
name: localize('status.editor.status', "Language Status"),
text: first.text,
ariaLabel: first.text,
tooltip: first.message,
backgroundColor,
color
};

this.updateElement(this.statusElement, props, 'status.editor.status', StatusbarAlignment.RIGHT, 100.05);
}

private updateMetadataElement(text: string | undefined): void {
if (!text) {
this.metadataElement.clear();
Expand Down Expand Up @@ -645,7 +601,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.updateEncodingElement(this.state.encoding);
this.updateEOLElement(this.state.EOL ? this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF : undefined);
this.updateModeElement(this.state.mode);
this.updateStatusElement(this.state.status);
this.updateMetadataElement(this.state.metadata);
}

Expand Down Expand Up @@ -683,7 +638,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.onScreenReaderModeChange(activeCodeEditor);
this.onSelectionChange(activeCodeEditor);
this.onModeChange(activeCodeEditor, activeInput);
this.onLanguageStatusChange(activeCodeEditor);
this.onEOLChange(activeCodeEditor);
this.onEncodingChange(activeEditorPane, activeCodeEditor);
this.onIndentationChange(activeCodeEditor);
Expand Down Expand Up @@ -717,10 +671,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.onModeChange(activeCodeEditor, activeInput);
}));

this.activeEditorListeners.add(this.languageStatusService.onDidChange(() => {
this.onLanguageStatusChange(activeCodeEditor);
}));

// Hook Listener for content changes
this.activeEditorListeners.add(activeCodeEditor.onDidChangeModelContent((e) => {
this.onEOLChange(activeCodeEditor);
Expand Down Expand Up @@ -787,14 +737,6 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
this.updateState(info);
}

private async onLanguageStatusChange(editorWidget: ICodeEditor | undefined): Promise<void> {
const update: StateDelta = { type: 'languageStatus', status: undefined };
if (editorWidget?.hasModel()) {
update.status = await this.languageStatusService.getLanguageStatus(editorWidget.getModel());
}
this.updateState(update);
}

private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
const update: StateDelta = { type: 'indentation', indentation: undefined };

Expand Down
Loading