From 90e3aea0f25529426d565ecd4efa09d926699cf7 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 28 Nov 2022 13:51:32 -0800 Subject: [PATCH] Add keydown handler for rendered markdown strings (#166999) Ref #163086 Ref #159088 --- src/vs/base/browser/formattedTextRenderer.ts | 3 +- src/vs/base/browser/markdownRenderer.ts | 33 ++++++++++++------- .../untitledTextEditorHint.ts | 4 +-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/vs/base/browser/formattedTextRenderer.ts b/src/vs/base/browser/formattedTextRenderer.ts index 2ae545682cdca..0355a7ac51d85 100644 --- a/src/vs/base/browser/formattedTextRenderer.ts +++ b/src/vs/base/browser/formattedTextRenderer.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; import { DisposableStore } from 'vs/base/common/lifecycle'; export interface IContentActionHandler { - callback: (content: string, event: IMouseEvent) => void; + callback: (content: string, event: IMouseEvent | IKeyboardEvent) => void; readonly disposables: DisposableStore; } diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index aadf5d2c87a32..e4ac43d8c83ee 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -7,6 +7,7 @@ import * as DOM from 'vs/base/browser/dom'; import * as dompurify from 'vs/base/browser/dompurify/dompurify'; import { DomEmitter } from 'vs/base/browser/event'; import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -14,6 +15,7 @@ import { Event } from 'vs/base/common/event'; import { IMarkdownString, escapeDoubleQuotes, parseHrefAndDimensions, removeMarkdownEscapes, MarkdownStringTrustedOptions } from 'vs/base/common/htmlContent'; import { markdownEscapeEscapedIcons } from 'vs/base/common/iconLabels'; import { defaultGenerator } from 'vs/base/common/idGenerator'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; @@ -158,15 +160,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } if (options.actionHandler) { - const onClick = options.actionHandler.disposables.add(new DomEmitter(element, 'click')); - const onAuxClick = options.actionHandler.disposables.add(new DomEmitter(element, 'auxclick')); - options.actionHandler.disposables.add(Event.any(onClick.event, onAuxClick.event)(e => { - const mouseEvent = new StandardMouseEvent(e); - if (!mouseEvent.leftButton && !mouseEvent.middleButton) { - return; - } - - let target: HTMLElement | null = mouseEvent.target; + const _activateLink = function (event: StandardMouseEvent | StandardKeyboardEvent): void { + let target: HTMLElement | null = event.target; if (target.tagName !== 'A') { target = target.parentElement; if (!target || target.tagName !== 'A') { @@ -179,13 +174,29 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende if (markdown.baseUri) { href = resolveWithBaseUri(URI.from(markdown.baseUri), href); } - options.actionHandler!.callback(href, mouseEvent); + options.actionHandler!.callback(href, event); } } catch (err) { onUnexpectedError(err); } finally { - mouseEvent.preventDefault(); + event.preventDefault(); + } + }; + const onClick = options.actionHandler.disposables.add(new DomEmitter(element, 'click')); + const onAuxClick = options.actionHandler.disposables.add(new DomEmitter(element, 'auxclick')); + options.actionHandler.disposables.add(Event.any(onClick.event, onAuxClick.event)(e => { + const mouseEvent = new StandardMouseEvent(e); + if (!mouseEvent.leftButton && !mouseEvent.middleButton) { + return; + } + _activateLink(mouseEvent); + })); + options.actionHandler.disposables.add(DOM.addDisposableListener(element, 'keydown', (e) => { + const keyboardEvent = new StandardKeyboardEvent(e); + if (!keyboardEvent.equals(KeyCode.Space) && !keyboardEvent.equals(KeyCode.Enter)) { + return; } + _activateLink(keyboardEvent); })); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts index 6539a6441eccb..ed0849a17421e 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts @@ -141,7 +141,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { } // the actual command handlers... - const languageOnClickOrTap = async (e: MouseEvent) => { + const languageOnClickOrTap = async (e: UIEvent) => { e.stopPropagation(); // Need to focus editor before so current editor becomes active and the command is properly executed this.editor.focus(); @@ -149,7 +149,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { this.editor.focus(); }; - const chooseEditorOnClickOrTap = async (e: MouseEvent) => { + const chooseEditorOnClickOrTap = async (e: UIEvent) => { e.stopPropagation(); const activeEditorInput = this.editorGroupsService.activeGroup.activeEditor;