Skip to content

Commit

Permalink
Adding the possibility to enrich hovers with additional data (#210472)
Browse files Browse the repository at this point in the history
Adding the possibility to enrich hovers with additional data
  • Loading branch information
aiday-mar authored Apr 19, 2024
1 parent 9cae075 commit b8eba5d
Show file tree
Hide file tree
Showing 26 changed files with 717 additions and 96 deletions.
42 changes: 27 additions & 15 deletions src/vs/base/browser/ui/hover/hoverWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,8 @@ export class HoverAction extends Disposable {
const label = dom.append(this.action, $('span'));
label.textContent = keybindingLabel ? `${actionOptions.label} (${keybindingLabel})` : actionOptions.label;

this._register(dom.addDisposableListener(this.actionContainer, dom.EventType.CLICK, e => {
e.stopPropagation();
e.preventDefault();
actionOptions.run(this.actionContainer);
}));

this._register(dom.addDisposableListener(this.actionContainer, dom.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
e.stopPropagation();
e.preventDefault();
actionOptions.run(this.actionContainer);
}
}));

this._store.add(new ClickAction(this.actionContainer, actionOptions.run));
this._store.add(new KeyDownAction(this.actionContainer, actionOptions.run, [KeyCode.Enter, KeyCode.Space]));
this.setEnabled(true);
}

