Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as dom from '../../../../base/browser/dom.js';
7
import { status } from '../../../../base/browser/ui/aria/aria.js';
8
import { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js';
9
import { Event } from '../../../../base/common/event.js';
10
import { ResolvedKeybinding } from '../../../../base/common/keybindings.js';
11
import { Disposable } from '../../../../base/common/lifecycle.js';
12
import { OS } from '../../../../base/common/platform.js';
13
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../editor/browser/editorBrowser.js';
14
import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js';
15
import { Position } from '../../../../editor/common/core/position.js';
16
import { localize } from '../../../../nls.js';
17
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
18
import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
19
import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js';
20
import { AccessibilityCommandId } from '../../accessibility/common/accessibilityCommands.js';
21
import { ReplEditorSettings } from './interactiveCommon.js';
22
23
24
export class ReplInputHintContentWidget extends Disposable implements IContentWidget {
25
26
private static readonly ID = 'replInput.widget.emptyHint';
27
28
private domNode: HTMLElement | undefined;
29
private ariaLabel: string = '';
30
private label: KeybindingLabel | undefined;
31
32
constructor(
33
private readonly editor: ICodeEditor,
34
@IConfigurationService private readonly configurationService: IConfigurationService,
35
@IKeybindingService private readonly keybindingService: IKeybindingService,
36
) {
37
super();
38
39
this._register(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => {
40
if (this.domNode && e.hasChanged(EditorOption.fontInfo)) {
41
this.editor.applyFontInfo(this.domNode);
42
}
43
}));
44
const onDidFocusEditorText = Event.debounce(this.editor.onDidFocusEditorText, () => undefined, 500);
45
this._register(onDidFocusEditorText(() => {
46
if (this.editor.hasTextFocus() && this.ariaLabel && configurationService.getValue(AccessibilityVerbositySettingId.ReplEditor)) {
47
status(this.ariaLabel);
48
}
49
}));
50
this._register(configurationService.onDidChangeConfiguration(e => {
51
if (e.affectsConfiguration(ReplEditorSettings.executeWithShiftEnter)) {
52
this.setHint();
53
}
54
}));
55
this.editor.addContentWidget(this);
56
}
57
58
getId(): string {
59
return ReplInputHintContentWidget.ID;
60
}
61
62
getPosition(): IContentWidgetPosition | null {
63
return {
64
position: { lineNumber: 1, column: 1 },
65
preference: [ContentWidgetPositionPreference.EXACT]
66
};
67
}
68
69
getDomNode(): HTMLElement {
70
if (!this.domNode) {
71
this.domNode = dom.$('.empty-editor-hint');
72
this.domNode.style.width = 'max-content';
73
this.domNode.style.paddingLeft = '4px';
74
75
this.setHint();
76
77
this._register(dom.addDisposableListener(this.domNode, 'click', () => {
78
this.editor.focus();
79
}));
80
81
this.editor.applyFontInfo(this.domNode);
82
const lineHeight = this.editor.getLineHeightForPosition(new Position(1, 1));
83
this.domNode.style.lineHeight = lineHeight + 'px';
84
}
85
86
return this.domNode;
87
}
88
89
private setHint() {
90
if (!this.domNode) {
91
return;
92
}
93
while (this.domNode.firstChild) {
94
this.domNode.removeChild(this.domNode.firstChild);
95
}
96
97
const hintElement = dom.$('div.empty-hint-text');
98
hintElement.style.cursor = 'text';
99
hintElement.style.whiteSpace = 'nowrap';
100
101
const keybinding = this.getKeybinding();
102
const keybindingHintLabel = keybinding?.getLabel();
103
104
if (keybinding && keybindingHintLabel) {
105
const actionPart = localize('emptyHintText', 'Press {0} to execute. ', keybindingHintLabel);
106
107
const [before, after] = actionPart.split(keybindingHintLabel).map((fragment) => {
108
const hintPart = dom.$('span', undefined, fragment);
109
hintPart.style.fontStyle = 'italic';
110
return hintPart;
111
});
112
113
hintElement.appendChild(before);
114
115
if (this.label) {
116
this.label.dispose();
117
}
118
this.label = this._register(new KeybindingLabel(hintElement, OS));
119
this.label.set(keybinding);
120
this.label.element.style.width = 'min-content';
121
this.label.element.style.display = 'inline';
122
123
hintElement.appendChild(after);
124
this.domNode.append(hintElement);
125
126
const helpKeybinding = this.keybindingService.lookupKeybinding(AccessibilityCommandId.OpenAccessibilityHelp)?.getLabel();
127
const helpInfo = helpKeybinding
128
? localize('ReplInputAriaLabelHelp', "Use {0} for accessibility help. ", helpKeybinding)
129
: localize('ReplInputAriaLabelHelpNoKb', "Run the Open Accessibility Help command for more information. ");
130
131
this.ariaLabel = actionPart.concat(helpInfo, localize('disableHint', ' Toggle {0} in settings to disable this hint.', AccessibilityVerbositySettingId.ReplEditor));
132
}
133
}
134
135
private getKeybinding() {
136
const keybindings = this.keybindingService.lookupKeybindings('interactive.execute');
137
const shiftEnterConfig = this.configurationService.getValue(ReplEditorSettings.executeWithShiftEnter);
138
const hasEnterChord = (kb: ResolvedKeybinding, modifier: string = '') => {
139
const chords = kb.getDispatchChords();
140
const chord = modifier + 'Enter';
141
const chordAlt = modifier + '[Enter]';
142
return chords.length === 1 && (chords[0] === chord || chords[0] === chordAlt);
143
};
144
145
if (shiftEnterConfig) {
146
const keybinding = keybindings.find(kb => hasEnterChord(kb, 'shift+'));
147
if (keybinding) {
148
return keybinding;
149
}
150
} else {
151
let keybinding = keybindings.find(kb => hasEnterChord(kb));
152
if (keybinding) {
153
return keybinding;
154
}
155
keybinding = this.keybindingService.lookupKeybindings('python.execInREPLEnter')
156
.find(kb => hasEnterChord(kb));
157
if (keybinding) {
158
return keybinding;
159
}
160
}
161
162
return keybindings?.[0];
163
}
164
165
override dispose(): void {
166
super.dispose();
167
this.editor.removeContentWidget(this);
168
this.label?.dispose();
169
}
170
}
171
172