"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const async = require("../../../../base/common/async.js");
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const event = require("../../../../base/common/event.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const strings = require("../../../../base/common/strings.js");
const selection = require("../../../common/core/selection.js");
const editorWorker = require("../../../common/services/editorWorker.js");
const snippetController2 = require("../../snippet/browser/snippetController2.js");
const wordDistance = require("./wordDistance.js");
const clipboardService = require("../../../../platform/clipboard/common/clipboardService.js");
const configuration = require("../../../../platform/configuration/common/configuration.js");
const contextkey = require("../../../../platform/contextkey/common/contextkey.js");
const log = require("../../../../platform/log/common/log.js");
const telemetry = require("../../../../platform/telemetry/common/telemetry.js");
const completionModel = require("./completionModel.js");
const suggest = require("./suggest.js");
const languageFeatures = require("../../../common/services/languageFeatures.js");
var __decorate = globalThis && globalThis.__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 = globalThis && globalThis.__param || function(paramIndex, decorator) {
  return function(target, key) {
    decorator(target, key, paramIndex);
  };
};
var __awaiter = globalThis && globalThis.__awaiter || function(thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function(resolve) {
      resolve(value);
    });
  }
  return new (P || (P = Promise))(function(resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }
    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }
    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }
    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};
class LineContext {
  constructor(model, position, auto, shy) {
    this.leadingLineContent = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
    this.leadingWord = model.getWordUntilPosition(position);
    this.lineNumber = position.lineNumber;
    this.column = position.column;
    this.auto = auto;
    this.shy = shy;
  }
  static shouldAutoTrigger(editor) {
    if (!editor.hasModel()) {
      return false;
    }
    const model = editor.getModel();
    const pos = editor.getPosition();
    model.tokenizeIfCheap(pos.lineNumber);
    const word = model.getWordAtPosition(pos);
    if (!word) {
      return false;
    }
    if (word.endColumn !== pos.column) {
      return false;
    }
    if (!isNaN(Number(word.word))) {
      return false;
    }
    return true;
  }
}
function canShowQuickSuggest(editor, contextKeyService, configurationService) {
  if (!Boolean(contextKeyService.getContextKeyValue("inlineSuggestionVisible"))) {
    return true;
  }
  const allowQuickSuggestions = configurationService.getValue("editor.inlineSuggest.allowQuickSuggestions");
  if (allowQuickSuggestions !== void 0) {
    return Boolean(allowQuickSuggestions);
  }
  return false;
}
function canShowSuggestOnTriggerCharacters(editor, contextKeyService, configurationService) {
  if (!Boolean(contextKeyService.getContextKeyValue("inlineSuggestionVisible"))) {
    return true;
  }
  const allowQuickSuggestions = configurationService.getValue("editor.inlineSuggest.allowSuggestOnTriggerCharacters");
  if (allowQuickSuggestions !== void 0) {
    return Boolean(allowQuickSuggestions);
  }
  return false;
}
exports.SuggestModel = class SuggestModel {
  constructor(_editor, _editorWorkerService, _clipboardService, _telemetryService, _logService, _contextKeyService, _configurationService, _languageFeaturesService) {
    this._editor = _editor;
    this._editorWorkerService = _editorWorkerService;
    this._clipboardService = _clipboardService;
    this._telemetryService = _telemetryService;
    this._logService = _logService;
    this._contextKeyService = _contextKeyService;
    this._configurationService = _configurationService;
    this._languageFeaturesService = _languageFeaturesService;
    this._toDispose = new lifecycle.DisposableStore();
    this._quickSuggestDelay = 10;
    this._triggerCharacterListener = new lifecycle.DisposableStore();
    this._triggerQuickSuggest = new async.TimeoutTimer();
    this._state = 0;
    this._completionDisposables = new lifecycle.DisposableStore();
    this._onDidCancel = new event.Emitter();
    this._onDidTrigger = new event.Emitter();
    this._onDidSuggest = new event.Emitter();
    this.onDidCancel = this._onDidCancel.event;
    this.onDidTrigger = this._onDidTrigger.event;
    this.onDidSuggest = this._onDidSuggest.event;
    this._telemetryGate = 0;
    this._currentSelection = this._editor.getSelection() || new selection.Selection(1, 1, 1, 1);
    this._toDispose.add(this._editor.onDidChangeModel(() => {
      this._updateTriggerCharacters();
      this.cancel();
    }));
    this._toDispose.add(this._editor.onDidChangeModelLanguage(() => {
      this._updateTriggerCharacters();
      this.cancel();
    }));
    this._toDispose.add(this._editor.onDidChangeConfiguration(() => {
      this._updateTriggerCharacters();
      this._updateQuickSuggest();
    }));
    this._toDispose.add(this._languageFeaturesService.completionProvider.onDidChange(() => {
      this._updateTriggerCharacters();
      this._updateActiveSuggestSession();
    }));
    let editorIsComposing = false;
    this._toDispose.add(this._editor.onDidCompositionStart(() => {
      editorIsComposing = true;
    }));
    this._toDispose.add(this._editor.onDidCompositionEnd(() => {
      editorIsComposing = false;
      this._onCompositionEnd();
    }));
    this._toDispose.add(this._editor.onDidChangeCursorSelection((e) => {
      if (!editorIsComposing) {
        this._onCursorChange(e);
      }
    }));
    this._toDispose.add(this._editor.onDidChangeModelContent(() => {
      if (!editorIsComposing) {
        this._refilterCompletionItems();
      }
    }));
    this._updateTriggerCharacters();
    this._updateQuickSuggest();
  }
  dispose() {
    lifecycle.dispose(this._triggerCharacterListener);
    lifecycle.dispose([this._onDidCancel, this._onDidSuggest, this._onDidTrigger, this._triggerQuickSuggest]);
    this._toDispose.dispose();
    this._completionDisposables.dispose();
    this.cancel();
  }
  // --- handle configuration & precondition changes
  _updateQuickSuggest() {
    this._quickSuggestDelay = this._editor.getOption(
      80
      /* quickSuggestionsDelay */
    );
    if (isNaN(this._quickSuggestDelay) || !this._quickSuggestDelay && this._quickSuggestDelay !== 0 || this._quickSuggestDelay < 0) {
      this._quickSuggestDelay = 10;
    }
  }
  _updateTriggerCharacters() {
    this._triggerCharacterListener.clear();
    if (this._editor.getOption(
      81
      /* readOnly */
    ) || !this._editor.hasModel() || !this._editor.getOption(
      109
      /* suggestOnTriggerCharacters */
    )) {
      return;
    }
    const supportsByTriggerCharacter = /* @__PURE__ */ new Map();
    for (const support of this._languageFeaturesService.completionProvider.all(this._editor.getModel())) {
      for (const ch of support.triggerCharacters || []) {
        let set = supportsByTriggerCharacter.get(ch);
        if (!set) {
          set = /* @__PURE__ */ new Set();
          set.add(suggest.getSnippetSuggestSupport());
          supportsByTriggerCharacter.set(ch, set);
        }
        set.add(support);
      }
    }
    const checkTriggerCharacter = (text) => {
      if (!canShowSuggestOnTriggerCharacters(this._editor, this._contextKeyService, this._configurationService)) {
        return;
      }
      if (LineContext.shouldAutoTrigger(this._editor)) {
        return;
      }
      if (!text) {
        const position = this._editor.getPosition();
        const model = this._editor.getModel();
        text = model.getLineContent(position.lineNumber).substr(0, position.column - 1);
      }
      let lastChar = "";
      if (strings.isLowSurrogate(text.charCodeAt(text.length - 1))) {
        if (strings.isHighSurrogate(text.charCodeAt(text.length - 2))) {
          lastChar = text.substr(text.length - 2);
        }
      } else {
        lastChar = text.charAt(text.length - 1);
      }
      const supports = supportsByTriggerCharacter.get(lastChar);
      if (supports) {
        const existing = this._completionModel ? { items: this._completionModel.adopt(supports), clipboardText: this._completionModel.clipboardText } : void 0;
        this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, existing);
      }
    };
    this._triggerCharacterListener.add(this._editor.onDidType(checkTriggerCharacter));
    this._triggerCharacterListener.add(this._editor.onDidCompositionEnd(() => checkTriggerCharacter()));
  }
  // --- trigger/retrigger/cancel suggest
  get state() {
    return this._state;
  }
  cancel(retrigger = false) {
    var _a;
    if (this._state !== 0) {
      this._triggerQuickSuggest.cancel();
      (_a = this._requestToken) === null || _a === void 0 ? void 0 : _a.cancel();
      this._requestToken = void 0;
      this._state = 0;
      this._completionModel = void 0;
      this._context = void 0;
      this._onDidCancel.fire({ retrigger });
    }
  }
  clear() {
    this._completionDisposables.clear();
  }
  _updateActiveSuggestSession() {
    if (this._state !== 0) {
      if (!this._editor.hasModel() || !this._languageFeaturesService.completionProvider.has(this._editor.getModel())) {
        this.cancel();
      } else {
        this.trigger({ auto: this._state === 2, shy: false }, true);
      }
    }
  }
  _onCursorChange(e) {
    if (!this._editor.hasModel()) {
      return;
    }
    const prevSelection = this._currentSelection;
    this._currentSelection = this._editor.getSelection();
    if (!e.selection.isEmpty() || e.reason !== 0 && e.reason !== 3 || e.source !== "keyboard" && e.source !== "deleteLeft") {
      this.cancel();
      return;
    }
    if (this._state === 0 && e.reason === 0) {
      if (prevSelection.containsRange(this._currentSelection) || prevSelection.getEndPosition().isBeforeOrEqual(this._currentSelection.getPosition())) {
        this._doTriggerQuickSuggest();
      }
    } else if (this._state !== 0 && e.reason === 3) {
      this._refilterCompletionItems();
    }
  }
  _onCompositionEnd() {
    if (this._state === 0) {
      this._doTriggerQuickSuggest();
    } else {
      this._refilterCompletionItems();
    }
  }
  _doTriggerQuickSuggest() {
    var _a;
    if (this._editor.getOption(
      79
      /* quickSuggestions */
    ) === false) {
      return;
    }
    if (this._editor.getOption(
      106
      /* suggest */
    ).snippetsPreventQuickSuggestions && ((_a = snippetController2.SnippetController2.get(this._editor)) === null || _a === void 0 ? void 0 : _a.isInSnippet())) {
      return;
    }
    this.cancel();
    this._triggerQuickSuggest.cancelAndSet(() => {
      if (this._state !== 0) {
        return;
      }
      if (!LineContext.shouldAutoTrigger(this._editor)) {
        return;
      }
      if (!this._editor.hasModel()) {
        return;
      }
      const model = this._editor.getModel();
      const pos = this._editor.getPosition();
      const quickSuggestions = this._editor.getOption(
        79
        /* quickSuggestions */
      );
      if (quickSuggestions === false) {
        return;
      } else if (quickSuggestions === true)
        ;
      else {
        model.tokenizeIfCheap(pos.lineNumber);
        const lineTokens = model.getLineTokens(pos.lineNumber);
        const tokenType = lineTokens.getStandardTokenType(lineTokens.findTokenIndexAtOffset(Math.max(pos.column - 1 - 1, 0)));
        const inValidScope = quickSuggestions.other && tokenType === 0 || quickSuggestions.comments && tokenType === 1 || quickSuggestions.strings && tokenType === 2;
        if (!inValidScope) {
          return;
        }
      }
      if (!canShowQuickSuggest(this._editor, this._contextKeyService, this._configurationService)) {
        return;
      }
      if (!this._languageFeaturesService.completionProvider.has(model)) {
        return;
      }
      this.trigger({ auto: true, shy: false });
    }, this._quickSuggestDelay);
  }
  _refilterCompletionItems() {
    Promise.resolve().then(() => {
      if (this._state === 0) {
        return;
      }
      if (!this._editor.hasModel()) {
        return;
      }
      const model = this._editor.getModel();
      const position = this._editor.getPosition();
      const ctx = new LineContext(model, position, this._state === 2, false);
      this._onNewContext(ctx);
    });
  }
  trigger(context, retrigger = false, onlyFrom, existing) {
    var _a;
    if (!this._editor.hasModel()) {
      return;
    }
    const model = this._editor.getModel();
    const auto = context.auto;
    const ctx = new LineContext(model, this._editor.getPosition(), auto, context.shy);
    this.cancel(retrigger);
    this._state = auto ? 2 : 1;
    this._onDidTrigger.fire({ auto, shy: context.shy, position: this._editor.getPosition() });
    this._context = ctx;
    let suggestCtx = {
      triggerKind: (_a = context.triggerKind) !== null && _a !== void 0 ? _a : 0
      /* Invoke */
    };
    if (context.triggerCharacter) {
      suggestCtx = {
        triggerKind: 1,
        triggerCharacter: context.triggerCharacter
      };
    }
    this._requestToken = new cancellation.CancellationTokenSource();
    const snippetSuggestions = this._editor.getOption(
      101
      /* snippetSuggestions */
    );
    let snippetSortOrder = 1;
    switch (snippetSuggestions) {
      case "top":
        snippetSortOrder = 0;
        break;
      case "bottom":
        snippetSortOrder = 2;
        break;
    }
    const { itemKind: itemKindFilter, showDeprecated } = SuggestModel._createSuggestFilter(this._editor);
    const wordDistance$1 = wordDistance.WordDistance.create(this._editorWorkerService, this._editor);
    const completions = suggest.provideSuggestionItems(this._languageFeaturesService.completionProvider, model, this._editor.getPosition(), new suggest.CompletionOptions(snippetSortOrder, itemKindFilter, onlyFrom, showDeprecated), suggestCtx, this._requestToken.token);
    Promise.all([completions, wordDistance$1]).then(([completions2, wordDistance2]) => __awaiter(this, void 0, void 0, function* () {
      var _b;
      (_b = this._requestToken) === null || _b === void 0 ? void 0 : _b.dispose();
      if (!this._editor.hasModel()) {
        return;
      }
      let clipboardText = existing === null || existing === void 0 ? void 0 : existing.clipboardText;
      if (!clipboardText && completions2.needsClipboard) {
        clipboardText = yield this._clipboardService.readText();
      }
      if (this._state === 0) {
        return;
      }
      const model2 = this._editor.getModel();
      let items = completions2.items;
      if (existing) {
        const cmpFn = suggest.getSuggestionComparator(snippetSortOrder);
        items = items.concat(existing.items).sort(cmpFn);
      }
      const ctx2 = new LineContext(model2, this._editor.getPosition(), auto, context.shy);
      this._completionModel = new completionModel.CompletionModel(items, this._context.column, {
        leadingLineContent: ctx2.leadingLineContent,
        characterCountDelta: ctx2.column - this._context.column
      }, wordDistance2, this._editor.getOption(
        106
        /* suggest */
      ), this._editor.getOption(
        101
        /* snippetSuggestions */
      ), clipboardText);
      this._completionDisposables.add(completions2.disposable);
      this._onNewContext(ctx2);
      this._reportDurationsTelemetry(completions2.durations);
    })).catch(errors.onUnexpectedError);
  }
  _reportDurationsTelemetry(durations) {
    if (this._telemetryGate++ % 230 !== 0) {
      return;
    }
    setTimeout(() => {
      this._telemetryService.publicLog2("suggest.durations.json", { data: JSON.stringify(durations) });
      this._logService.debug("suggest.durations.json", durations);
    });
  }
  static _createSuggestFilter(editor) {
    const result = /* @__PURE__ */ new Set();
    const snippetSuggestions = editor.getOption(
      101
      /* snippetSuggestions */
    );
    if (snippetSuggestions === "none") {
      result.add(
        27
        /* Snippet */
      );
    }
    const suggestOptions = editor.getOption(
      106
      /* suggest */
    );
    if (!suggestOptions.showMethods) {
      result.add(
        0
        /* Method */
      );
    }
    if (!suggestOptions.showFunctions) {
      result.add(
        1
        /* Function */
      );
    }
    if (!suggestOptions.showConstructors) {
      result.add(
        2
        /* Constructor */
      );
    }
    if (!suggestOptions.showFields) {
      result.add(
        3
        /* Field */
      );
    }
    if (!suggestOptions.showVariables) {
      result.add(
        4
        /* Variable */
      );
    }
    if (!suggestOptions.showClasses) {
      result.add(
        5
        /* Class */
      );
    }
    if (!suggestOptions.showStructs) {
      result.add(
        6
        /* Struct */
      );
    }
    if (!suggestOptions.showInterfaces) {
      result.add(
        7
        /* Interface */
      );
    }
    if (!suggestOptions.showModules) {
      result.add(
        8
        /* Module */
      );
    }
    if (!suggestOptions.showProperties) {
      result.add(
        9
        /* Property */
      );
    }
    if (!suggestOptions.showEvents) {
      result.add(
        10
        /* Event */
      );
    }
    if (!suggestOptions.showOperators) {
      result.add(
        11
        /* Operator */
      );
    }
    if (!suggestOptions.showUnits) {
      result.add(
        12
        /* Unit */
      );
    }
    if (!suggestOptions.showValues) {
      result.add(
        13
        /* Value */
      );
    }
    if (!suggestOptions.showConstants) {
      result.add(
        14
        /* Constant */
      );
    }
    if (!suggestOptions.showEnums) {
      result.add(
        15
        /* Enum */
      );
    }
    if (!suggestOptions.showEnumMembers) {
      result.add(
        16
        /* EnumMember */
      );
    }
    if (!suggestOptions.showKeywords) {
      result.add(
        17
        /* Keyword */
      );
    }
    if (!suggestOptions.showWords) {
      result.add(
        18
        /* Text */
      );
    }
    if (!suggestOptions.showColors) {
      result.add(
        19
        /* Color */
      );
    }
    if (!suggestOptions.showFiles) {
      result.add(
        20
        /* File */
      );
    }
    if (!suggestOptions.showReferences) {
      result.add(
        21
        /* Reference */
      );
    }
    if (!suggestOptions.showColors) {
      result.add(
        22
        /* Customcolor */
      );
    }
    if (!suggestOptions.showFolders) {
      result.add(
        23
        /* Folder */
      );
    }
    if (!suggestOptions.showTypeParameters) {
      result.add(
        24
        /* TypeParameter */
      );
    }
    if (!suggestOptions.showSnippets) {
      result.add(
        27
        /* Snippet */
      );
    }
    if (!suggestOptions.showUsers) {
      result.add(
        25
        /* User */
      );
    }
    if (!suggestOptions.showIssues) {
      result.add(
        26
        /* Issue */
      );
    }
    return { itemKind: result, showDeprecated: suggestOptions.showDeprecated };
  }
  _onNewContext(ctx) {
    if (!this._context) {
      return;
    }
    if (ctx.lineNumber !== this._context.lineNumber) {
      this.cancel();
      return;
    }
    if (strings.getLeadingWhitespace(ctx.leadingLineContent) !== strings.getLeadingWhitespace(this._context.leadingLineContent)) {
      this.cancel();
      return;
    }
    if (ctx.column < this._context.column) {
      if (ctx.leadingWord.word) {
        this.trigger({ auto: this._context.auto, shy: false }, true);
      } else {
        this.cancel();
      }
      return;
    }
    if (!this._completionModel) {
      return;
    }
    if (ctx.leadingWord.word.length !== 0 && ctx.leadingWord.startColumn > this._context.leadingWord.startColumn) {
      const inactiveProvider = new Set(this._languageFeaturesService.completionProvider.all(this._editor.getModel()));
      for (let provider of this._completionModel.allProvider) {
        inactiveProvider.delete(provider);
      }
      const items = this._completionModel.adopt(/* @__PURE__ */ new Set());
      this.trigger({ auto: this._context.auto, shy: false }, true, inactiveProvider, { items, clipboardText: this._completionModel.clipboardText });
      return;
    }
    if (ctx.column > this._context.column && this._completionModel.incomplete.size > 0 && ctx.leadingWord.word.length !== 0) {
      const { incomplete } = this._completionModel;
      const items = this._completionModel.adopt(incomplete);
      this.trigger({
        auto: this._state === 2,
        shy: false,
        triggerKind: 2
        /* TriggerForIncompleteCompletions */
      }, true, incomplete, { items, clipboardText: this._completionModel.clipboardText });
    } else {
      let oldLineContext = this._completionModel.lineContext;
      let isFrozen = false;
      this._completionModel.lineContext = {
        leadingLineContent: ctx.leadingLineContent,
        characterCountDelta: ctx.column - this._context.column
      };
      if (this._completionModel.items.length === 0) {
        if (LineContext.shouldAutoTrigger(this._editor) && this._context.leadingWord.endColumn < ctx.leadingWord.startColumn) {
          this.trigger({ auto: this._context.auto, shy: false }, true);
          return;
        }
        if (!this._context.auto) {
          this._completionModel.lineContext = oldLineContext;
          isFrozen = this._completionModel.items.length > 0;
          if (isFrozen && ctx.leadingWord.word.length === 0) {
            this.cancel();
            return;
          }
        } else {
          this.cancel();
          return;
        }
      }
      this._onDidSuggest.fire({
        completionModel: this._completionModel,
        auto: this._context.auto,
        shy: this._context.shy,
        isFrozen
      });
    }
  }
};
exports.SuggestModel = __decorate([
  __param(1, editorWorker.IEditorWorkerService),
  __param(2, clipboardService.IClipboardService),
  __param(3, telemetry.ITelemetryService),
  __param(4, log.ILogService),
  __param(5, contextkey.IContextKeyService),
  __param(6, configuration.IConfigurationService),
  __param(7, languageFeatures.ILanguageFeaturesService)
], exports.SuggestModel);
exports.LineContext = LineContext;
