monaco-editor
Version:
A browser based code editor
978 lines (977 loc) • 46 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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 './media/suggest.css';
import * as nls from '../../../nls.js';
import { createMatches } from '../../../base/common/filters.js';
import * as strings from '../../../base/common/strings.js';
import { Emitter, chain } from '../../../base/common/event.js';
import { onUnexpectedError } from '../../../base/common/errors.js';
import { dispose, toDisposable } from '../../../base/common/lifecycle.js';
import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from '../../../base/browser/dom.js';
import { List } from '../../../base/browser/ui/list/listWidget.js';
import { DomScrollableElement } from '../../../base/browser/ui/scrollbar/scrollableElement.js';
import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';
import { IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
import { Context as SuggestContext } from './suggest.js';
import { alert } from '../../../base/browser/ui/aria/aria.js';
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
import { attachListStyler } from '../../../platform/theme/common/styler.js';
import { IThemeService, registerThemingParticipant } from '../../../platform/theme/common/themeService.js';
import { registerColor, editorWidgetBackground, listFocusBackground, activeContrastBorder, listHighlightForeground, editorForeground, editorWidgetBorder, focusBorder, textLinkForeground, textCodeBlockBackground } from '../../../platform/theme/common/colorRegistry.js';
import { IStorageService } from '../../../platform/storage/common/storage.js';
import { MarkdownRenderer } from '../markdown/markdownRenderer.js';
import { IModeService } from '../../common/services/modeService.js';
import { IOpenerService } from '../../../platform/opener/common/opener.js';
import { TimeoutTimer, createCancelablePromise } from '../../../base/common/async.js';
import { CancellationToken } from '../../../base/common/cancellation.js';
import { completionKindToCssClass } from '../../common/modes.js';
import { IconLabel } from '../../../base/browser/ui/iconLabel/iconLabel.js';
import { getIconClasses } from '../../common/services/getIconClasses.js';
import { IModelService } from '../../common/services/modelService.js';
import { URI } from '../../../base/common/uri.js';
import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
import { FileKind } from '../../../platform/files/common/files.js';
var expandSuggestionDocsByDefault = false;
var maxSuggestionsToShow = 12;
/**
* Suggest widget colors
*/
export var editorSuggestWidgetBackground = registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.'));
export var editorSuggestWidgetBorder = registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.'));
export var editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hc: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.'));
export var editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export var editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
var colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
function matchesColor(text) {
return text && text.match(colorRegExp) ? text : null;
}
function canExpandCompletionItem(item) {
if (!item) {
return false;
}
var suggestion = item.suggestion;
if (suggestion.documentation) {
return true;
}
return (suggestion.detail && suggestion.detail !== suggestion.label);
}
var Renderer = /** @class */ (function () {
function Renderer(widget, editor, triggerKeybindingLabel, _modelService, _modeService, _themeService) {
this.widget = widget;
this.editor = editor;
this.triggerKeybindingLabel = triggerKeybindingLabel;
this._modelService = _modelService;
this._modeService = _modeService;
this._themeService = _themeService;
}
Object.defineProperty(Renderer.prototype, "templateId", {
get: function () {
return 'suggestion';
},
enumerable: true,
configurable: true
});
Renderer.prototype.renderTemplate = function (container) {
var _this = this;
var data = Object.create(null);
data.disposables = [];
data.root = container;
addClass(data.root, 'show-file-icons');
data.icon = append(container, $('.icon'));
data.colorspan = append(data.icon, $('span.colorspan'));
var text = append(container, $('.contents'));
var main = append(text, $('.main'));
data.iconLabel = new IconLabel(main, { supportHighlights: true });
data.disposables.push(data.iconLabel);
data.typeLabel = append(main, $('span.type-label'));
data.readMore = append(main, $('span.readMore'));
data.readMore.title = nls.localize('readMore', "Read More...{0}", this.triggerKeybindingLabel);
var configureFont = function () {
var configuration = _this.editor.getConfiguration();
var fontFamily = configuration.fontInfo.fontFamily;
var fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize;
var lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight;
var fontWeight = configuration.fontInfo.fontWeight;
var fontSizePx = fontSize + "px";
var lineHeightPx = lineHeight + "px";
data.root.style.fontSize = fontSizePx;
data.root.style.fontWeight = fontWeight;
main.style.fontFamily = fontFamily;
main.style.lineHeight = lineHeightPx;
data.icon.style.height = lineHeightPx;
data.icon.style.width = lineHeightPx;
data.readMore.style.height = lineHeightPx;
data.readMore.style.width = lineHeightPx;
};
configureFont();
chain(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(function (e) { return e.fontInfo || e.contribInfo; })
.on(configureFont, null, data.disposables);
return data;
};
Renderer.prototype.renderElement = function (element, _index, templateData) {
var _this = this;
var data = templateData;
var suggestion = element.suggestion;
data.icon.className = 'icon ' + completionKindToCssClass(suggestion.kind);
data.colorspan.style.backgroundColor = '';
var labelOptions = {
labelEscapeNewLines: true,
matches: createMatches(element.matches)
};
var color;
if (suggestion.kind === 19 /* Color */ && (color = matchesColor(suggestion.label) || typeof suggestion.documentation === 'string' && matchesColor(suggestion.documentation))) {
// special logic for 'color' completion items
data.icon.className = 'icon customcolor';
data.colorspan.style.backgroundColor = color;
}
else if (suggestion.kind === 20 /* File */ && this._themeService.getIconTheme().hasFileIcons) {
// special logic for 'file' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = [].concat(getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE));
}
else if (suggestion.kind === 23 /* Folder */ && this._themeService.getIconTheme().hasFolderIcons) {
// special logic for 'folder' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = [].concat(getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER), getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER));
}
else {
// normal icon
data.icon.className = 'icon hide';
labelOptions.extraClasses = [
"suggest-icon " + completionKindToCssClass(suggestion.kind)
];
}
data.iconLabel.setValue(suggestion.label, undefined, labelOptions);
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');
if (canExpandCompletionItem(element)) {
show(data.readMore);
data.readMore.onmousedown = function (e) {
e.stopPropagation();
e.preventDefault();
};
data.readMore.onclick = function (e) {
e.stopPropagation();
e.preventDefault();
_this.widget.toggleDetails();
};
}
else {
hide(data.readMore);
data.readMore.onmousedown = null;
data.readMore.onclick = null;
}
};
Renderer.prototype.disposeElement = function () {
// noop
};
Renderer.prototype.disposeTemplate = function (templateData) {
templateData.disposables = dispose(templateData.disposables);
};
Renderer = __decorate([
__param(3, IModelService),
__param(4, IModeService),
__param(5, IThemeService)
], Renderer);
return Renderer;
}());
var SuggestionDetails = /** @class */ (function () {
function SuggestionDetails(container, widget, editor, markdownRenderer, triggerKeybindingLabel) {
var _this = this;
this.widget = widget;
this.editor = editor;
this.markdownRenderer = markdownRenderer;
this.triggerKeybindingLabel = triggerKeybindingLabel;
this.borderWidth = 1;
this.disposables = [];
this.el = append(container, $('.details'));
this.disposables.push(toDisposable(function () { return container.removeChild(_this.el); }));
this.body = $('.body');
this.scrollbar = new DomScrollableElement(this.body, {});
append(this.el, this.scrollbar.getDomNode());
this.disposables.push(this.scrollbar);
this.header = append(this.body, $('.header'));
this.close = append(this.header, $('span.close'));
this.close.title = nls.localize('readLess', "Read less...{0}", this.triggerKeybindingLabel);
this.type = append(this.header, $('p.type'));
this.docs = append(this.body, $('p.docs'));
this.ariaLabel = null;
this.configureFont();
chain(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(function (e) { return e.fontInfo; })
.on(this.configureFont, this, this.disposables);
markdownRenderer.onDidRenderCodeBlock(function () { return _this.scrollbar.scanDomNode(); }, this, this.disposables);
}
Object.defineProperty(SuggestionDetails.prototype, "element", {
get: function () {
return this.el;
},
enumerable: true,
configurable: true
});
SuggestionDetails.prototype.render = function (item) {
var _this = this;
this.renderDisposeable = dispose(this.renderDisposeable);
if (!item || !canExpandCompletionItem(item)) {
this.type.textContent = '';
this.docs.textContent = '';
addClass(this.el, 'no-docs');
this.ariaLabel = null;
return;
}
removeClass(this.el, 'no-docs');
if (typeof item.suggestion.documentation === 'string') {
removeClass(this.docs, 'markdown-docs');
this.docs.textContent = item.suggestion.documentation;
}
else {
addClass(this.docs, 'markdown-docs');
this.docs.innerHTML = '';
var renderedContents = this.markdownRenderer.render(item.suggestion.documentation);
this.renderDisposeable = renderedContents;
this.docs.appendChild(renderedContents.element);
}
if (item.suggestion.detail) {
this.type.innerText = item.suggestion.detail;
show(this.type);
}
else {
this.type.innerText = '';
hide(this.type);
}
this.el.style.height = this.header.offsetHeight + this.docs.offsetHeight + (this.borderWidth * 2) + 'px';
this.close.onmousedown = function (e) {
e.preventDefault();
e.stopPropagation();
};
this.close.onclick = function (e) {
e.preventDefault();
e.stopPropagation();
_this.widget.toggleDetails();
};
this.body.scrollTop = 0;
this.scrollbar.scanDomNode();
this.ariaLabel = strings.format('{0}{1}', item.suggestion.detail || '', item.suggestion.documentation ? (typeof item.suggestion.documentation === 'string' ? item.suggestion.documentation : item.suggestion.documentation.value) : '');
};
SuggestionDetails.prototype.getAriaLabel = function () {
return this.ariaLabel;
};
SuggestionDetails.prototype.scrollDown = function (much) {
if (much === void 0) { much = 8; }
this.body.scrollTop += much;
};
SuggestionDetails.prototype.scrollUp = function (much) {
if (much === void 0) { much = 8; }
this.body.scrollTop -= much;
};
SuggestionDetails.prototype.scrollTop = function () {
this.body.scrollTop = 0;
};
SuggestionDetails.prototype.scrollBottom = function () {
this.body.scrollTop = this.body.scrollHeight;
};
SuggestionDetails.prototype.pageDown = function () {
this.scrollDown(80);
};
SuggestionDetails.prototype.pageUp = function () {
this.scrollUp(80);
};
SuggestionDetails.prototype.setBorderWidth = function (width) {
this.borderWidth = width;
};
SuggestionDetails.prototype.configureFont = function () {
var configuration = this.editor.getConfiguration();
var fontFamily = configuration.fontInfo.fontFamily;
var fontSize = configuration.contribInfo.suggestFontSize || configuration.fontInfo.fontSize;
var lineHeight = configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight;
var fontWeight = configuration.fontInfo.fontWeight;
var fontSizePx = fontSize + "px";
var lineHeightPx = lineHeight + "px";
this.el.style.fontSize = fontSizePx;
this.el.style.fontWeight = fontWeight;
this.type.style.fontFamily = fontFamily;
this.close.style.height = lineHeightPx;
this.close.style.width = lineHeightPx;
};
SuggestionDetails.prototype.dispose = function () {
this.disposables = dispose(this.disposables);
this.renderDisposeable = dispose(this.renderDisposeable);
};
return SuggestionDetails;
}());
var SuggestWidget = /** @class */ (function () {
function SuggestWidget(editor, telemetryService, contextKeyService, themeService, storageService, keybindingService, modeService, openerService, instantiationService) {
var _this = this;
this.editor = editor;
this.telemetryService = telemetryService;
// Editor.IContentWidget.allowEditorOverflow
this.allowEditorOverflow = true;
this.ignoreFocusEvents = false;
this.editorBlurTimeout = new TimeoutTimer();
this.showTimeout = new TimeoutTimer();
this.onDidSelectEmitter = new Emitter();
this.onDidFocusEmitter = new Emitter();
this.onDidHideEmitter = new Emitter();
this.onDidShowEmitter = new Emitter();
this.onDidSelect = this.onDidSelectEmitter.event;
this.onDidFocus = this.onDidFocusEmitter.event;
this.onDidHide = this.onDidHideEmitter.event;
this.onDidShow = this.onDidShowEmitter.event;
this.maxWidgetWidth = 660;
this.listWidth = 330;
this.storageServiceAvailable = true;
this.expandSuggestionDocs = false;
this.firstFocusInCurrentList = false;
var kb = keybindingService.lookupKeybinding('editor.action.triggerSuggest');
var triggerKeybindingLabel = !kb ? '' : " (" + kb.getLabel() + ")";
var markdownRenderer = new MarkdownRenderer(editor, modeService, openerService);
this.isAuto = false;
this.focusedItem = null;
this.storageService = storageService;
// :facepalm:
this.storageService.store('___suggest___', true, 0 /* GLOBAL */);
if (!this.storageService.get('___suggest___', 0 /* GLOBAL */)) {
this.storageServiceAvailable = false;
}
this.storageService.remove('___suggest___', 0 /* GLOBAL */);
this.element = $('.editor-widget.suggest-widget');
if (!this.editor.getConfiguration().contribInfo.iconsInSuggestions) {
addClass(this.element, 'no-icons');
}
this.messageElement = append(this.element, $('.message'));
this.listElement = append(this.element, $('.tree'));
this.details = new SuggestionDetails(this.element, this, this.editor, markdownRenderer, triggerKeybindingLabel);
var renderer = instantiationService.createInstance(Renderer, this, this.editor, triggerKeybindingLabel);
this.list = new List(this.listElement, this, [renderer], {
useShadows: false,
selectOnMouseDown: true,
focusOnMouseDown: false,
openController: { shouldOpen: function () { return false; } }
});
this.toDispose = [
attachListStyler(this.list, themeService, {
listInactiveFocusBackground: editorSuggestWidgetSelectedBackground,
listInactiveFocusOutline: activeContrastBorder
}),
themeService.onThemeChange(function (t) { return _this.onThemeChange(t); }),
editor.onDidLayoutChange(function () { return _this.onEditorLayoutChange(); }),
this.list.onSelectionChange(function (e) { return _this.onListSelection(e); }),
this.list.onFocusChange(function (e) { return _this.onListFocus(e); }),
this.editor.onDidChangeCursorSelection(function () { return _this.onCursorSelectionChanged(); })
];
this.suggestWidgetVisible = SuggestContext.Visible.bindTo(contextKeyService);
this.suggestWidgetMultipleSuggestions = SuggestContext.MultipleSuggestions.bindTo(contextKeyService);
this.editor.addContentWidget(this);
this.setState(0 /* Hidden */);
this.onThemeChange(themeService.getTheme());
}
SuggestWidget.prototype.onCursorSelectionChanged = function () {
if (this.state === 0 /* Hidden */) {
return;
}
this.editor.layoutContentWidget(this);
};
SuggestWidget.prototype.onEditorLayoutChange = function () {
if ((this.state === 3 /* Open */ || this.state === 5 /* Details */) && this.expandDocsSettingFromStorage()) {
this.expandSideOrBelow();
}
};
SuggestWidget.prototype.onListSelection = function (e) {
var _this = this;
if (!e.elements.length) {
return;
}
var item = e.elements[0];
var index = e.indexes[0];
item.resolve(CancellationToken.None).then(function () {
_this.onDidSelectEmitter.fire({ item: item, index: index, model: _this.completionModel });
alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.suggestion.label));
_this.editor.focus();
});
};
SuggestWidget.prototype._getSuggestionAriaAlertLabel = function (item) {
var isSnippet = item.suggestion.kind === 25 /* Snippet */;
if (!canExpandCompletionItem(item)) {
return isSnippet ? nls.localize('ariaCurrentSnippetSuggestion', "{0}, snippet suggestion", item.suggestion.label)
: nls.localize('ariaCurrentSuggestion', "{0}, suggestion", item.suggestion.label);
}
else if (this.expandDocsSettingFromStorage()) {
return isSnippet ? nls.localize('ariaCurrentSnippeSuggestionReadDetails', "{0}, snippet suggestion. Reading details. {1}", item.suggestion.label, this.details.getAriaLabel())
: nls.localize('ariaCurrenttSuggestionReadDetails', "{0}, suggestion. Reading details. {1}", item.suggestion.label, this.details.getAriaLabel());
}
else {
return isSnippet ? nls.localize('ariaCurrentSnippetSuggestionWithDetails', "{0}, snippet suggestion, has details", item.suggestion.label)
: nls.localize('ariaCurrentSuggestionWithDetails', "{0}, suggestion, has details", item.suggestion.label);
}
};
SuggestWidget.prototype._ariaAlert = function (newAriaAlertLabel) {
if (this._lastAriaAlertLabel === newAriaAlertLabel) {
return;
}
this._lastAriaAlertLabel = newAriaAlertLabel;
if (this._lastAriaAlertLabel) {
alert(this._lastAriaAlertLabel);
}
};
SuggestWidget.prototype.onThemeChange = function (theme) {
var backgroundColor = theme.getColor(editorSuggestWidgetBackground);
if (backgroundColor) {
this.listElement.style.backgroundColor = backgroundColor.toString();
this.details.element.style.backgroundColor = backgroundColor.toString();
this.messageElement.style.backgroundColor = backgroundColor.toString();
}
var borderColor = theme.getColor(editorSuggestWidgetBorder);
if (borderColor) {
this.listElement.style.borderColor = borderColor.toString();
this.details.element.style.borderColor = borderColor.toString();
this.messageElement.style.borderColor = borderColor.toString();
this.detailsBorderColor = borderColor.toString();
}
var focusBorderColor = theme.getColor(focusBorder);
if (focusBorderColor) {
this.detailsFocusBorderColor = focusBorderColor.toString();
}
this.details.setBorderWidth(theme.type === 'hc' ? 2 : 1);
};
SuggestWidget.prototype.onListFocus = function (e) {
var _this = this;
if (this.ignoreFocusEvents) {
return;
}
if (!e.elements.length) {
if (this.currentSuggestionDetails) {
this.currentSuggestionDetails.cancel();
this.currentSuggestionDetails = null;
this.focusedItem = null;
}
this._ariaAlert(null);
return;
}
var item = e.elements[0];
var index = e.indexes[0];
this.firstFocusInCurrentList = !this.focusedItem;
if (item !== this.focusedItem) {
if (this.currentSuggestionDetails) {
this.currentSuggestionDetails.cancel();
this.currentSuggestionDetails = null;
}
this.focusedItem = item;
this.list.reveal(index);
this.currentSuggestionDetails = createCancelablePromise(function (token) { return item.resolve(token); });
this.currentSuggestionDetails.then(function () {
if (_this.list.length < index) {
return;
}
// item can have extra information, so re-render
_this.ignoreFocusEvents = true;
_this.list.splice(index, 1, [item]);
_this.list.setFocus([index]);
_this.ignoreFocusEvents = false;
if (_this.expandDocsSettingFromStorage()) {
_this.showDetails();
}
else {
removeClass(_this.element, 'docs-side');
}
_this._ariaAlert(_this._getSuggestionAriaAlertLabel(item));
}).catch(onUnexpectedError).then(function () {
if (_this.focusedItem === item) {
_this.currentSuggestionDetails = null;
}
});
}
// emit an event
this.onDidFocusEmitter.fire({ item: item, index: index, model: this.completionModel });
};
SuggestWidget.prototype.setState = function (state) {
if (!this.element) {
return;
}
var stateChanged = this.state !== state;
this.state = state;
toggleClass(this.element, 'frozen', state === 4 /* Frozen */);
switch (state) {
case 0 /* Hidden */:
hide(this.messageElement, this.details.element, this.listElement);
this.hide();
this.listHeight = 0;
if (stateChanged) {
this.list.splice(0, this.list.length);
}
this.focusedItem = null;
break;
case 1 /* Loading */:
this.messageElement.textContent = SuggestWidget.LOADING_MESSAGE;
hide(this.listElement, this.details.element);
show(this.messageElement);
removeClass(this.element, 'docs-side');
this.show();
this.focusedItem = null;
break;
case 2 /* Empty */:
this.messageElement.textContent = SuggestWidget.NO_SUGGESTIONS_MESSAGE;
hide(this.listElement, this.details.element);
show(this.messageElement);
removeClass(this.element, 'docs-side');
this.show();
this.focusedItem = null;
break;
case 3 /* Open */:
hide(this.messageElement);
show(this.listElement);
this.show();
break;
case 4 /* Frozen */:
hide(this.messageElement);
show(this.listElement);
this.show();
break;
case 5 /* Details */:
hide(this.messageElement);
show(this.details.element, this.listElement);
this.show();
this._ariaAlert(this.details.getAriaLabel());
break;
}
};
SuggestWidget.prototype.showTriggered = function (auto, delay) {
var _this = this;
if (this.state !== 0 /* Hidden */) {
return;
}
this.isAuto = !!auto;
if (!this.isAuto) {
this.loadingTimeout = setTimeout(function () {
_this.loadingTimeout = null;
_this.setState(1 /* Loading */);
}, delay);
}
};
SuggestWidget.prototype.showSuggestions = function (completionModel, selectionIndex, isFrozen, isAuto) {
if (this.loadingTimeout) {
clearTimeout(this.loadingTimeout);
this.loadingTimeout = null;
}
if (this.currentSuggestionDetails) {
this.currentSuggestionDetails.cancel();
this.currentSuggestionDetails = null;
}
if (this.completionModel !== completionModel) {
this.completionModel = completionModel;
}
if (isFrozen && this.state !== 2 /* Empty */ && this.state !== 0 /* Hidden */) {
this.setState(4 /* Frozen */);
return;
}
var visibleCount = this.completionModel.items.length;
var isEmpty = visibleCount === 0;
this.suggestWidgetMultipleSuggestions.set(visibleCount > 1);
if (isEmpty) {
if (isAuto) {
this.setState(0 /* Hidden */);
}
else {
this.setState(2 /* Empty */);
}
this.completionModel = null;
}
else {
if (this.state !== 3 /* Open */) {
var stats = this.completionModel.stats;
stats['wasAutomaticallyTriggered'] = !!isAuto;
/* __GDPR__
"suggestWidget" : {
"wasAutomaticallyTriggered" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true },
"${include}": [
"${ICompletionStats}",
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget', __assign({}, stats, this.editor.getTelemetryData()));
}
this.focusedItem = null;
this.list.splice(0, this.list.length, this.completionModel.items);
if (isFrozen) {
this.setState(4 /* Frozen */);
}
else {
this.setState(3 /* Open */);
}
this.list.reveal(selectionIndex, selectionIndex);
this.list.setFocus([selectionIndex]);
// Reset focus border
if (this.detailsBorderColor) {
this.details.element.style.borderColor = this.detailsBorderColor;
}
}
};
SuggestWidget.prototype.selectNextPage = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 5 /* Details */:
this.details.pageDown();
return true;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusNextPage();
return true;
}
};
SuggestWidget.prototype.selectNext = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusNext(1, true);
return true;
}
};
SuggestWidget.prototype.selectLast = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 5 /* Details */:
this.details.scrollBottom();
return true;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusLast();
return true;
}
};
SuggestWidget.prototype.selectPreviousPage = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 5 /* Details */:
this.details.pageUp();
return true;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusPreviousPage();
return true;
}
};
SuggestWidget.prototype.selectPrevious = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusPrevious(1, true);
return false;
}
};
SuggestWidget.prototype.selectFirst = function () {
switch (this.state) {
case 0 /* Hidden */:
return false;
case 5 /* Details */:
this.details.scrollTop();
return true;
case 1 /* Loading */:
return !this.isAuto;
default:
this.list.focusFirst();
return true;
}
};
SuggestWidget.prototype.getFocusedItem = function () {
if (this.state !== 0 /* Hidden */
&& this.state !== 2 /* Empty */
&& this.state !== 1 /* Loading */) {
return {
item: this.list.getFocusedElements()[0],
index: this.list.getFocus()[0],
model: this.completionModel
};
}
return undefined;
};
SuggestWidget.prototype.toggleDetailsFocus = function () {
if (this.state === 5 /* Details */) {
this.setState(3 /* Open */);
if (this.detailsBorderColor) {
this.details.element.style.borderColor = this.detailsBorderColor;
}
}
else if (this.state === 3 /* Open */ && this.expandDocsSettingFromStorage()) {
this.setState(5 /* Details */);
if (this.detailsFocusBorderColor) {
this.details.element.style.borderColor = this.detailsFocusBorderColor;
}
}
/* __GDPR__
"suggestWidget:toggleDetailsFocus" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:toggleDetailsFocus', this.editor.getTelemetryData());
};
SuggestWidget.prototype.toggleDetails = function () {
if (!canExpandCompletionItem(this.list.getFocusedElements()[0])) {
return;
}
if (this.expandDocsSettingFromStorage()) {
this.updateExpandDocsSetting(false);
hide(this.details.element);
removeClass(this.element, 'docs-side');
removeClass(this.element, 'docs-below');
this.editor.layoutContentWidget(this);
/* __GDPR__
"suggestWidget:collapseDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:collapseDetails', this.editor.getTelemetryData());
}
else {
if (this.state !== 3 /* Open */ && this.state !== 5 /* Details */ && this.state !== 4 /* Frozen */) {
return;
}
this.updateExpandDocsSetting(true);
this.showDetails();
this._ariaAlert(this.details.getAriaLabel());
/* __GDPR__
"suggestWidget:expandDetails" : {
"${include}": [
"${EditorTelemetryData}"
]
}
*/
this.telemetryService.publicLog('suggestWidget:expandDetails', this.editor.getTelemetryData());
}
};
SuggestWidget.prototype.showDetails = function () {
this.expandSideOrBelow();
show(this.details.element);
this.details.render(this.list.getFocusedElements()[0]);
this.details.element.style.maxHeight = this.maxWidgetHeight + 'px';
// Reset margin-top that was set as Fix for #26416
this.listElement.style.marginTop = '0px';
// with docs showing up widget width/height may change, so reposition the widget
this.editor.layoutContentWidget(this);
this.adjustDocsPosition();
this.editor.focus();
};
SuggestWidget.prototype.show = function () {
var _this = this;
var newHeight = this.updateListHeight();
if (newHeight !== this.listHeight) {
this.editor.layoutContentWidget(this);
this.listHeight = newHeight;
}
this.suggestWidgetVisible.set(true);
this.showTimeout.cancelAndSet(function () {
addClass(_this.element, 'visible');
_this.onDidShowEmitter.fire(_this);
}, 100);
};
SuggestWidget.prototype.hide = function () {
this.suggestWidgetVisible.reset();
this.suggestWidgetMultipleSuggestions.reset();
removeClass(this.element, 'visible');
};
SuggestWidget.prototype.hideWidget = function () {
clearTimeout(this.loadingTimeout);
this.setState(0 /* Hidden */);
this.onDidHideEmitter.fire(this);
};
SuggestWidget.prototype.getPosition = function () {
if (this.state === 0 /* Hidden */) {
return null;
}
return {
position: this.editor.getPosition(),
preference: [2 /* BELOW */, 1 /* ABOVE */]
};
};
SuggestWidget.prototype.getDomNode = function () {
return this.element;
};
SuggestWidget.prototype.getId = function () {
return SuggestWidget.ID;
};
SuggestWidget.prototype.updateListHeight = function () {
var height = 0;
if (this.state === 2 /* Empty */ || this.state === 1 /* Loading */) {
height = this.unfocusedHeight;
}
else {
var suggestionCount = this.list.contentHeight / this.unfocusedHeight;
height = Math.min(suggestionCount, maxSuggestionsToShow) * this.unfocusedHeight;
}
this.element.style.lineHeight = this.unfocusedHeight + "px";
this.listElement.style.height = height + "px";
this.list.layout(height);
return height;
};
SuggestWidget.prototype.adjustDocsPosition = function () {
var lineHeight = this.editor.getConfiguration().fontInfo.lineHeight;
var cursorCoords = this.editor.getScrolledVisiblePosition(this.editor.getPosition());
var editorCoords = getDomNodePagePosition(this.editor.getDomNode());
var cursorX = editorCoords.left + cursorCoords.left;
var cursorY = editorCoords.top + cursorCoords.top + cursorCoords.height;
var widgetCoords = getDomNodePagePosition(this.element);
var widgetX = widgetCoords.left;
var widgetY = widgetCoords.top;
if (widgetX < cursorX - this.listWidth) {
// Widget is too far to the left of cursor, swap list and docs
addClass(this.element, 'list-right');
}
else {
removeClass(this.element, 'list-right');
}
// Compare top of the cursor (cursorY - lineheight) with widgetTop to determine if
// margin-top needs to be applied on list to make it appear right above the cursor
// Cannot compare cursorY directly as it may be a few decimals off due to zoooming
if (hasClass(this.element, 'docs-side')
&& cursorY - lineHeight > widgetY
&& this.details.element.offsetHeight > this.listElement.offsetHeight) {
// Fix for #26416
// Docs is bigger than list and widget is above cursor, apply margin-top so that list appears right above cursor
this.listElement.style.marginTop = this.details.element.offsetHeight - this.listElement.offsetHeight + "px";
}
};
SuggestWidget.prototype.expandSideOrBelow = function () {
if (!canExpandCompletionItem(this.focusedItem) && this.firstFocusInCurrentList) {
removeClass(this.element, 'docs-side');
removeClass(this.element, 'docs-below');
return;
}
var matches = this.element.style.maxWidth.match(/(\d+)px/);
if (!matches || Number(matches[1]) < this.maxWidgetWidth) {
addClass(this.element, 'docs-below');
removeClass(this.element, 'docs-side');
}
else if (canExpandCompletionItem(this.focusedItem)) {
addClass(this.element, 'docs-side');
removeClass(this.element, 'docs-below');
}
};
Object.defineProperty(SuggestWidget.prototype, "maxWidgetHeight", {
// Heights
get: function () {
return this.unfocusedHeight * maxSuggestionsToShow;
},
enumerable: true,
configurable: true
});
Object.defineProperty(SuggestWidget.prototype, "unfocusedHeight", {
get: function () {
var configuration = this.editor.getConfiguration();
return configuration.contribInfo.suggestLineHeight || configuration.fontInfo.lineHeight;
},
enumerable: true,
configurable: true
});
// IDelegate
SuggestWidget.prototype.getHeight = function (element) {
return this.unfocusedHeight;
};
SuggestWidget.prototype.getTemplateId = function (element) {
return 'suggestion';
};
SuggestWidget.prototype.expandDocsSettingFromStorage = function () {
if (this.storageServiceAvailable) {
return this.storageService.getBoolean('expandSuggestionDocs', 0 /* GLOBAL */, expandSuggestionDocsByDefault);
}
else {
return this.expandSuggestionDocs;
}
};
SuggestWidget.prototype.updateExpandDocsSetting = function (value) {
if (this.storageServiceAvailable) {
this.storageService.store('expandSuggestionDocs', value, 0 /* GLOBAL */);
}
else {
this.expandSuggestionDocs = value;
}
};
SuggestWidget.prototype.dispose = function () {
this.state = null;
this.currentSuggestionDetails = null;
this.focusedItem = null;
this.element = null;
this.messageElement = null;
this.listElement = null;
this.details.dispose();
this.details = null;
this.list.dispose();
this.list = null;
this.toDispose = dispose(this.toDispose);
if (this.loadingTimeout) {
clearTimeout(this.loadingTimeout);
this.loadingTimeout = null;
}
this.editorBlurTimeout.dispose();
this.showTimeout.dispose();
};
SuggestWidget.ID = 'editor.widget.suggestWidget';
SuggestWidget.LOADING_MESSAGE = nls.localize('suggestWidget.loading', "Loading...");
SuggestWidget.NO_SUGGESTIONS_MESSAGE = nls.localize('suggestWidget.noSuggestions', "No suggestions.");
SuggestWidget = __decorate([
__param(1, ITelemetryService),
__param(2, IContextKeyService),
__param(3, IThemeService),
__param(4, IStorageService),
__param(5, IKeybindingService),
__param(6, IModeService),
__param(7, IOpenerService),
__param(8, IInstantiationService)
], SuggestWidget);
return SuggestWidget;
}());
export { SuggestWidget };
registerThemingParticipant(function (theme, collector) {
var matchHighlight = theme.getColor(editorSuggestWidgetHighlightForeground);
if (matchHighlight) {
collector.addRule(".monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-highlighted-label .highlight { color: " + matchHighlight + "; }");
}
var foreground = theme.getColor(editorSuggestWidgetForeground);
if (foreground) {
collector.addRule(".monaco-editor .suggest-widget { color: " + foreground + "; }");
}
var link = theme.getColor(textLinkForeground);
if (link) {
collector.addRule(".monaco-editor .suggest-widget a { color: " + link + "; }");
}
var codeBackground = theme.getColor(textCodeBlockBackground);
if (codeBackground) {
collector.addRule(".monaco-editor .suggest-widget code { background-color: " + codeBackground + "; }");
}
});