123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- /*---------------------------------------------------------------------------------------------
- * 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 { alert } from '../../../base/browser/ui/aria/aria.js';
- import { isNonEmptyArray } from '../../../base/common/arrays.js';
- import { IdleValue } from '../../../base/common/async.js';
- import { CancellationTokenSource } from '../../../base/common/cancellation.js';
- import { onUnexpectedError } from '../../../base/common/errors.js';
- import { Event } from '../../../base/common/event.js';
- import { SimpleKeybinding } from '../../../base/common/keybindings.js';
- import { DisposableStore, dispose, MutableDisposable, toDisposable } from '../../../base/common/lifecycle.js';
- import * as platform from '../../../base/common/platform.js';
- import { StopWatch } from '../../../base/common/stopwatch.js';
- import { assertType, isObject } from '../../../base/common/types.js';
- import { StableEditorScrollState } from '../../browser/core/editorState.js';
- import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution } from '../../browser/editorExtensions.js';
- import { EditOperation } from '../../common/core/editOperation.js';
- import { Position } from '../../common/core/position.js';
- import { Range } from '../../common/core/range.js';
- import { EditorContextKeys } from '../../common/editorContextKeys.js';
- import { SnippetController2 } from '../snippet/snippetController2.js';
- import { SnippetParser } from '../snippet/snippetParser.js';
- import { ISuggestMemoryService } from './suggestMemory.js';
- import { WordContextKey } from './wordContextKey.js';
- import * as nls from '../../../nls.js';
- import { MenuRegistry } from '../../../platform/actions/common/actions.js';
- import { CommandsRegistry, ICommandService } from '../../../platform/commands/common/commands.js';
- import { ContextKeyExpr, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js';
- import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js';
- import { KeybindingsRegistry } from '../../../platform/keybinding/common/keybindingsRegistry.js';
- import { ILogService } from '../../../platform/log/common/log.js';
- import { Context as SuggestContext, suggestWidgetStatusbarMenu } from './suggest.js';
- import { SuggestAlternatives } from './suggestAlternatives.js';
- import { CommitCharacterController } from './suggestCommitCharacters.js';
- import { SuggestModel } from './suggestModel.js';
- import { OvertypingCapturer } from './suggestOvertypingCapturer.js';
- import { SuggestWidget } from './suggestWidget.js';
- import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
- import { basename, extname } from '../../../base/common/resources.js';
- import { hash } from '../../../base/common/hash.js';
- // sticky suggest widget which doesn't disappear on focus out and such
- let _sticky = false;
- // _sticky = Boolean("true"); // done "weirdly" so that a lint warning prevents you from pushing this
- class LineSuffix {
- constructor(_model, _position) {
- this._model = _model;
- this._position = _position;
- // spy on what's happening right of the cursor. two cases:
- // 1. end of line -> check that it's still end of line
- // 2. mid of line -> add a marker and compute the delta
- const maxColumn = _model.getLineMaxColumn(_position.lineNumber);
- if (maxColumn !== _position.column) {
- const offset = _model.getOffsetAt(_position);
- const end = _model.getPositionAt(offset + 1);
- this._marker = _model.deltaDecorations([], [{
- range: Range.fromPositions(_position, end),
- options: { description: 'suggest-line-suffix', stickiness: 1 /* NeverGrowsWhenTypingAtEdges */ }
- }]);
- }
- }
- dispose() {
- if (this._marker && !this._model.isDisposed()) {
- this._model.deltaDecorations(this._marker, []);
- }
- }
- delta(position) {
- if (this._model.isDisposed() || this._position.lineNumber !== position.lineNumber) {
- // bail out early if things seems fishy
- return 0;
- }
- // read the marker (in case suggest was triggered at line end) or compare
- // the cursor to the line end.
- if (this._marker) {
- const range = this._model.getDecorationRange(this._marker[0]);
- const end = this._model.getOffsetAt(range.getStartPosition());
- return end - this._model.getOffsetAt(position);
- }
- else {
- return this._model.getLineMaxColumn(position.lineNumber) - position.column;
- }
- }
- }
- let SuggestController = class SuggestController {
- constructor(editor, _memoryService, _commandService, _contextKeyService, _instantiationService, _logService, _telemetryService) {
- this._memoryService = _memoryService;
- this._commandService = _commandService;
- this._contextKeyService = _contextKeyService;
- this._instantiationService = _instantiationService;
- this._logService = _logService;
- this._telemetryService = _telemetryService;
- this._lineSuffix = new MutableDisposable();
- this._toDispose = new DisposableStore();
- this._selectors = new PriorityRegistry(s => s.priority);
- this._telemetryGate = 0;
- this.editor = editor;
- this.model = _instantiationService.createInstance(SuggestModel, this.editor);
- // context key: update insert/replace mode
- const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService);
- ctxInsertMode.set(editor.getOption(105 /* suggest */).insertMode);
- this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(105 /* suggest */).insertMode));
- this.widget = this._toDispose.add(new IdleValue(() => {
- const widget = this._instantiationService.createInstance(SuggestWidget, this.editor);
- this._toDispose.add(widget);
- this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, 0), this));
- // Wire up logic to accept a suggestion on certain characters
- const commitCharacterController = new CommitCharacterController(this.editor, widget, item => this._insertSuggestion(item, 2 /* NoAfterUndoStop */));
- this._toDispose.add(commitCharacterController);
- this._toDispose.add(this.model.onDidSuggest(e => {
- if (e.completionModel.items.length === 0) {
- commitCharacterController.reset();
- }
- }));
- // Wire up makes text edit context key
- const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
- const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
- const ctxCanResolve = SuggestContext.CanResolve.bindTo(this._contextKeyService);
- this._toDispose.add(toDisposable(() => {
- ctxMakesTextEdit.reset();
- ctxHasInsertAndReplace.reset();
- ctxCanResolve.reset();
- }));
- this._toDispose.add(widget.onDidFocus(({ item }) => {
- // (ctx: makesTextEdit)
- const position = this.editor.getPosition();
- const startColumn = item.editStart.column;
- const endColumn = position.column;
- let value = true;
- if (this.editor.getOption(1 /* acceptSuggestionOnEnter */) === 'smart'
- && this.model.state === 2 /* Auto */
- && !item.completion.additionalTextEdits
- && !(item.completion.insertTextRules & 4 /* InsertAsSnippet */)
- && endColumn - startColumn === item.completion.insertText.length) {
- const oldText = this.editor.getModel().getValueInRange({
- startLineNumber: position.lineNumber,
- startColumn,
- endLineNumber: position.lineNumber,
- endColumn
- });
- value = oldText !== item.completion.insertText;
- }
- ctxMakesTextEdit.set(value);
- // (ctx: hasInsertAndReplaceRange)
- ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
- // (ctx: canResolve)
- ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label);
- }));
- this._toDispose.add(widget.onDetailsKeyDown(e => {
- // cmd + c on macOS, ctrl + c on Win / Linux
- if (e.toKeybinding().equals(new SimpleKeybinding(true, false, false, false, 33 /* KeyC */)) ||
- (platform.isMacintosh && e.toKeybinding().equals(new SimpleKeybinding(false, false, false, true, 33 /* KeyC */)))) {
- e.stopPropagation();
- return;
- }
- if (!e.toKeybinding().isModifierKey()) {
- this.editor.focus();
- }
- }));
- return widget;
- }));
- // Wire up text overtyping capture
- this._overtypingCapturer = this._toDispose.add(new IdleValue(() => {
- return this._toDispose.add(new OvertypingCapturer(this.editor, this.model));
- }));
- this._alternatives = this._toDispose.add(new IdleValue(() => {
- return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService));
- }));
- this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor));
- this._toDispose.add(this.model.onDidTrigger(e => {
- this.widget.value.showTriggered(e.auto, e.shy ? 250 : 50);
- this._lineSuffix.value = new LineSuffix(this.editor.getModel(), e.position);
- }));
- this._toDispose.add(this.model.onDidSuggest(e => {
- if (!e.shy) {
- let index = -1;
- for (const selector of this._selectors.itemsOrderedByPriorityDesc) {
- index = selector.select(this.editor.getModel(), this.editor.getPosition(), e.completionModel.items);
- if (index !== -1) {
- break;
- }
- }
- if (index === -1) {
- index = this._memoryService.select(this.editor.getModel(), this.editor.getPosition(), e.completionModel.items);
- }
- this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.auto);
- }
- }));
- this._toDispose.add(this.model.onDidCancel(e => {
- if (!e.retrigger) {
- this.widget.value.hideWidget();
- }
- }));
- this._toDispose.add(this.editor.onDidBlurEditorWidget(() => {
- if (!_sticky) {
- this.model.cancel();
- this.model.clear();
- }
- }));
- // Manage the acceptSuggestionsOnEnter context key
- let acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
- let updateFromConfig = () => {
- const acceptSuggestionOnEnter = this.editor.getOption(1 /* acceptSuggestionOnEnter */);
- acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart');
- };
- this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig()));
- updateFromConfig();
- }
- static get(editor) {
- return editor.getContribution(SuggestController.ID);
- }
- dispose() {
- this._alternatives.dispose();
- this._toDispose.dispose();
- this.widget.dispose();
- this.model.dispose();
- this._lineSuffix.dispose();
- }
- _insertSuggestion(event, flags) {
- if (!event || !event.item) {
- this._alternatives.value.reset();
- this.model.cancel();
- this.model.clear();
- return;
- }
- if (!this.editor.hasModel()) {
- return;
- }
- const model = this.editor.getModel();
- const modelVersionNow = model.getAlternativeVersionId();
- const { item } = event;
- //
- const tasks = [];
- const cts = new CancellationTokenSource();
- // pushing undo stops *before* additional text edits and
- // *after* the main edit
- if (!(flags & 1 /* NoBeforeUndoStop */)) {
- this.editor.pushUndoStop();
- }
- // compute overwrite[Before|After] deltas BEFORE applying extra edits
- const info = this.getOverwriteInfo(item, Boolean(flags & 8 /* AlternativeOverwriteConfig */));
- // keep item in memory
- this._memoryService.memorize(model, this.editor.getPosition(), item);
- if (Array.isArray(item.completion.additionalTextEdits)) {
- // sync additional edits
- const scrollState = StableEditorScrollState.capture(this.editor);
- this.editor.executeEdits('suggestController.additionalTextEdits.sync', item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
- scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
- }
- else if (!item.isResolved) {
- // async additional edits
- const sw = new StopWatch(true);
- let position;
- const docListener = model.onDidChangeContent(e => {
- if (e.isFlush) {
- cts.cancel();
- docListener.dispose();
- return;
- }
- for (let change of e.changes) {
- const thisPosition = Range.getEndPosition(change.range);
- if (!position || Position.isBefore(thisPosition, position)) {
- position = thisPosition;
- }
- }
- });
- let oldFlags = flags;
- flags |= 2 /* NoAfterUndoStop */;
- let didType = false;
- let typeListener = this.editor.onWillType(() => {
- typeListener.dispose();
- didType = true;
- if (!(oldFlags & 2 /* NoAfterUndoStop */)) {
- this.editor.pushUndoStop();
- }
- });
- tasks.push(item.resolve(cts.token).then(() => {
- if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) {
- return false;
- }
- if (position && item.completion.additionalTextEdits.some(edit => Position.isBefore(position, Range.getStartPosition(edit.range)))) {
- return false;
- }
- if (didType) {
- this.editor.pushUndoStop();
- }
- const scrollState = StableEditorScrollState.capture(this.editor);
- this.editor.executeEdits('suggestController.additionalTextEdits.async', item.completion.additionalTextEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
- scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
- if (didType || !(oldFlags & 2 /* NoAfterUndoStop */)) {
- this.editor.pushUndoStop();
- }
- return true;
- }).then(applied => {
- this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied);
- docListener.dispose();
- typeListener.dispose();
- }));
- }
- let { insertText } = item.completion;
- if (!(item.completion.insertTextRules & 4 /* InsertAsSnippet */)) {
- insertText = SnippetParser.escape(insertText);
- }
- SnippetController2.get(this.editor).insert(insertText, {
- overwriteBefore: info.overwriteBefore,
- overwriteAfter: info.overwriteAfter,
- undoStopBefore: false,
- undoStopAfter: false,
- adjustWhitespace: !(item.completion.insertTextRules & 1 /* KeepWhitespace */),
- clipboardText: event.model.clipboardText,
- overtypingCapturer: this._overtypingCapturer.value
- });
- if (!(flags & 2 /* NoAfterUndoStop */)) {
- this.editor.pushUndoStop();
- }
- if (!item.completion.command) {
- // done
- this.model.cancel();
- }
- else if (item.completion.command.id === TriggerSuggestAction.id) {
- // retigger
- this.model.trigger({ auto: true, shy: false }, true);
- }
- else {
- // exec command, done
- tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(onUnexpectedError));
- this.model.cancel();
- }
- if (flags & 4 /* KeepAlternativeSuggestions */) {
- this._alternatives.value.set(event, next => {
- // cancel resolving of additional edits
- cts.cancel();
- // this is not so pretty. when inserting the 'next'
- // suggestion we undo until we are at the state at
- // which we were before inserting the previous suggestion...
- while (model.canUndo()) {
- if (modelVersionNow !== model.getAlternativeVersionId()) {
- model.undo();
- }
- this._insertSuggestion(next, 1 /* NoBeforeUndoStop */ | 2 /* NoAfterUndoStop */ | (flags & 8 /* AlternativeOverwriteConfig */ ? 8 /* AlternativeOverwriteConfig */ : 0));
- break;
- }
- });
- }
- this._alertCompletionItem(item);
- // clear only now - after all tasks are done
- Promise.all(tasks).finally(() => {
- this._reportSuggestionAcceptedTelemetry(model, event);
- this.model.clear();
- cts.dispose();
- });
- }
- _reportSuggestionAcceptedTelemetry(model, acceptedSuggestion) {
- var _a;
- if (this._telemetryGate++ % 100 !== 0) {
- return;
- }
- // _debugDisplayName looks like `vscode.css-language-features(/-:)`, where the last bit is the trigger chars
- // normalize it to just the extension ID and lowercase
- const providerId = ((_a = acceptedSuggestion.item.provider._debugDisplayName) !== null && _a !== void 0 ? _a : 'unknown').split('(', 1)[0].toLowerCase();
- this._telemetryService.publicLog2('suggest.acceptedSuggestion', {
- providerId,
- basenameHash: hash(basename(model.uri)).toString(16),
- languageId: model.getLanguageId(),
- fileExtension: extname(model.uri),
- });
- }
- getOverwriteInfo(item, toggleMode) {
- assertType(this.editor.hasModel());
- let replace = this.editor.getOption(105 /* suggest */).insertMode === 'replace';
- if (toggleMode) {
- replace = !replace;
- }
- const overwriteBefore = item.position.column - item.editStart.column;
- const overwriteAfter = (replace ? item.editReplaceEnd.column : item.editInsertEnd.column) - item.position.column;
- const columnDelta = this.editor.getPosition().column - item.position.column;
- const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this.editor.getPosition()) : 0;
- return {
- overwriteBefore: overwriteBefore + columnDelta,
- overwriteAfter: overwriteAfter + suffixDelta
- };
- }
- _alertCompletionItem(item) {
- if (isNonEmptyArray(item.completion.additionalTextEdits)) {
- let msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", item.textLabel, item.completion.additionalTextEdits.length);
- alert(msg);
- }
- }
- triggerSuggest(onlyFrom, auto) {
- if (this.editor.hasModel()) {
- this.model.trigger({ auto: auto !== null && auto !== void 0 ? auto : false, shy: false }, false, onlyFrom);
- this.editor.revealPosition(this.editor.getPosition(), 0 /* Smooth */);
- this.editor.focus();
- }
- }
- triggerSuggestAndAcceptBest(arg) {
- if (!this.editor.hasModel()) {
- return;
- }
- const positionNow = this.editor.getPosition();
- const fallback = () => {
- if (positionNow.equals(this.editor.getPosition())) {
- this._commandService.executeCommand(arg.fallback);
- }
- };
- const makesTextEdit = (item) => {
- if (item.completion.insertTextRules & 4 /* InsertAsSnippet */ || item.completion.additionalTextEdits) {
- // snippet, other editor -> makes edit
- return true;
- }
- const position = this.editor.getPosition();
- const startColumn = item.editStart.column;
- const endColumn = position.column;
- if (endColumn - startColumn !== item.completion.insertText.length) {
- // unequal lengths -> makes edit
- return true;
- }
- const textNow = this.editor.getModel().getValueInRange({
- startLineNumber: position.lineNumber,
- startColumn,
- endLineNumber: position.lineNumber,
- endColumn
- });
- // unequal text -> makes edit
- return textNow !== item.completion.insertText;
- };
- Event.once(this.model.onDidTrigger)(_ => {
- // wait for trigger because only then the cancel-event is trustworthy
- let listener = [];
- Event.any(this.model.onDidTrigger, this.model.onDidCancel)(() => {
- // retrigger or cancel -> try to type default text
- dispose(listener);
- fallback();
- }, undefined, listener);
- this.model.onDidSuggest(({ completionModel }) => {
- dispose(listener);
- if (completionModel.items.length === 0) {
- fallback();
- return;
- }
- const index = this._memoryService.select(this.editor.getModel(), this.editor.getPosition(), completionModel.items);
- const item = completionModel.items[index];
- if (!makesTextEdit(item)) {
- fallback();
- return;
- }
- this.editor.pushUndoStop();
- this._insertSuggestion({ index, item, model: completionModel }, 4 /* KeepAlternativeSuggestions */ | 1 /* NoBeforeUndoStop */ | 2 /* NoAfterUndoStop */);
- }, undefined, listener);
- });
- this.model.trigger({ auto: false, shy: true });
- this.editor.revealPosition(positionNow, 0 /* Smooth */);
- this.editor.focus();
- }
- acceptSelectedSuggestion(keepAlternativeSuggestions, alternativeOverwriteConfig) {
- const item = this.widget.value.getFocusedItem();
- let flags = 0;
- if (keepAlternativeSuggestions) {
- flags |= 4 /* KeepAlternativeSuggestions */;
- }
- if (alternativeOverwriteConfig) {
- flags |= 8 /* AlternativeOverwriteConfig */;
- }
- this._insertSuggestion(item, flags);
- }
- acceptNextSuggestion() {
- this._alternatives.value.next();
- }
- acceptPrevSuggestion() {
- this._alternatives.value.prev();
- }
- cancelSuggestWidget() {
- this.model.cancel();
- this.model.clear();
- this.widget.value.hideWidget();
- }
- selectNextSuggestion() {
- this.widget.value.selectNext();
- }
- selectNextPageSuggestion() {
- this.widget.value.selectNextPage();
- }
- selectLastSuggestion() {
- this.widget.value.selectLast();
- }
- selectPrevSuggestion() {
- this.widget.value.selectPrevious();
- }
- selectPrevPageSuggestion() {
- this.widget.value.selectPreviousPage();
- }
- selectFirstSuggestion() {
- this.widget.value.selectFirst();
- }
- toggleSuggestionDetails() {
- this.widget.value.toggleDetails();
- }
- toggleExplainMode() {
- this.widget.value.toggleExplainMode();
- }
- toggleSuggestionFocus() {
- this.widget.value.toggleDetailsFocus();
- }
- resetWidgetSize() {
- this.widget.value.resetPersistedSize();
- }
- forceRenderingAbove() {
- this.widget.value.forceRenderingAbove();
- }
- stopForceRenderingAbove() {
- if (!this.widget.isInitialized) {
- // This method has no effect if the widget is not initialized yet.
- return;
- }
- this.widget.value.stopForceRenderingAbove();
- }
- registerSelector(selector) {
- return this._selectors.register(selector);
- }
- };
- SuggestController.ID = 'editor.contrib.suggestController';
- SuggestController = __decorate([
- __param(1, ISuggestMemoryService),
- __param(2, ICommandService),
- __param(3, IContextKeyService),
- __param(4, IInstantiationService),
- __param(5, ILogService),
- __param(6, ITelemetryService)
- ], SuggestController);
- export { SuggestController };
- class PriorityRegistry {
- constructor(prioritySelector) {
- this.prioritySelector = prioritySelector;
- this._items = new Array();
- }
- register(value) {
- if (this._items.indexOf(value) !== -1) {
- throw new Error('Value is already registered');
- }
- this._items.push(value);
- this._items.sort((s1, s2) => this.prioritySelector(s2) - this.prioritySelector(s1));
- return {
- dispose: () => {
- const idx = this._items.indexOf(value);
- if (idx >= 0) {
- this._items.splice(idx, 1);
- }
- }
- };
- }
- get itemsOrderedByPriorityDesc() {
- return this._items;
- }
- }
- export class TriggerSuggestAction extends EditorAction {
- constructor() {
- super({
- id: TriggerSuggestAction.id,
- label: nls.localize('suggest.trigger.label', "Trigger Suggest"),
- alias: 'Trigger Suggest',
- precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCompletionItemProvider),
- kbOpts: {
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 2048 /* CtrlCmd */ | 10 /* Space */,
- secondary: [2048 /* CtrlCmd */ | 39 /* KeyI */],
- mac: { primary: 256 /* WinCtrl */ | 10 /* Space */, secondary: [512 /* Alt */ | 9 /* Escape */, 2048 /* CtrlCmd */ | 39 /* KeyI */] },
- weight: 100 /* EditorContrib */
- }
- });
- }
- run(_accessor, editor, args) {
- const controller = SuggestController.get(editor);
- if (!controller) {
- return;
- }
- let auto;
- if (args && typeof args === 'object') {
- if (args.auto === true) {
- auto = true;
- }
- }
- controller.triggerSuggest(undefined, auto);
- }
- }
- TriggerSuggestAction.id = 'editor.action.triggerSuggest';
- registerEditorContribution(SuggestController.ID, SuggestController);
- registerEditorAction(TriggerSuggestAction);
- const weight = 100 /* EditorContrib */ + 90;
- const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get);
- registerEditorCommand(new SuggestCommand({
- id: 'acceptSelectedSuggestion',
- precondition: SuggestContext.Visible,
- handler(x) {
- x.acceptSelectedSuggestion(true, false);
- }
- }));
- // normal tab
- KeybindingsRegistry.registerKeybindingRule({
- id: 'acceptSelectedSuggestion',
- when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
- primary: 2 /* Tab */,
- weight
- });
- // accept on enter has special rules
- KeybindingsRegistry.registerKeybindingRule({
- id: 'acceptSelectedSuggestion',
- when: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
- primary: 3 /* Enter */,
- weight,
- });
- MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
- command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.insert', "Insert") },
- group: 'left',
- order: 1,
- when: SuggestContext.HasInsertAndReplaceRange.toNegated()
- });
- MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
- command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.insert', "Insert") },
- group: 'left',
- order: 1,
- when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert'))
- });
- MenuRegistry.appendMenuItem(suggestWidgetStatusbarMenu, {
- command: { id: 'acceptSelectedSuggestion', title: nls.localize('accept.replace', "Replace") },
- group: 'left',
- order: 1,
- when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace'))
- });
- registerEditorCommand(new SuggestCommand({
- id: 'acceptAlternativeSelectedSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 1024 /* Shift */ | 3 /* Enter */,
- secondary: [1024 /* Shift */ | 2 /* Tab */],
- },
- handler(x) {
- x.acceptSelectedSuggestion(false, true);
- },
- menuOpts: [{
- menuId: suggestWidgetStatusbarMenu,
- group: 'left',
- order: 2,
- when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert')),
- title: nls.localize('accept.replace', "Replace")
- }, {
- menuId: suggestWidgetStatusbarMenu,
- group: 'left',
- order: 2,
- when: ContextKeyExpr.and(SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace')),
- title: nls.localize('accept.insert', "Insert")
- }]
- }));
- // continue to support the old command
- CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion');
- registerEditorCommand(new SuggestCommand({
- id: 'hideSuggestWidget',
- precondition: SuggestContext.Visible,
- handler: x => x.cancelSuggestWidget(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 9 /* Escape */,
- secondary: [1024 /* Shift */ | 9 /* Escape */]
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectNextSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectNextSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 18 /* DownArrow */,
- secondary: [2048 /* CtrlCmd */ | 18 /* DownArrow */],
- mac: { primary: 18 /* DownArrow */, secondary: [2048 /* CtrlCmd */ | 18 /* DownArrow */, 256 /* WinCtrl */ | 44 /* KeyN */] }
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectNextPageSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectNextPageSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 12 /* PageDown */,
- secondary: [2048 /* CtrlCmd */ | 12 /* PageDown */]
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectLastSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectLastSuggestion()
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectPrevSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectPrevSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 16 /* UpArrow */,
- secondary: [2048 /* CtrlCmd */ | 16 /* UpArrow */],
- mac: { primary: 16 /* UpArrow */, secondary: [2048 /* CtrlCmd */ | 16 /* UpArrow */, 256 /* WinCtrl */ | 46 /* KeyP */] }
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectPrevPageSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectPrevPageSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 11 /* PageUp */,
- secondary: [2048 /* CtrlCmd */ | 11 /* PageUp */]
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'selectFirstSuggestion',
- precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.MultipleSuggestions),
- handler: c => c.selectFirstSuggestion()
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'toggleSuggestionDetails',
- precondition: SuggestContext.Visible,
- handler: x => x.toggleSuggestionDetails(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 2048 /* CtrlCmd */ | 10 /* Space */,
- secondary: [2048 /* CtrlCmd */ | 39 /* KeyI */],
- mac: { primary: 256 /* WinCtrl */ | 10 /* Space */, secondary: [2048 /* CtrlCmd */ | 39 /* KeyI */] }
- },
- menuOpts: [{
- menuId: suggestWidgetStatusbarMenu,
- group: 'right',
- order: 1,
- when: ContextKeyExpr.and(SuggestContext.DetailsVisible, SuggestContext.CanResolve),
- title: nls.localize('detail.more', "show less")
- }, {
- menuId: suggestWidgetStatusbarMenu,
- group: 'right',
- order: 1,
- when: ContextKeyExpr.and(SuggestContext.DetailsVisible.toNegated(), SuggestContext.CanResolve),
- title: nls.localize('detail.less', "show more")
- }]
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'toggleExplainMode',
- precondition: SuggestContext.Visible,
- handler: x => x.toggleExplainMode(),
- kbOpts: {
- weight: 100 /* EditorContrib */,
- primary: 2048 /* CtrlCmd */ | 85 /* Slash */,
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'toggleSuggestionFocus',
- precondition: SuggestContext.Visible,
- handler: x => x.toggleSuggestionFocus(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 2048 /* CtrlCmd */ | 512 /* Alt */ | 10 /* Space */,
- mac: { primary: 256 /* WinCtrl */ | 512 /* Alt */ | 10 /* Space */ }
- }
- }));
- //#region tab completions
- registerEditorCommand(new SuggestCommand({
- id: 'insertBestCompletion',
- precondition: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), WordContextKey.AtEnd, SuggestContext.Visible.toNegated(), SuggestAlternatives.OtherSuggestions.toNegated(), SnippetController2.InSnippetMode.toNegated()),
- handler: (x, arg) => {
- x.triggerSuggestAndAcceptBest(isObject(arg) ? Object.assign({ fallback: 'tab' }, arg) : { fallback: 'tab' });
- },
- kbOpts: {
- weight,
- primary: 2 /* Tab */
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'insertNextSuggestion',
- precondition: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), SuggestAlternatives.OtherSuggestions, SuggestContext.Visible.toNegated(), SnippetController2.InSnippetMode.toNegated()),
- handler: x => x.acceptNextSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 2 /* Tab */
- }
- }));
- registerEditorCommand(new SuggestCommand({
- id: 'insertPrevSuggestion',
- precondition: ContextKeyExpr.and(EditorContextKeys.textInputFocus, ContextKeyExpr.equals('config.editor.tabCompletion', 'on'), SuggestAlternatives.OtherSuggestions, SuggestContext.Visible.toNegated(), SnippetController2.InSnippetMode.toNegated()),
- handler: x => x.acceptPrevSuggestion(),
- kbOpts: {
- weight: weight,
- kbExpr: EditorContextKeys.textInputFocus,
- primary: 1024 /* Shift */ | 2 /* Tab */
- }
- }));
- registerEditorAction(class extends EditorAction {
- constructor() {
- super({
- id: 'editor.action.resetSuggestSize',
- label: nls.localize('suggest.reset.label', "Reset Suggest Widget Size"),
- alias: 'Reset Suggest Widget Size',
- precondition: undefined
- });
- }
- run(_accessor, editor) {
- SuggestController.get(editor).resetWidgetSize();
- }
- });
|