Expand All @@ -99,3 +86,28 @@ export class HoverAction extends Disposable {
export function getHoverAccessibleViewHint(shouldHaveHint?: boolean, keybinding?: string | null): string | undefined {
return shouldHaveHint && keybinding ? localize('acessibleViewHint', "Inspect this in the accessible view with {0}.", keybinding) : shouldHaveHint ? localize('acessibleViewHintNoKbOpen', "Inspect this in the accessible view via the command Open Accessible View which is currently not triggerable via keybinding.") : '';
}

export class ClickAction extends Disposable {
constructor(container: HTMLElement, run: (container: HTMLElement) => void) {
super();
this._register(dom.addDisposableListener(container, dom.EventType.CLICK, e => {
e.stopPropagation();
e.preventDefault();
run(container);
}));
}
}

export class KeyDownAction extends Disposable {
constructor(container: HTMLElement, run: (container: HTMLElement) => void, keyCodes: KeyCode[]) {
super();
this._register(dom.addDisposableListener(container, dom.EventType.KEY_DOWN, e => {
const event = new StandardKeyboardEvent(e);
if (keyCodes.some(keyCode => event.equals(keyCode))) {
e.stopPropagation();
e.preventDefault();
run(container);
}
}));
}
}
38 changes: 35 additions & 3 deletions src/vs/editor/common/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,19 +168,51 @@ export interface Hover {
* current position itself.
*/
range?: IRange;

/**
* Can increase the verbosity of the hover
*/
canIncreaseVerbosity?: boolean;

/**
* Can decrease the verbosity of the hover
*/
canDecreaseVerbosity?: boolean;
}

/**
* The hover provider interface defines the contract between extensions and
* the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature.
*/
export interface HoverProvider {
export interface HoverProvider<THover = Hover> {
/**
* Provide a hover for the given position and document. Multiple hovers at the same
* Provide a hover for the given position, context and document. Multiple hovers at the same
* position will be merged by the editor. A hover can have a range which defaults
* to the word range at the position when omitted.
*/
provideHover(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult<Hover>;
provideHover(model: model.ITextModel, position: Position, token: CancellationToken, context?: HoverContext<THover>): ProviderResult<Hover>;
}

export interface HoverContext<THover = Hover> {
/**
* Whether to increase or decrease the hover's verbosity
*/
action?: HoverVerbosityAction;
/**
* The previous hover for the same position
*/
previousHover?: THover;
}

export enum HoverVerbosityAction {
/**
* Increase the verbosity of the hover
*/
Increase,
/**
* Decrease the verbosity of the hover
*/
Decrease
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/vs/editor/common/standalone/standaloneEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,17 @@ export enum GlyphMarginLane {
Right = 3
}

export enum HoverVerbosityAction {
/**
* Increase the verbosity of the hover
*/
Increase = 0,
/**
* Decrease the verbosity of the hover
*/
Decrease = 1
}

/**
* Describes what to do with the indentation when pressing Enter.
*/
Expand Down
15 changes: 14 additions & 1 deletion src/vs/editor/contrib/hover/browser/contentHoverController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import { HoverOperation, HoverStartMode, HoverStartSource } from 'vs/editor/cont
import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IEditorHoverColorPickerWidget, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IHoverWidget } from 'vs/editor/contrib/hover/browser/hoverTypes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { MarkdownHoverParticipant } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant';
import { InlayHintsHover } from 'vs/editor/contrib/inlayHints/browser/inlayHintsHover';
import { HoverVerbosityAction } from 'vs/editor/common/standalone/standaloneEnums';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/browser/contentHoverWidget';
import { ContentHoverComputer } from 'vs/editor/contrib/hover/browser/contentHoverComputer';
import { ContentHoverVisibleData, HoverResult } from 'vs/editor/contrib/hover/browser/contentHoverTypes';
Expand All @@ -28,6 +31,8 @@ export class ContentHoverController extends Disposable implements IHoverWidget {
private readonly _computer: ContentHoverComputer;
private readonly _widget: ContentHoverWidget;
private readonly _participants: IEditorHoverParticipant[];
// TODO@aiday-mar make array of participants, dispatch between them
private readonly _markdownHoverParticipant: MarkdownHoverParticipant | undefined;
private readonly _hoverOperation: HoverOperation<IHoverPart>;

constructor(
Expand All @@ -42,7 +47,11 @@ export class ContentHoverController extends Disposable implements IHoverWidget {
// Instantiate participants and sort them by `hoverOrdinal` which is relevant for rendering order.
this._participants = [];
for (const participant of HoverParticipantRegistry.getAll()) {
this._participants.push(this._instantiationService.createInstance(participant, this._editor));
const participantInstance = this._instantiationService.createInstance(participant, this._editor);
if (participantInstance instanceof MarkdownHoverParticipant && !(participantInstance instanceof InlayHintsHover)) {
this._markdownHoverParticipant = participantInstance;
}
this._participants.push(participantInstance);
}
this._participants.sort((p1, p2) => p1.hoverOrdinal - p2.hoverOrdinal);

Expand Down Expand Up @@ -342,6 +351,10 @@ export class ContentHoverController extends Disposable implements IHoverWidget {
this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null);
}

public async updateFocusedMarkdownHoverVerbosityLevel(action: HoverVerbosityAction): Promise<void> {
this._markdownHoverParticipant?.updateFocusedMarkdownHoverPartVerbosityLevel(action);
}

public getWidgetContent(): string | undefined {
const node = this._widget.getDomNode();
if (!node.textContent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class EditorHoverStatusBar extends Disposable implements IEditorHoverStat
) {
super();
this.hoverElement = $('div.hover-row.status-bar');
this.hoverElement.tabIndex = 0;
this.actionsElement = dom.append(this.hoverElement, $('div.actions'));
}

Expand Down
27 changes: 14 additions & 13 deletions src/vs/editor/contrib/hover/browser/getHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,32 @@ export class HoverProviderResult {
) { }
}

/**
* Does not throw or return a rejected promise (returns undefined instead).
*/
async function executeProvider(provider: HoverProvider, ordinal: number, model: ITextModel, position: Position, token: CancellationToken): Promise<HoverProviderResult | undefined> {
try {
const result = await Promise.resolve(provider.provideHover(model, position, token));
if (result && isValid(result)) {
return new HoverProviderResult(provider, result, ordinal);
}
} catch (err) {
onUnexpectedExternalError(err);
const result = await Promise
.resolve(provider.provideHover(model, position, token))
.catch(onUnexpectedExternalError);
if (!result || !isValid(result)) {
return undefined;
}
return undefined;
return new HoverProviderResult(provider, result, ordinal);
}

export function getHover(registry: LanguageFeatureRegistry<HoverProvider>, model: ITextModel, position: Position, token: CancellationToken): AsyncIterableObject<HoverProviderResult> {
export function getHoverProviderResultsAsAsyncIterable(registry: LanguageFeatureRegistry<HoverProvider>, model: ITextModel, position: Position, token: CancellationToken): AsyncIterableObject<HoverProviderResult> {
const providers = registry.ordered(model);
const promises = providers.map((provider, index) => executeProvider(provider, index, model, position, token));
return AsyncIterableObject.fromPromises(promises).coalesce();
}

export function getHoverPromise(registry: LanguageFeatureRegistry<HoverProvider>, model: ITextModel, position: Position, token: CancellationToken): Promise<Hover[]> {
return getHover(registry, model, position, token).map(item => item.hover).toPromise();
export function getHoversPromise(registry: LanguageFeatureRegistry<HoverProvider>, model: ITextModel, position: Position, token: CancellationToken): Promise<Hover[]> {
return getHoverProviderResultsAsAsyncIterable(registry, model, position, token).map(item => item.hover).toPromise();
}

registerModelAndPositionCommand('_executeHoverProvider', (accessor, model, position) => {
registerModelAndPositionCommand('_executeHoverProvider', (accessor, model, position): Promise<Hover[]> => {
const languageFeaturesService = accessor.get(ILanguageFeaturesService);
return getHoverPromise(languageFeaturesService.hoverProvider, model, position, CancellationToken.None);
return getHoversPromise(languageFeaturesService.hoverProvider, model, position, CancellationToken.None);
});

function isValid(result: Hover) {
Expand Down
22 changes: 22 additions & 0 deletions src/vs/editor/contrib/hover/browser/hover.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@
flex-direction: column;
}

.monaco-editor .monaco-hover .hover-row .verbosity-actions {
display: flex;
flex-direction: column;
padding-left: 5px;
padding-right: 5px;
justify-content: end;
border-right: 1px solid var(--vscode-editorHoverWidget-border);
}

.monaco-editor .monaco-hover .hover-row .verbosity-actions .codicon {
cursor: pointer;
font-size: 11px;
}

.monaco-editor .monaco-hover .hover-row .verbosity-actions .codicon.enabled {
color: var(--vscode-textLink-foreground);
}

.monaco-editor .monaco-hover .hover-row .verbosity-actions .codicon.disabled {
opacity: 0.6;
}

.monaco-editor .monaco-hover .hover-row .actions {
background-color: var(--vscode-editorHoverWidget-statusBarBackground);
}
Expand Down
2 changes: 2 additions & 0 deletions src/vs/editor/contrib/hover/browser/hoverActionIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export const PAGE_UP_HOVER_ACTION_ID = 'editor.action.pageUpHover';
export const PAGE_DOWN_HOVER_ACTION_ID = 'editor.action.pageDownHover';
export const GO_TO_TOP_HOVER_ACTION_ID = 'editor.action.goToTopHover';
export const GO_TO_BOTTOM_HOVER_ACTION_ID = 'editor.action.goToBottomHover';
export const INCREASE_HOVER_VERBOSITY_ACTION_ID = 'editor.action.increaseHoverVerbosityLevel';
export const DECREASE_HOVER_VERBOSITY_ACTION_ID = 'editor.action.decreaseHoverVerbosityLevel';
51 changes: 50 additions & 1 deletion src/vs/editor/contrib/hover/browser/hoverActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { GO_TO_BOTTOM_HOVER_ACTION_ID, GO_TO_TOP_HOVER_ACTION_ID, PAGE_DOWN_HOVER_ACTION_ID, PAGE_UP_HOVER_ACTION_ID, SCROLL_DOWN_HOVER_ACTION_ID, SCROLL_LEFT_HOVER_ACTION_ID, SCROLL_RIGHT_HOVER_ACTION_ID, SCROLL_UP_HOVER_ACTION_ID, SHOW_DEFINITION_PREVIEW_HOVER_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from 'vs/editor/contrib/hover/browser/hoverActionIds';
import { DECREASE_HOVER_VERBOSITY_ACTION_ID, GO_TO_BOTTOM_HOVER_ACTION_ID, GO_TO_TOP_HOVER_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, PAGE_DOWN_HOVER_ACTION_ID, PAGE_UP_HOVER_ACTION_ID, SCROLL_DOWN_HOVER_ACTION_ID, SCROLL_LEFT_HOVER_ACTION_ID, SCROLL_RIGHT_HOVER_ACTION_ID, SCROLL_UP_HOVER_ACTION_ID, SHOW_DEFINITION_PREVIEW_HOVER_ACTION_ID, SHOW_OR_FOCUS_HOVER_ACTION_ID } from 'vs/editor/contrib/hover/browser/hoverActionIds';
import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
Expand All @@ -15,6 +15,7 @@ import { HoverStartMode, HoverStartSource } from 'vs/editor/contrib/hover/browse
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { HoverController } from 'vs/editor/contrib/hover/browser/hoverController';
import { HoverVerbosityAction } from 'vs/editor/common/languages';
import * as nls from 'vs/nls';
import 'vs/css!./hover';

Expand Down Expand Up @@ -418,3 +419,51 @@ export class GoToBottomHoverAction extends EditorAction {
controller.goToBottom();
}
}

export class IncreaseHoverVerbosityLevel extends EditorAction {

constructor() {
super({
id: INCREASE_HOVER_VERBOSITY_ACTION_ID,
label: nls.localize({
key: 'increaseHoverVerbosityLevel',
comment: ['Label for action that will increase the hover verbosity level.']
}, "Increase Hover Verbosity Level"),
alias: 'Increase Hover Verbosity Level',
precondition: EditorContextKeys.hoverFocused,
kbOpts: {
kbExpr: EditorContextKeys.hoverFocused,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyP),
weight: KeybindingWeight.EditorContrib
}
});
}

public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
HoverController.get(editor)?.updateFocusedMarkdownHoverVerbosityLevel(HoverVerbosityAction.Increase);
}
}

export class DecreaseHoverVerbosityLevel extends EditorAction {

constructor() {
super({
id: DECREASE_HOVER_VERBOSITY_ACTION_ID,
label: nls.localize({
key: 'decreaseHoverVerbosityLevel',
comment: ['Label for action that will decrease the hover verbosity level.']
}, "Decrease Hover Verbosity Level"),
alias: 'Decrease Hover Verbosity Level',
precondition: EditorContextKeys.hoverFocused,
kbOpts: {
kbExpr: EditorContextKeys.hoverFocused,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyM),
weight: KeybindingWeight.EditorContrib
}
});
}

public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
HoverController.get(editor)?.updateFocusedMarkdownHoverVerbosityLevel(HoverVerbosityAction.Decrease);
}
}
4 changes: 3 additions & 1 deletion src/vs/editor/contrib/hover/browser/hoverContribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { GoToBottomHoverAction, GoToTopHoverAction, PageDownHoverAction, PageUpHoverAction, ScrollDownHoverAction, ScrollLeftHoverAction, ScrollRightHoverAction, ScrollUpHoverAction, ShowDefinitionPreviewHoverAction, ShowOrFocusHoverAction } from 'vs/editor/contrib/hover/browser/hoverActions';
import { DecreaseHoverVerbosityLevel, GoToBottomHoverAction, GoToTopHoverAction, IncreaseHoverVerbosityLevel, PageDownHoverAction, PageUpHoverAction, ScrollDownHoverAction, ScrollLeftHoverAction, ScrollRightHoverAction, ScrollUpHoverAction, ShowDefinitionPreviewHoverAction, ShowOrFocusHoverAction } from 'vs/editor/contrib/hover/browser/hoverActions';
import { EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { editorHoverBorder } from 'vs/platform/theme/common/colorRegistry';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
Expand All @@ -24,6 +24,8 @@ registerEditorAction(PageUpHoverAction);
registerEditorAction(PageDownHoverAction);
registerEditorAction(GoToTopHoverAction);
registerEditorAction(GoToBottomHoverAction);
registerEditorAction(IncreaseHoverVerbosityLevel);
registerEditorAction(DecreaseHoverVerbosityLevel);
HoverParticipantRegistry.register(MarkdownHoverParticipant);
HoverParticipantRegistry.register(MarkerHoverParticipant);

Expand Down
5 changes: 5 additions & 0 deletions src/vs/editor/contrib/hover/browser/hoverController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { IHoverWidget } from 'vs/editor/contrib/hover/browser/hoverTypes';
import { InlineSuggestionHintsContentWidget } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver';
import { HoverVerbosityAction } from 'vs/editor/common/languages';
import { RunOnceScheduler } from 'vs/base/common/async';
import { ContentHoverWidget } from 'vs/editor/contrib/hover/browser/contentHoverWidget';
import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHoverController';
Expand Down Expand Up @@ -401,6 +402,10 @@ export class HoverController extends Disposable implements IEditorContribution {
return this._contentWidget?.widget.isResizing || false;
}

public updateFocusedMarkdownHoverVerbosityLevel(action: HoverVerbosityAction): void {
this._getOrCreateContentWidget().updateFocusedMarkdownHoverVerbosityLevel(action);
}

public focus(): void {
this._contentWidget?.focus();
}
Expand Down
Loading

0 comments on commit b8eba5d

Please sign in to comment.