UNPKG

monaco-editor

Version:
260 lines (259 loc) • 16.1 kB
/*--------------------------------------------------------------------------------------------- * 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); } }; var InlineCompletionsController_1; import { createStyleSheet2 } from '../../../../base/browser/dom.js'; import { alert } from '../../../../base/browser/ui/aria/aria.js'; import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { autorun, autorunHandleChanges, constObservable, derived, disposableObservableValue, observableFromEvent, observableSignal, observableValue, transaction } from '../../../../base/common/observable.js'; import { CoreEditingCommands } from '../../../browser/coreCommands.js'; import { Position } from '../../../common/core/position.js'; import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; import { inlineSuggestCommitId } from './commandIds.js'; import { GhostTextWidget } from './ghostTextWidget.js'; import { InlineCompletionContextKeys } from './inlineCompletionContextKeys.js'; import { InlineCompletionsHintsWidget, InlineSuggestionHintsContentWidget } from './inlineCompletionsHintsWidget.js'; import { InlineCompletionsModel, VersionIdChangeReason } from './inlineCompletionsModel.js'; import { SuggestWidgetAdaptor } from './suggestWidgetInlineCompletionProvider.js'; import { localize } from '../../../../nls.js'; import { AudioCue, IAudioCueService } from '../../../../platform/audioCues/browser/audioCueService.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; let InlineCompletionsController = InlineCompletionsController_1 = class InlineCompletionsController extends Disposable { static get(editor) { return editor.getContribution(InlineCompletionsController_1.ID); } constructor(editor, _instantiationService, _contextKeyService, _configurationService, _commandService, _debounceService, _languageFeaturesService, _audioCueService, _keybindingService) { super(); this.editor = editor; this._instantiationService = _instantiationService; this._contextKeyService = _contextKeyService; this._configurationService = _configurationService; this._commandService = _commandService; this._debounceService = _debounceService; this._languageFeaturesService = _languageFeaturesService; this._audioCueService = _audioCueService; this._keybindingService = _keybindingService; this.model = disposableObservableValue('inlineCompletionModel', undefined); this._textModelVersionId = observableValue(this, -1); this._cursorPosition = observableValue(this, new Position(1, 1)); this._suggestWidgetAdaptor = this._register(new SuggestWidgetAdaptor(this.editor, () => { var _a, _b; return (_b = (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.selectedInlineCompletion.get()) === null || _b === void 0 ? void 0 : _b.toSingleTextEdit(undefined); }, (tx) => this.updateObservables(tx, VersionIdChangeReason.Other), (item) => { transaction(tx => { var _a; /** @description InlineCompletionsController.handleSuggestAccepted */ this.updateObservables(tx, VersionIdChangeReason.Other); (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.handleSuggestAccepted(item); }); })); this._enabled = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(62 /* EditorOption.inlineSuggest */).enabled); this._fontFamily = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(62 /* EditorOption.inlineSuggest */).fontFamily); this._ghostTextWidget = this._register(this._instantiationService.createInstance(GhostTextWidget, this.editor, { ghostText: this.model.map((v, reader) => /** ghostText */ v === null || v === void 0 ? void 0 : v.ghostText.read(reader)), minReservedLineCount: constObservable(0), targetTextModel: this.model.map(v => v === null || v === void 0 ? void 0 : v.textModel), })); this._debounceValue = this._debounceService.for(this._languageFeaturesService.inlineCompletionsProvider, 'InlineCompletionsDebounce', { min: 50, max: 50 }); this._playAudioCueSignal = observableSignal(this); this._isReadonly = observableFromEvent(this.editor.onDidChangeConfiguration, () => this.editor.getOption(90 /* EditorOption.readOnly */)); this._textModel = observableFromEvent(this.editor.onDidChangeModel, () => this.editor.getModel()); this._textModelIfWritable = derived(reader => this._isReadonly.read(reader) ? undefined : this._textModel.read(reader)); this._register(new InlineCompletionContextKeys(this._contextKeyService, this.model)); this._register(autorun(reader => { /** @description InlineCompletionsController.update model */ const textModel = this._textModelIfWritable.read(reader); transaction(tx => { /** @description InlineCompletionsController.onDidChangeModel/readonly */ this.model.set(undefined, tx); this.updateObservables(tx, VersionIdChangeReason.Other); if (textModel) { const model = _instantiationService.createInstance(InlineCompletionsModel, textModel, this._suggestWidgetAdaptor.selectedItem, this._cursorPosition, this._textModelVersionId, this._debounceValue, observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(117 /* EditorOption.suggest */).preview), observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(117 /* EditorOption.suggest */).previewMode), observableFromEvent(editor.onDidChangeConfiguration, () => editor.getOption(62 /* EditorOption.inlineSuggest */).mode), this._enabled); this.model.set(model, tx); } }); })); const styleElement = this._register(createStyleSheet2()); this._register(autorun(reader => { const fontFamily = this._fontFamily.read(reader); styleElement.setStyle(fontFamily === '' || fontFamily === 'default' ? `` : ` .monaco-editor .ghost-text-decoration, .monaco-editor .ghost-text-decoration-preview, .monaco-editor .ghost-text { font-family: ${fontFamily}; }`); })); const getReason = (e) => { var _a; if (e.isUndoing) { return VersionIdChangeReason.Undo; } if (e.isRedoing) { return VersionIdChangeReason.Redo; } if ((_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.isAcceptingPartially) { return VersionIdChangeReason.AcceptWord; } return VersionIdChangeReason.Other; }; this._register(editor.onDidChangeModelContent((e) => transaction(tx => /** @description InlineCompletionsController.onDidChangeModelContent */ this.updateObservables(tx, getReason(e))))); this._register(editor.onDidChangeCursorPosition(e => transaction(tx => { var _a; /** @description InlineCompletionsController.onDidChangeCursorPosition */ this.updateObservables(tx, VersionIdChangeReason.Other); if (e.reason === 3 /* CursorChangeReason.Explicit */ || e.source === 'api') { (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.stop(tx); } }))); this._register(editor.onDidType(() => transaction(tx => { var _a; /** @description InlineCompletionsController.onDidType */ this.updateObservables(tx, VersionIdChangeReason.Other); if (this._enabled.get()) { (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.trigger(tx); } }))); this._register(this._commandService.onDidExecuteCommand((e) => { // These commands don't trigger onDidType. const commands = new Set([ CoreEditingCommands.Tab.id, CoreEditingCommands.DeleteLeft.id, CoreEditingCommands.DeleteRight.id, inlineSuggestCommitId, 'acceptSelectedSuggestion', ]); if (commands.has(e.commandId) && editor.hasTextFocus() && this._enabled.get()) { transaction(tx => { var _a; /** @description onDidExecuteCommand */ (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.trigger(tx); }); } })); this._register(this.editor.onDidBlurEditorWidget(() => { // This is a hidden setting very useful for debugging if (this._contextKeyService.getContextKeyValue('accessibleViewIsShown') || this._configurationService.getValue('editor.inlineSuggest.keepOnBlur') || editor.getOption(62 /* EditorOption.inlineSuggest */).keepOnBlur) { return; } if (InlineSuggestionHintsContentWidget.dropDownVisible) { return; } transaction(tx => { var _a; /** @description InlineCompletionsController.onDidBlurEditorWidget */ (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.stop(tx); }); })); this._register(autorun(reader => { var _a; /** @description InlineCompletionsController.forceRenderingAbove */ const state = (_a = this.model.read(reader)) === null || _a === void 0 ? void 0 : _a.state.read(reader); if (state === null || state === void 0 ? void 0 : state.suggestItem) { if (state.ghostText.lineCount >= 2) { this._suggestWidgetAdaptor.forceRenderingAbove(); } } else { this._suggestWidgetAdaptor.stopForceRenderingAbove(); } })); this._register(toDisposable(() => { this._suggestWidgetAdaptor.stopForceRenderingAbove(); })); let lastInlineCompletionId = undefined; this._register(autorunHandleChanges({ handleChange: (context, changeSummary) => { if (context.didChange(this._playAudioCueSignal)) { lastInlineCompletionId = undefined; } return true; }, }, async (reader) => { /** @description InlineCompletionsController.playAudioCueAndReadSuggestion */ this._playAudioCueSignal.read(reader); const model = this.model.read(reader); const state = model === null || model === void 0 ? void 0 : model.state.read(reader); if (!model || !state || !state.inlineCompletion) { lastInlineCompletionId = undefined; return; } if (state.inlineCompletion.semanticId !== lastInlineCompletionId) { lastInlineCompletionId = state.inlineCompletion.semanticId; const lineText = model.textModel.getLineContent(state.ghostText.lineNumber); this._audioCueService.playAudioCue(AudioCue.inlineSuggestion).then(() => { if (this.editor.getOption(8 /* EditorOption.screenReaderAnnounceInlineSuggestion */)) { this.provideScreenReaderUpdate(state.ghostText.renderForScreenReader(lineText)); } }); } })); this._register(new InlineCompletionsHintsWidget(this.editor, this.model, this._instantiationService)); this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('accessibility.verbosity.inlineCompletions')) { this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') }); } })); this.editor.updateOptions({ inlineCompletionsAccessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.inlineCompletions') }); } playAudioCue(tx) { this._playAudioCueSignal.trigger(tx); } provideScreenReaderUpdate(content) { const accessibleViewShowing = this._contextKeyService.getContextKeyValue('accessibleViewIsShown'); const accessibleViewKeybinding = this._keybindingService.lookupKeybinding('editor.action.accessibleView'); let hint; if (!accessibleViewShowing && accessibleViewKeybinding && this.editor.getOption(147 /* EditorOption.inlineCompletionsAccessibilityVerbose */)) { hint = localize('showAccessibleViewHint', "Inspect this in the accessible view ({0})", accessibleViewKeybinding.getAriaLabel()); } hint ? alert(content + ', ' + hint) : alert(content); } /** * Copies over the relevant state from the text model to observables. * This solves all kind of eventing issues, as we make sure we always operate on the latest state, * regardless of who calls into us. */ updateObservables(tx, changeReason) { var _a, _b; const newModel = this.editor.getModel(); this._textModelVersionId.set((_a = newModel === null || newModel === void 0 ? void 0 : newModel.getVersionId()) !== null && _a !== void 0 ? _a : -1, tx, changeReason); this._cursorPosition.set((_b = this.editor.getPosition()) !== null && _b !== void 0 ? _b : new Position(1, 1), tx); } shouldShowHoverAt(range) { var _a; const ghostText = (_a = this.model.get()) === null || _a === void 0 ? void 0 : _a.ghostText.get(); if (ghostText) { return ghostText.parts.some(p => range.containsPosition(new Position(ghostText.lineNumber, p.column))); } return false; } shouldShowHoverAtViewZone(viewZoneId) { return this._ghostTextWidget.ownsViewZone(viewZoneId); } }; InlineCompletionsController.ID = 'editor.contrib.inlineCompletionsController'; InlineCompletionsController = InlineCompletionsController_1 = __decorate([ __param(1, IInstantiationService), __param(2, IContextKeyService), __param(3, IConfigurationService), __param(4, ICommandService), __param(5, ILanguageFeatureDebounceService), __param(6, ILanguageFeaturesService), __param(7, IAudioCueService), __param(8, IKeybindingService) ], InlineCompletionsController); export { InlineCompletionsController };