123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
- return c > 3 && r && Object.defineProperty(target, key, r), r;
- };
- var __param = (this && this.__param) || function (paramIndex, decorator) {
- return function (target, key) { decorator(target, key, paramIndex); }
- };
- import * as dom from '../../../base/browser/dom.js';
- import * as aria from '../../../base/browser/ui/aria/aria.js';
- import { DomScrollableElement } from '../../../base/browser/ui/scrollbar/scrollableElement.js';
- import { Codicon } from '../../../base/common/codicons.js';
- import { Event } from '../../../base/common/event.js';
- import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js';
- import { escapeRegExpCharacters } from '../../../base/common/strings.js';
- import { assertIsDefined } from '../../../base/common/types.js';
- import './parameterHints.css';
- import { MarkdownRenderer } from '../../browser/core/markdownRenderer.js';
- import { IModeService } from '../../common/services/modeService.js';
- import { ParameterHintsModel } from './parameterHintsModel.js';
- import { Context } from './provideSignatureHelp.js';
- import * as nls from '../../../nls.js';
- import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
- import { IOpenerService } from '../../../platform/opener/common/opener.js';
- import { editorHoverBackground, editorHoverBorder, editorHoverForeground, registerColor, textCodeBlockBackground, textLinkActiveForeground, textLinkForeground, listHighlightForeground } from '../../../platform/theme/common/colorRegistry.js';
- import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';
- import { ColorScheme } from '../../../platform/theme/common/theme.js';
- import { registerThemingParticipant, ThemeIcon } from '../../../platform/theme/common/themeService.js';
- const $ = dom.$;
- const parameterHintsNextIcon = registerIcon('parameter-hints-next', Codicon.chevronDown, nls.localize('parameterHintsNextIcon', 'Icon for show next parameter hint.'));
- const parameterHintsPreviousIcon = registerIcon('parameter-hints-previous', Codicon.chevronUp, nls.localize('parameterHintsPreviousIcon', 'Icon for show previous parameter hint.'));
- let ParameterHintsWidget = class ParameterHintsWidget extends Disposable {
- constructor(editor, contextKeyService, openerService, modeService) {
- super();
- this.editor = editor;
- this.renderDisposeables = this._register(new DisposableStore());
- this.visible = false;
- this.announcedLabel = null;
- // Editor.IContentWidget.allowEditorOverflow
- this.allowEditorOverflow = true;
- this.markdownRenderer = this._register(new MarkdownRenderer({ editor }, modeService, openerService));
- this.model = this._register(new ParameterHintsModel(editor));
- this.keyVisible = Context.Visible.bindTo(contextKeyService);
- this.keyMultipleSignatures = Context.MultipleSignatures.bindTo(contextKeyService);
- this._register(this.model.onChangedHints(newParameterHints => {
- if (newParameterHints) {
- this.show();
- this.render(newParameterHints);
- }
- else {
- this.hide();
- }
- }));
- }
- createParameterHintDOMNodes() {
- const element = $('.editor-widget.parameter-hints-widget');
- const wrapper = dom.append(element, $('.phwrapper'));
- wrapper.tabIndex = -1;
- const controls = dom.append(wrapper, $('.controls'));
- const previous = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsPreviousIcon)));
- const overloads = dom.append(controls, $('.overloads'));
- const next = dom.append(controls, $('.button' + ThemeIcon.asCSSSelector(parameterHintsNextIcon)));
- this._register(dom.addDisposableListener(previous, 'click', e => {
- dom.EventHelper.stop(e);
- this.previous();
- }));
- this._register(dom.addDisposableListener(next, 'click', e => {
- dom.EventHelper.stop(e);
- this.next();
- }));
- const body = $('.body');
- const scrollbar = new DomScrollableElement(body, {});
- this._register(scrollbar);
- wrapper.appendChild(scrollbar.getDomNode());
- const signature = dom.append(body, $('.signature'));
- const docs = dom.append(body, $('.docs'));
- element.style.userSelect = 'text';
- this.domNodes = {
- element,
- signature,
- overloads,
- docs,
- scrollbar,
- };
- this.editor.addContentWidget(this);
- this.hide();
- this._register(this.editor.onDidChangeCursorSelection(e => {
- if (this.visible) {
- this.editor.layoutContentWidget(this);
- }
- }));
- const updateFont = () => {
- if (!this.domNodes) {
- return;
- }
- const fontInfo = this.editor.getOption(43 /* fontInfo */);
- this.domNodes.element.style.fontSize = `${fontInfo.fontSize}px`;
- this.domNodes.element.style.lineHeight = `${fontInfo.lineHeight / fontInfo.fontSize}`;
- };
- updateFont();
- this._register(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor))
- .filter(e => e.hasChanged(43 /* fontInfo */))
- .on(updateFont, null));
- this._register(this.editor.onDidLayoutChange(e => this.updateMaxHeight()));
- this.updateMaxHeight();
- }
- show() {
- if (this.visible) {
- return;
- }
- if (!this.domNodes) {
- this.createParameterHintDOMNodes();
- }
- this.keyVisible.set(true);
- this.visible = true;
- setTimeout(() => {
- if (this.domNodes) {
- this.domNodes.element.classList.add('visible');
- }
- }, 100);
- this.editor.layoutContentWidget(this);
- }
- hide() {
- this.renderDisposeables.clear();
- if (!this.visible) {
- return;
- }
- this.keyVisible.reset();
- this.visible = false;
- this.announcedLabel = null;
- if (this.domNodes) {
- this.domNodes.element.classList.remove('visible');
- }
- this.editor.layoutContentWidget(this);
- }
- getPosition() {
- if (this.visible) {
- return {
- position: this.editor.getPosition(),
- preference: [1 /* ABOVE */, 2 /* BELOW */]
- };
- }
- return null;
- }
- render(hints) {
- var _a;
- this.renderDisposeables.clear();
- if (!this.domNodes) {
- return;
- }
- const multiple = hints.signatures.length > 1;
- this.domNodes.element.classList.toggle('multiple', multiple);
- this.keyMultipleSignatures.set(multiple);
- this.domNodes.signature.innerText = '';
- this.domNodes.docs.innerText = '';
- const signature = hints.signatures[hints.activeSignature];
- if (!signature) {
- return;
- }
- const code = dom.append(this.domNodes.signature, $('.code'));
- const fontInfo = this.editor.getOption(43 /* fontInfo */);
- code.style.fontSize = `${fontInfo.fontSize}px`;
- code.style.fontFamily = fontInfo.fontFamily;
- const hasParameters = signature.parameters.length > 0;
- const activeParameterIndex = (_a = signature.activeParameter) !== null && _a !== void 0 ? _a : hints.activeParameter;
- if (!hasParameters) {
- const label = dom.append(code, $('span'));
- label.textContent = signature.label;
- }
- else {
- this.renderParameters(code, signature, activeParameterIndex);
- }
- const activeParameter = signature.parameters[activeParameterIndex];
- if (activeParameter === null || activeParameter === void 0 ? void 0 : activeParameter.documentation) {
- const documentation = $('span.documentation');
- if (typeof activeParameter.documentation === 'string') {
- documentation.textContent = activeParameter.documentation;
- }
- else {
- const renderedContents = this.renderMarkdownDocs(activeParameter.documentation);
- documentation.appendChild(renderedContents.element);
- }
- dom.append(this.domNodes.docs, $('p', {}, documentation));
- }
- if (signature.documentation === undefined) {
- /** no op */
- }
- else if (typeof signature.documentation === 'string') {
- dom.append(this.domNodes.docs, $('p', {}, signature.documentation));
- }
- else {
- const renderedContents = this.renderMarkdownDocs(signature.documentation);
- dom.append(this.domNodes.docs, renderedContents.element);
- }
- const hasDocs = this.hasDocs(signature, activeParameter);
- this.domNodes.signature.classList.toggle('has-docs', hasDocs);
- this.domNodes.docs.classList.toggle('empty', !hasDocs);
- this.domNodes.overloads.textContent =
- String(hints.activeSignature + 1).padStart(hints.signatures.length.toString().length, '0') + '/' + hints.signatures.length;
- if (activeParameter) {
- let labelToAnnounce = '';
- const param = signature.parameters[activeParameterIndex];
- if (Array.isArray(param.label)) {
- labelToAnnounce = signature.label.substring(param.label[0], param.label[1]);
- }
- else {
- labelToAnnounce = param.label;
- }
- if (param.documentation) {
- labelToAnnounce += typeof param.documentation === 'string' ? `, ${param.documentation}` : `, ${param.documentation.value}`;
- }
- if (signature.documentation) {
- labelToAnnounce += typeof signature.documentation === 'string' ? `, ${signature.documentation}` : `, ${signature.documentation.value}`;
- }
- // Select method gets called on every user type while parameter hints are visible.
- // We do not want to spam the user with same announcements, so we only announce if the current parameter changed.
- if (this.announcedLabel !== labelToAnnounce) {
- aria.alert(nls.localize('hint', "{0}, hint", labelToAnnounce));
- this.announcedLabel = labelToAnnounce;
- }
- }
- this.editor.layoutContentWidget(this);
- this.domNodes.scrollbar.scanDomNode();
- }
- renderMarkdownDocs(markdown) {
- const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(markdown, {
- asyncRenderCallback: () => {
- var _a;
- (_a = this.domNodes) === null || _a === void 0 ? void 0 : _a.scrollbar.scanDomNode();
- }
- }));
- renderedContents.element.classList.add('markdown-docs');
- return renderedContents;
- }
- hasDocs(signature, activeParameter) {
- if (activeParameter && typeof activeParameter.documentation === 'string' && assertIsDefined(activeParameter.documentation).length > 0) {
- return true;
- }
- if (activeParameter && typeof activeParameter.documentation === 'object' && assertIsDefined(activeParameter.documentation).value.length > 0) {
- return true;
- }
- if (signature.documentation && typeof signature.documentation === 'string' && assertIsDefined(signature.documentation).length > 0) {
- return true;
- }
- if (signature.documentation && typeof signature.documentation === 'object' && assertIsDefined(signature.documentation.value).length > 0) {
- return true;
- }
- return false;
- }
- renderParameters(parent, signature, activeParameterIndex) {
- const [start, end] = this.getParameterLabelOffsets(signature, activeParameterIndex);
- const beforeSpan = document.createElement('span');
- beforeSpan.textContent = signature.label.substring(0, start);
- const paramSpan = document.createElement('span');
- paramSpan.textContent = signature.label.substring(start, end);
- paramSpan.className = 'parameter active';
- const afterSpan = document.createElement('span');
- afterSpan.textContent = signature.label.substring(end);
- dom.append(parent, beforeSpan, paramSpan, afterSpan);
- }
- getParameterLabelOffsets(signature, paramIdx) {
- const param = signature.parameters[paramIdx];
- if (!param) {
- return [0, 0];
- }
- else if (Array.isArray(param.label)) {
- return param.label;
- }
- else if (!param.label.length) {
- return [0, 0];
- }
- else {
- const regex = new RegExp(`(\\W|^)${escapeRegExpCharacters(param.label)}(?=\\W|$)`, 'g');
- regex.test(signature.label);
- const idx = regex.lastIndex - param.label.length;
- return idx >= 0
- ? [idx, regex.lastIndex]
- : [0, 0];
- }
- }
- next() {
- this.editor.focus();
- this.model.next();
- }
- previous() {
- this.editor.focus();
- this.model.previous();
- }
- cancel() {
- this.model.cancel();
- }
- getDomNode() {
- if (!this.domNodes) {
- this.createParameterHintDOMNodes();
- }
- return this.domNodes.element;
- }
- getId() {
- return ParameterHintsWidget.ID;
- }
- trigger(context) {
- this.model.trigger(context, 0);
- }
- updateMaxHeight() {
- if (!this.domNodes) {
- return;
- }
- const height = Math.max(this.editor.getLayoutInfo().height / 4, 250);
- const maxHeight = `${height}px`;
- this.domNodes.element.style.maxHeight = maxHeight;
- const wrapper = this.domNodes.element.getElementsByClassName('phwrapper');
- if (wrapper.length) {
- wrapper[0].style.maxHeight = maxHeight;
- }
- }
- };
- ParameterHintsWidget.ID = 'editor.widget.parameterHintsWidget';
- ParameterHintsWidget = __decorate([
- __param(1, IContextKeyService),
- __param(2, IOpenerService),
- __param(3, IModeService)
- ], ParameterHintsWidget);
- export { ParameterHintsWidget };
- export const editorHoverWidgetHighlightForeground = registerColor('editorHoverWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorHoverWidgetHighlightForeground', 'Foreground color of the active item in the parameter hint.'));
- registerThemingParticipant((theme, collector) => {
- const border = theme.getColor(editorHoverBorder);
- if (border) {
- const borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1;
- collector.addRule(`.monaco-editor .parameter-hints-widget { border: ${borderWidth}px solid ${border}; }`);
- collector.addRule(`.monaco-editor .parameter-hints-widget.multiple .body { border-left: 1px solid ${border.transparent(0.5)}; }`);
- collector.addRule(`.monaco-editor .parameter-hints-widget .signature.has-docs { border-bottom: 1px solid ${border.transparent(0.5)}; }`);
- }
- const background = theme.getColor(editorHoverBackground);
- if (background) {
- collector.addRule(`.monaco-editor .parameter-hints-widget { background-color: ${background}; }`);
- }
- const link = theme.getColor(textLinkForeground);
- if (link) {
- collector.addRule(`.monaco-editor .parameter-hints-widget a { color: ${link}; }`);
- }
- const linkHover = theme.getColor(textLinkActiveForeground);
- if (linkHover) {
- collector.addRule(`.monaco-editor .parameter-hints-widget a:hover { color: ${linkHover}; }`);
- }
- const foreground = theme.getColor(editorHoverForeground);
- if (foreground) {
- collector.addRule(`.monaco-editor .parameter-hints-widget { color: ${foreground}; }`);
- }
- const codeBackground = theme.getColor(textCodeBlockBackground);
- if (codeBackground) {
- collector.addRule(`.monaco-editor .parameter-hints-widget code { background-color: ${codeBackground}; }`);
- }
- const parameterHighlightColor = theme.getColor(editorHoverWidgetHighlightForeground);
- if (parameterHighlightColor) {
- collector.addRule(`.monaco-editor .parameter-hints-widget .parameter.active { color: ${parameterHighlightColor}}`);
- }
- });
|