Skip to content

Commit 90e3aea

Browse files
authored
Add keydown handler for rendered markdown strings (microsoft#166999)
Ref microsoft#163086 Ref microsoft#159088
1 parent 14cd314 commit 90e3aea

File tree

3 files changed

+26
-14
lines changed

3 files changed

+26
-14
lines changed

src/vs/base/browser/formattedTextRenderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import * as DOM from 'vs/base/browser/dom';
7+
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
78
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
89
import { DisposableStore } from 'vs/base/common/lifecycle';
910

1011
export interface IContentActionHandler {
11-
callback: (content: string, event: IMouseEvent) => void;
12+
callback: (content: string, event: IMouseEvent | IKeyboardEvent) => void;
1213
readonly disposables: DisposableStore;
1314
}
1415

src/vs/base/browser/markdownRenderer.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ import * as DOM from 'vs/base/browser/dom';
77
import * as dompurify from 'vs/base/browser/dompurify/dompurify';
88
import { DomEmitter } from 'vs/base/browser/event';
99
import { createElement, FormattedTextRenderOptions } from 'vs/base/browser/formattedTextRenderer';
10+
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
1011
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
1112
import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels';
1213
import { onUnexpectedError } from 'vs/base/common/errors';
1314
import { Event } from 'vs/base/common/event';
1415
import { IMarkdownString, escapeDoubleQuotes, parseHrefAndDimensions, removeMarkdownEscapes, MarkdownStringTrustedOptions } from 'vs/base/common/htmlContent';
1516
import { markdownEscapeEscapedIcons } from 'vs/base/common/iconLabels';
1617
import { defaultGenerator } from 'vs/base/common/idGenerator';
18+
import { KeyCode } from 'vs/base/common/keyCodes';
1719
import { Lazy } from 'vs/base/common/lazy';
1820
import { DisposableStore } from 'vs/base/common/lifecycle';
1921
import { marked } from 'vs/base/common/marked/marked';
@@ -158,15 +160,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
158160
}
159161

160162
if (options.actionHandler) {
161-
const onClick = options.actionHandler.disposables.add(new DomEmitter(element, 'click'));
162-
const onAuxClick = options.actionHandler.disposables.add(new DomEmitter(element, 'auxclick'));
163-
options.actionHandler.disposables.add(Event.any(onClick.event, onAuxClick.event)(e => {
164-
const mouseEvent = new StandardMouseEvent(e);
165-
if (!mouseEvent.leftButton && !mouseEvent.middleButton) {
166-
return;
167-
}
168-
169-
let target: HTMLElement | null = mouseEvent.target;
163+
const _activateLink = function (event: StandardMouseEvent | StandardKeyboardEvent): void {
164+
let target: HTMLElement | null = event.target;
170165
if (target.tagName !== 'A') {
171166
target = target.parentElement;
172167
if (!target || target.tagName !== 'A') {
@@ -179,13 +174,29 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
179174
if (markdown.baseUri) {
180175
href = resolveWithBaseUri(URI.from(markdown.baseUri), href);
181176
}
182-
options.actionHandler!.callback(href, mouseEvent);
177+
options.actionHandler!.callback(href, event);
183178
}
184179
} catch (err) {
185180
onUnexpectedError(err);
186181
} finally {
187-
mouseEvent.preventDefault();
182+
event.preventDefault();
183+
}
184+
};
185+
const onClick = options.actionHandler.disposables.add(new DomEmitter(element, 'click'));
186+
const onAuxClick = options.actionHandler.disposables.add(new DomEmitter(element, 'auxclick'));
187+
options.actionHandler.disposables.add(Event.any(onClick.event, onAuxClick.event)(e => {
188+
const mouseEvent = new StandardMouseEvent(e);
189+
if (!mouseEvent.leftButton && !mouseEvent.middleButton) {
190+
return;
191+
}
192+
_activateLink(mouseEvent);
193+
}));
194+
options.actionHandler.disposables.add(DOM.addDisposableListener(element, 'keydown', (e) => {
195+
const keyboardEvent = new StandardKeyboardEvent(e);
196+
if (!keyboardEvent.equals(KeyCode.Space) && !keyboardEvent.equals(KeyCode.Enter)) {
197+
return;
188198
}
199+
_activateLink(keyboardEvent);
189200
}));
190201
}
191202

src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,15 @@ class UntitledTextEditorHintContentWidget implements IContentWidget {
141141
}
142142

143143
// the actual command handlers...
144-
const languageOnClickOrTap = async (e: MouseEvent) => {
144+
const languageOnClickOrTap = async (e: UIEvent) => {
145145
e.stopPropagation();
146146
// Need to focus editor before so current editor becomes active and the command is properly executed
147147
this.editor.focus();
148148
await this.commandService.executeCommand(ChangeLanguageAction.ID, { from: 'hint' });
149149
this.editor.focus();
150150
};
151151

152-
const chooseEditorOnClickOrTap = async (e: MouseEvent) => {
152+
const chooseEditorOnClickOrTap = async (e: UIEvent) => {
153153
e.stopPropagation();
154154

155155
const activeEditorInput = this.editorGroupsService.activeGroup.activeEditor;

0 commit comments

Comments
 (0)