
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { alert } from '../../../../base/browser/ui/aria/aria.js';
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
import { onUnexpectedExternalError, onUnexpectedError } from '../../../../base/common/errors.js';
import { Emitter, Event } from '../../../../base/common/event.js';
import { KeyCode, KeyMod as KeyMod$1 } from '../../../../base/common/keyCodes.js';
import { KeyCodeChord } from '../../../../base/common/keybindings.js';
import { MutableDisposable, DisposableStore, toDisposable, dispose } from '../../../../base/common/lifecycle.js';
import { isMacintosh } from '../../../../base/common/platform.js';
import { StopWatch } from '../../../../base/common/stopwatch.js';
import { assertType, isObject } from '../../../../base/common/types.js';
import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js';
import { EditorAction, registerEditorContribution, EditorContributionInstantiation, registerEditorAction, EditorCommand, registerEditorCommand } from '../../../browser/editorExtensions.js';
import { EditorOption } from '../../../common/config/editorOptions.js';
import { EditOperation } from '../../../common/core/editOperation.js';
import { Position } from '../../../common/core/position.js';
import { Range } from '../../../common/core/range.js';
import { ScrollType } from '../../../common/editorCommon.js';
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
import { TrackedRangeStickiness } from '../../../common/model.js';
import { CompletionItemInsertTextRule, CompletionTriggerKind } from '../../../common/languages.js';
import { SnippetController2 } from '../../snippet/browser/snippetController2.js';
import { SnippetParser } from '../../snippet/browser/snippetParser.js';
import { ISuggestMemoryService } from './suggestMemory.service.js';
import { WordContextKey } from './wordContextKey.js';
import { localize, localize2 } from '../../../../nls.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
import { ICommandService } from '../../../../platform/commands/common/commands.service.js';
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.service.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { Context, suggestWidgetStatusbarMenu } from './suggest.js';
import { SuggestAlternatives } from './suggestAlternatives.js';
import { CommitCharacterController } from './suggestCommitCharacters.js';
import { SuggestModel, State } from './suggestModel.js';
import { OvertypingCapturer } from './suggestOvertypingCapturer.js';
import { SuggestWidget } from './suggestWidget.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.service.js';
import { extname, basename } from '../../../../base/common/resources.js';
import { hash } from '../../../../base/common/hash.js';
import { WindowIdleValue, getWindow } from '../../../../base/browser/dom.js';
import { ModelDecorationOptions } from '../../../common/model/textModel.js';

var SuggestController_1;
class LineSuffix {
    constructor(_model, _position) {
        this._model = _model;
        this._position = _position;
        this._decorationOptions = ModelDecorationOptions.register({
            description: 'suggest-line-suffix',
            stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
        });
        const maxColumn = _model.getLineMaxColumn(_position.lineNumber);
        if (maxColumn !== _position.column) {
            const offset = _model.getOffsetAt(_position);
            const end = _model.getPositionAt(offset + 1);
            _model.changeDecorations(accessor => {
                if (this._marker) {
                    accessor.removeDecoration(this._marker);
                }
                this._marker = accessor.addDecoration(Range.fromPositions(_position, end), this._decorationOptions);
            });
        }
    }
    dispose() {
        if (this._marker && !this._model.isDisposed()) {
            this._model.changeDecorations(accessor => {
                accessor.removeDecoration(this._marker);
                this._marker = undefined;
            });
        }
    }
    delta(position) {
        if (this._model.isDisposed() || this._position.lineNumber !== position.lineNumber) {
            return 0;
        }
        if (this._marker) {
            const range = this._model.getDecorationRange(this._marker);
            const end = this._model.getOffsetAt(range.getStartPosition());
            return end - this._model.getOffsetAt(position);
        }
        else {
            return this._model.getLineMaxColumn(position.lineNumber) - position.column;
        }
    }
}
var InsertFlags;
(function (InsertFlags) {
    InsertFlags[InsertFlags["None"] = 0] = "None";
    InsertFlags[InsertFlags["NoBeforeUndoStop"] = 1] = "NoBeforeUndoStop";
    InsertFlags[InsertFlags["NoAfterUndoStop"] = 2] = "NoAfterUndoStop";
    InsertFlags[InsertFlags["KeepAlternativeSuggestions"] = 4] = "KeepAlternativeSuggestions";
    InsertFlags[InsertFlags["AlternativeOverwriteConfig"] = 8] = "AlternativeOverwriteConfig";
})(InsertFlags || (InsertFlags = {}));
let SuggestController = class SuggestController {
    static { SuggestController_1 = this; }
    static { this.ID = 'editor.contrib.suggestController'; }
    static get(editor) {
        return editor.getContribution(SuggestController_1.ID);
    }
    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._onWillInsertSuggestItem = ( new Emitter());
        this.onWillInsertSuggestItem = this._onWillInsertSuggestItem.event;
        this.editor = editor;
        this.model = _instantiationService.createInstance(SuggestModel, this.editor);
        this._selectors.register({
            priority: 0,
            select: (model, pos, items) => this._memoryService.select(model, pos, items)
        });
        const ctxInsertMode = Context.InsertMode.bindTo(_contextKeyService);
        ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode);
        this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)));
        this.widget = this._toDispose.add(( new WindowIdleValue(getWindow(editor.getDomNode()), () => {
            const widget = this._instantiationService.createInstance(SuggestWidget, this.editor);
            this._toDispose.add(widget);
            this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, InsertFlags.None), this));
            const commitCharacterController = ( new CommitCharacterController(
                this.editor,
                widget,
                this.model,
                item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop)
            ));
            this._toDispose.add(commitCharacterController);
            const ctxMakesTextEdit = Context.MakesTextEdit.bindTo(this._contextKeyService);
            const ctxHasInsertAndReplace = Context.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
            const ctxCanResolve = Context.CanResolve.bindTo(this._contextKeyService);
            this._toDispose.add(toDisposable(() => {
                ctxMakesTextEdit.reset();
                ctxHasInsertAndReplace.reset();
                ctxCanResolve.reset();
            }));
            this._toDispose.add(widget.onDidFocus(({ item }) => {
                const position = this.editor.getPosition();
                const startColumn = item.editStart.column;
                const endColumn = position.column;
                let value = true;
                if (this.editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart'
                    && this.model.state === State.Auto
                    && !item.completion.additionalTextEdits
                    && !(item.completion.insertTextRules & CompletionItemInsertTextRule.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);
                ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
                ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label);
            }));
            this._toDispose.add(widget.onDetailsKeyDown(e => {
                if (e.toKeyCodeChord().equals(( new KeyCodeChord(true, false, false, false, KeyCode.KeyC))) ||
                    (isMacintosh && e.toKeyCodeChord().equals(( new KeyCodeChord(false, false, false, true, KeyCode.KeyC))))) {
                    e.stopPropagation();
                    return;
                }
                if (!e.toKeyCodeChord().isModifierKey()) {
                    this.editor.focus();
                }
            }));
            return widget;
        })));
        this._overtypingCapturer = this._toDispose.add(( new WindowIdleValue(getWindow(editor.getDomNode()), () => {
            return this._toDispose.add(( new OvertypingCapturer(this.editor, this.model)));
        })));
        this._alternatives = this._toDispose.add(( new WindowIdleValue(getWindow(editor.getDomNode()), () => {
            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.triggerOptions.shy) {
                return;
            }
            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 = 0;
            }
            if (this.model.state === State.Idle) {
                return;
            }
            let noFocus = false;
            if (e.triggerOptions.auto) {
                const options = this.editor.getOption(EditorOption.suggest);
                if (options.selectionMode === 'never' || options.selectionMode === 'always') {
                    noFocus = options.selectionMode === 'never';
                }
                else if (options.selectionMode === 'whenTriggerCharacter') {
                    noFocus = e.triggerOptions.triggerKind !== CompletionTriggerKind.TriggerCharacter;
                }
                else if (options.selectionMode === 'whenQuickSuggestion') {
                    noFocus = e.triggerOptions.triggerKind === CompletionTriggerKind.TriggerCharacter && !e.triggerOptions.refilter;
                }
            }
            this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.triggerOptions.auto, noFocus);
        }));
        this._toDispose.add(this.model.onDidCancel(e => {
            if (!e.retrigger) {
                this.widget.value.hideWidget();
            }
        }));
        this._toDispose.add(this.editor.onDidBlurEditorWidget(() => {
            {
                this.model.cancel();
                this.model.clear();
            }
        }));
        const acceptSuggestionsOnEnter = Context.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
        const updateFromConfig = () => {
            const acceptSuggestionOnEnter = this.editor.getOption(EditorOption.acceptSuggestionOnEnter);
            acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart');
        };
        this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig()));
        updateFromConfig();
    }
    dispose() {
        this._alternatives.dispose();
        this._toDispose.dispose();
        this.widget.dispose();
        this.model.dispose();
        this._lineSuffix.dispose();
        this._onWillInsertSuggestItem.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 snippetController = SnippetController2.get(this.editor);
        if (!snippetController) {
            return;
        }
        this._onWillInsertSuggestItem.fire({ item: event.item });
        const model = this.editor.getModel();
        const modelVersionNow = model.getAlternativeVersionId();
        const { item } = event;
        const tasks = [];
        const cts = ( new CancellationTokenSource());
        if (!(flags & InsertFlags.NoBeforeUndoStop)) {
            this.editor.pushUndoStop();
        }
        const info = this.getOverwriteInfo(item, Boolean(flags & InsertFlags.AlternativeOverwriteConfig));
        this._memoryService.memorize(model, this.editor.getPosition(), item);
        const isResolved = item.isResolved;
        let _commandExectionDuration = -1;
        let _additionalEditsAppliedAsync = -1;
        if (Array.isArray(item.completion.additionalTextEdits)) {
            this.model.cancel();
            const scrollState = StableEditorScrollState.capture(this.editor);
            this.editor.executeEdits('suggestController.additionalTextEdits.sync', ( item.completion.additionalTextEdits.map(edit => {
                let range = Range.lift(edit.range);
                if (range.startLineNumber === item.position.lineNumber && range.startColumn > item.position.column) {
                    const columnDelta = this.editor.getPosition().column - item.position.column;
                    const startColumnDelta = columnDelta;
                    const endColumnDelta = Range.spansMultipleLines(range) ? 0 : columnDelta;
                    range = ( new Range(
                        range.startLineNumber,
                        range.startColumn + startColumnDelta,
                        range.endLineNumber,
                        range.endColumn + endColumnDelta
                    ));
                }
                return EditOperation.replaceMove(range, edit.text);
            })));
            scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
        }
        else if (!isResolved) {
            const sw = ( new StopWatch());
            let position;
            const docListener = model.onDidChangeContent(e => {
                if (e.isFlush) {
                    cts.cancel();
                    docListener.dispose();
                    return;
                }
                for (const change of e.changes) {
                    const thisPosition = Range.getEndPosition(change.range);
                    if (!position || Position.isBefore(thisPosition, position)) {
                        position = thisPosition;
                    }
                }
            });
            const oldFlags = flags;
            flags |= InsertFlags.NoAfterUndoStop;
            let didType = false;
            const typeListener = this.editor.onWillType(() => {
                typeListener.dispose();
                didType = true;
                if (!(oldFlags & InsertFlags.NoAfterUndoStop)) {
                    this.editor.pushUndoStop();
                }
            });
            tasks.push(item.resolve(cts.token).then(() => {
                if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) {
                    return undefined;
                }
                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.replaceMove(Range.lift(edit.range), edit.text))));
                scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
                if (didType || !(oldFlags & InsertFlags.NoAfterUndoStop)) {
                    this.editor.pushUndoStop();
                }
                return true;
            }).then(applied => {
                this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied);
                _additionalEditsAppliedAsync = applied === true ? 1 : applied === false ? 0 : -2;
            }).finally(() => {
                docListener.dispose();
                typeListener.dispose();
            }));
        }
        let { insertText } = item.completion;
        if (!(item.completion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)) {
            insertText = SnippetParser.escape(insertText);
        }
        this.model.cancel();
        snippetController.insert(insertText, {
            overwriteBefore: info.overwriteBefore,
            overwriteAfter: info.overwriteAfter,
            undoStopBefore: false,
            undoStopAfter: false,
            adjustWhitespace: !(item.completion.insertTextRules & CompletionItemInsertTextRule.KeepWhitespace),
            clipboardText: event.model.clipboardText,
            overtypingCapturer: this._overtypingCapturer.value
        });
        if (!(flags & InsertFlags.NoAfterUndoStop)) {
            this.editor.pushUndoStop();
        }
        if (item.completion.command) {
            if (item.completion.command.id === TriggerSuggestAction.id) {
                this.model.trigger({ auto: true, retrigger: true });
            }
            else {
                const sw = ( new StopWatch());
                tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(e => {
                    if (item.completion.extensionId) {
                        onUnexpectedExternalError(e);
                    }
                    else {
                        onUnexpectedError(e);
                    }
                }).finally(() => {
                    _commandExectionDuration = sw.elapsed();
                }));
            }
        }
        if (flags & InsertFlags.KeepAlternativeSuggestions) {
            this._alternatives.value.set(event, next => {
                cts.cancel();
                while (model.canUndo()) {
                    if (modelVersionNow !== model.getAlternativeVersionId()) {
                        model.undo();
                    }
                    this._insertSuggestion(next, InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop | (flags & InsertFlags.AlternativeOverwriteConfig ? InsertFlags.AlternativeOverwriteConfig : 0));
                    break;
                }
            });
        }
        this._alertCompletionItem(item);
        Promise.all(tasks).finally(() => {
            this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync, event.index, event.model.items);
            this.model.clear();
            cts.dispose();
        });
    }
    _reportSuggestionAcceptedTelemetry(item, model, itemResolved, commandExectionDuration, additionalEditsAppliedAsync, index, completionItems) {
        if (Math.random() > 0.0001) {
            return;
        }
        const labelMap = ( new Map());
        for (let i = 0; i < Math.min(30, completionItems.length); i++) {
            const label = completionItems[i].textLabel;
            if (( labelMap.has(label))) {
                labelMap.get(label).push(i);
            }
            else {
                labelMap.set(label, [i]);
            }
        }
        const firstIndexArray = labelMap.get(item.textLabel);
        const hasDuplicates = firstIndexArray && firstIndexArray.length > 1;
        const firstIndex = hasDuplicates ? firstIndexArray[0] : -1;
        this._telemetryService.publicLog2('suggest.acceptedSuggestion', {
            extensionId: item.extensionId?.value ?? 'unknown',
            providerId: item.provider._debugDisplayName ?? 'unknown',
            kind: item.completion.kind,
            basenameHash: ( hash(basename(model.uri)).toString(16)),
            languageId: model.getLanguageId(),
            fileExtension: extname(model.uri),
            resolveInfo: !item.provider.resolveCompletionItem ? -1 : itemResolved ? 1 : 0,
            resolveDuration: item.resolveDuration,
            commandDuration: commandExectionDuration,
            additionalEditsAsync: additionalEditsAppliedAsync,
            index,
            firstIndex,
        });
    }
    getOverwriteInfo(item, toggleMode) {
        assertType(this.editor.hasModel());
        let replace = this.editor.getOption(EditorOption.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)) {
            const msg = ( localize(
                1510,
                "Accepting '{0}' made {1} additional edits",
                item.textLabel,
                item.completion.additionalTextEdits.length
            ));
            alert(msg);
        }
    }
    triggerSuggest(onlyFrom, auto, noFilter) {
        if (this.editor.hasModel()) {
            this.model.trigger({
                auto: auto ?? false,
                completionOptions: { providerFilter: onlyFrom, kindFilter: noFilter ? ( new Set()) : undefined }
            });
            this.editor.revealPosition(this.editor.getPosition(), ScrollType.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 & CompletionItemInsertTextRule.InsertAsSnippet || item.completion.additionalTextEdits) {
                return true;
            }
            const position = this.editor.getPosition();
            const startColumn = item.editStart.column;
            const endColumn = position.column;
            if (endColumn - startColumn !== item.completion.insertText.length) {
                return true;
            }
            const textNow = this.editor.getModel().getValueInRange({
                startLineNumber: position.lineNumber,
                startColumn,
                endLineNumber: position.lineNumber,
                endColumn
            });
            return textNow !== item.completion.insertText;
        };
        Event.once(this.model.onDidTrigger)(_ => {
            const listener = [];
            Event.any(this.model.onDidTrigger, this.model.onDidCancel)(() => {
                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 }, InsertFlags.KeepAlternativeSuggestions | InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop);
            }, undefined, listener);
        });
        this.model.trigger({ auto: false, shy: true });
        this.editor.revealPosition(positionNow, ScrollType.Smooth);
        this.editor.focus();
    }
    acceptSelectedSuggestion(keepAlternativeSuggestions, alternativeOverwriteConfig) {
        const item = this.widget.value.getFocusedItem();
        let flags = 0;
        if (keepAlternativeSuggestions) {
            flags |= InsertFlags.KeepAlternativeSuggestions;
        }
        if (alternativeOverwriteConfig) {
            flags |= InsertFlags.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();
    }
    focusSuggestion() {
        this.widget.value.focusSelected();
    }
    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) {
            return;
        }
        this.widget.value.stopForceRenderingAbove();
    }
    registerSelector(selector) {
        return this._selectors.register(selector);
    }
};
SuggestController = SuggestController_1 = ( __decorate([
    ( __param(1, ISuggestMemoryService)),
    ( __param(2, ICommandService)),
    ( __param(3, IContextKeyService)),
    ( __param(4, IInstantiationService)),
    ( __param(5, ILogService)),
    ( __param(6, ITelemetryService))
], 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;
    }
}
class TriggerSuggestAction extends EditorAction {
    static { this.id = 'editor.action.triggerSuggest'; }
    constructor() {
        super({
            id: TriggerSuggestAction.id,
            label: ( localize2(1511, "Trigger Suggest")),
            precondition: ( ContextKeyExpr.and(
                EditorContextKeys.writable,
                EditorContextKeys.hasCompletionItemProvider,
                ( Context.Visible.toNegated())
            )),
            kbOpts: {
                kbExpr: EditorContextKeys.textInputFocus,
                primary: KeyMod$1.CtrlCmd | KeyCode.Space,
                secondary: [KeyMod$1.CtrlCmd | KeyCode.KeyI],
                mac: { primary: KeyMod$1.WinCtrl | KeyCode.Space, secondary: [KeyMod$1.Alt | KeyCode.Escape, KeyMod$1.CtrlCmd | KeyCode.KeyI] },
                weight: KeybindingWeight.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, undefined);
    }
}
registerEditorContribution(SuggestController.ID, SuggestController, EditorContributionInstantiation.BeforeFirstInteraction);
registerEditorAction(TriggerSuggestAction);
const weight = KeybindingWeight.EditorContrib + 90;
const SuggestCommand = EditorCommand.bindToContribution(SuggestController.get);
registerEditorCommand(( new SuggestCommand({
    id: 'acceptSelectedSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, Context.HasFocusedSuggestion)),
    handler(x) {
        x.acceptSelectedSuggestion(true, false);
    },
    kbOpts: [{
            primary: KeyCode.Tab,
            kbExpr: ( ContextKeyExpr.and(Context.Visible, EditorContextKeys.textInputFocus)),
            weight,
        }, {
            primary: KeyCode.Enter,
            kbExpr: ( ContextKeyExpr.and(
                Context.Visible,
                EditorContextKeys.textInputFocus,
                Context.AcceptSuggestionsOnEnter,
                Context.MakesTextEdit
            )),
            weight,
        }],
    menuOpts: [{
            menuId: suggestWidgetStatusbarMenu,
            title: ( localize(1512, "Insert")),
            group: 'left',
            order: 1,
            when: ( Context.HasInsertAndReplaceRange.toNegated())
        }, {
            menuId: suggestWidgetStatusbarMenu,
            title: ( localize(1512, "Insert")),
            group: 'left',
            order: 1,
            when: ( ContextKeyExpr.and(Context.HasInsertAndReplaceRange, ( Context.InsertMode.isEqualTo('insert'))))
        }, {
            menuId: suggestWidgetStatusbarMenu,
            title: ( localize(1513, "Replace")),
            group: 'left',
            order: 1,
            when: ( ContextKeyExpr.and(Context.HasInsertAndReplaceRange, ( Context.InsertMode.isEqualTo('replace'))))
        }]
})));
registerEditorCommand(( new SuggestCommand({
    id: 'acceptAlternativeSelectedSuggestion',
    precondition: ( ContextKeyExpr.and(
        Context.Visible,
        EditorContextKeys.textInputFocus,
        Context.HasFocusedSuggestion
    )),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyMod$1.Shift | KeyCode.Enter,
        secondary: [KeyMod$1.Shift | KeyCode.Tab],
    },
    handler(x) {
        x.acceptSelectedSuggestion(false, true);
    },
    menuOpts: [{
            menuId: suggestWidgetStatusbarMenu,
            group: 'left',
            order: 2,
            when: ( ContextKeyExpr.and(Context.HasInsertAndReplaceRange, ( Context.InsertMode.isEqualTo('insert')))),
            title: ( localize(1513, "Replace"))
        }, {
            menuId: suggestWidgetStatusbarMenu,
            group: 'left',
            order: 2,
            when: ( ContextKeyExpr.and(Context.HasInsertAndReplaceRange, ( Context.InsertMode.isEqualTo('replace')))),
            title: ( localize(1512, "Insert"))
        }]
})));
CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion');
registerEditorCommand(( new SuggestCommand({
    id: 'hideSuggestWidget',
    precondition: Context.Visible,
    handler: x => x.cancelSuggestWidget(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.Escape,
        secondary: [KeyMod$1.Shift | KeyCode.Escape]
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectNextSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectNextSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.DownArrow,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.DownArrow],
        mac: { primary: KeyCode.DownArrow, secondary: [KeyMod$1.CtrlCmd | KeyCode.DownArrow, KeyMod$1.WinCtrl | KeyCode.KeyN] }
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectNextPageSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectNextPageSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.PageDown,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.PageDown]
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectLastSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectLastSuggestion()
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectPrevSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectPrevSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.UpArrow,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.UpArrow],
        mac: { primary: KeyCode.UpArrow, secondary: [KeyMod$1.CtrlCmd | KeyCode.UpArrow, KeyMod$1.WinCtrl | KeyCode.KeyP] }
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectPrevPageSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectPrevPageSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.PageUp,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.PageUp]
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'selectFirstSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( ContextKeyExpr.or(Context.MultipleSuggestions, ( Context.HasFocusedSuggestion.negate()))))),
    handler: c => c.selectFirstSuggestion()
})));
registerEditorCommand(( new SuggestCommand({
    id: 'focusSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( Context.HasFocusedSuggestion.negate()))),
    handler: x => x.focusSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyMod$1.CtrlCmd | KeyCode.Space,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.KeyI],
        mac: { primary: KeyMod$1.WinCtrl | KeyCode.Space, secondary: [KeyMod$1.CtrlCmd | KeyCode.KeyI] }
    },
})));
registerEditorCommand(( new SuggestCommand({
    id: 'focusAndAcceptSuggestion',
    precondition: ( ContextKeyExpr.and(Context.Visible, ( Context.HasFocusedSuggestion.negate()))),
    handler: c => {
        c.focusSuggestion();
        c.acceptSelectedSuggestion(true, false);
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'toggleSuggestionDetails',
    precondition: ( ContextKeyExpr.and(Context.Visible, Context.HasFocusedSuggestion)),
    handler: x => x.toggleSuggestionDetails(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyMod$1.CtrlCmd | KeyCode.Space,
        secondary: [KeyMod$1.CtrlCmd | KeyCode.KeyI],
        mac: { primary: KeyMod$1.WinCtrl | KeyCode.Space, secondary: [KeyMod$1.CtrlCmd | KeyCode.KeyI] }
    },
    menuOpts: [{
            menuId: suggestWidgetStatusbarMenu,
            group: 'right',
            order: 1,
            when: ( ContextKeyExpr.and(Context.DetailsVisible, Context.CanResolve)),
            title: ( localize(1514, "Show Less"))
        }, {
            menuId: suggestWidgetStatusbarMenu,
            group: 'right',
            order: 1,
            when: ( ContextKeyExpr.and(( Context.DetailsVisible.toNegated()), Context.CanResolve)),
            title: ( localize(1515, "Show More"))
        }]
})));
registerEditorCommand(( new SuggestCommand({
    id: 'toggleExplainMode',
    precondition: Context.Visible,
    handler: x => x.toggleExplainMode(),
    kbOpts: {
        weight: KeybindingWeight.EditorContrib,
        primary: KeyMod$1.CtrlCmd | KeyCode.Slash,
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'toggleSuggestionFocus',
    precondition: Context.Visible,
    handler: x => x.toggleSuggestionFocus(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyMod$1.CtrlCmd | KeyMod$1.Alt | KeyCode.Space,
        mac: { primary: KeyMod$1.WinCtrl | KeyMod$1.Alt | KeyCode.Space }
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'insertBestCompletion',
    precondition: ( ContextKeyExpr.and(EditorContextKeys.textInputFocus, ( ContextKeyExpr.equals('config.editor.tabCompletion', 'on')), WordContextKey.AtEnd, ( Context.Visible.toNegated()), ( SuggestAlternatives.OtherSuggestions.toNegated()), ( SnippetController2.InSnippetMode.toNegated()))),
    handler: (x, arg) => {
        x.triggerSuggestAndAcceptBest(isObject(arg) ? { fallback: 'tab', ...arg } : { fallback: 'tab' });
    },
    kbOpts: {
        weight,
        primary: KeyCode.Tab
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'insertNextSuggestion',
    precondition: ( ContextKeyExpr.and(EditorContextKeys.textInputFocus, ( ContextKeyExpr.equals('config.editor.tabCompletion', 'on')), SuggestAlternatives.OtherSuggestions, ( Context.Visible.toNegated()), ( SnippetController2.InSnippetMode.toNegated()))),
    handler: x => x.acceptNextSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyCode.Tab
    }
})));
registerEditorCommand(( new SuggestCommand({
    id: 'insertPrevSuggestion',
    precondition: ( ContextKeyExpr.and(EditorContextKeys.textInputFocus, ( ContextKeyExpr.equals('config.editor.tabCompletion', 'on')), SuggestAlternatives.OtherSuggestions, ( Context.Visible.toNegated()), ( SnippetController2.InSnippetMode.toNegated()))),
    handler: x => x.acceptPrevSuggestion(),
    kbOpts: {
        weight: weight,
        kbExpr: EditorContextKeys.textInputFocus,
        primary: KeyMod$1.Shift | KeyCode.Tab
    }
})));
registerEditorAction(class extends EditorAction {
    constructor() {
        super({
            id: 'editor.action.resetSuggestSize',
            label: ( localize2(1516, "Reset Suggest Widget Size")),
            precondition: undefined
        });
    }
    run(_accessor, editor) {
        SuggestController.get(editor)?.resetWidgetSize();
    }
});

export { SuggestController, TriggerSuggestAction };
