"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const filters = require("../../../../base/common/filters.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const stopwatch = require("../../../../base/common/stopwatch.js");
const types = require("../../../../base/common/types.js");
const uri = require("../../../../base/common/uri.js");
const position = require("../../../common/core/position.js");
const range = require("../../../common/core/range.js");
const resolverService = require("../../../common/services/resolverService.js");
const snippetParser = require("../../snippet/browser/snippetParser.js");
const nls = require("../../../../nls.js");
const actions = require("../../../../platform/actions/common/actions.js");
const commands = require("../../../../platform/commands/common/commands.js");
const contextkey = require("../../../../platform/contextkey/common/contextkey.js");
const languageFeatures = require("../../../common/services/languageFeatures.js");
const contextScopedHistoryWidget = require("../../../../platform/history/browser/contextScopedHistoryWidget.js");
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());
  });
};
const Context = {
  Visible: contextScopedHistoryWidget.historyNavigationVisible,
  DetailsVisible: new contextkey.RawContextKey("suggestWidgetDetailsVisible", false, nls.localize("suggestWidgetDetailsVisible", "Whether suggestion details are visible")),
  MultipleSuggestions: new contextkey.RawContextKey("suggestWidgetMultipleSuggestions", false, nls.localize("suggestWidgetMultipleSuggestions", "Whether there are multiple suggestions to pick from")),
  MakesTextEdit: new contextkey.RawContextKey("suggestionMakesTextEdit", true, nls.localize("suggestionMakesTextEdit", "Whether inserting the current suggestion yields in a change or has everything already been typed")),
  AcceptSuggestionsOnEnter: new contextkey.RawContextKey("acceptSuggestionOnEnter", true, nls.localize("acceptSuggestionOnEnter", "Whether suggestions are inserted when pressing Enter")),
  HasInsertAndReplaceRange: new contextkey.RawContextKey("suggestionHasInsertAndReplaceRange", false, nls.localize("suggestionHasInsertAndReplaceRange", "Whether the current suggestion has insert and replace behaviour")),
  InsertMode: new contextkey.RawContextKey("suggestionInsertMode", void 0, { type: "string", description: nls.localize("suggestionInsertMode", "Whether the default behaviour is to insert or replace") }),
  CanResolve: new contextkey.RawContextKey("suggestionCanResolve", false, nls.localize("suggestionCanResolve", "Whether the current suggestion supports to resolve further details"))
};
const suggestWidgetStatusbarMenu = new actions.MenuId("suggestWidgetStatusBar");
class CompletionItem {
  constructor(position$1, completion, container, provider) {
    this.position = position$1;
    this.completion = completion;
    this.container = container;
    this.provider = provider;
    this.isInvalid = false;
    this.score = filters.FuzzyScore.Default;
    this.distance = 0;
    this.textLabel = typeof completion.label === "string" ? completion.label : completion.label.label;
    this.labelLow = this.textLabel.toLowerCase();
    this.isInvalid = !this.textLabel;
    this.sortTextLow = completion.sortText && completion.sortText.toLowerCase();
    this.filterTextLow = completion.filterText && completion.filterText.toLowerCase();
    if (range.Range.isIRange(completion.range)) {
      this.editStart = new position.Position(completion.range.startLineNumber, completion.range.startColumn);
      this.editInsertEnd = new position.Position(completion.range.endLineNumber, completion.range.endColumn);
      this.editReplaceEnd = new position.Position(completion.range.endLineNumber, completion.range.endColumn);
      this.isInvalid = this.isInvalid || range.Range.spansMultipleLines(completion.range) || completion.range.startLineNumber !== position$1.lineNumber;
    } else {
      this.editStart = new position.Position(completion.range.insert.startLineNumber, completion.range.insert.startColumn);
      this.editInsertEnd = new position.Position(completion.range.insert.endLineNumber, completion.range.insert.endColumn);
      this.editReplaceEnd = new position.Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn);
      this.isInvalid = this.isInvalid || range.Range.spansMultipleLines(completion.range.insert) || range.Range.spansMultipleLines(completion.range.replace) || completion.range.insert.startLineNumber !== position$1.lineNumber || completion.range.replace.startLineNumber !== position$1.lineNumber || completion.range.insert.startColumn !== completion.range.replace.startColumn;
    }
    if (typeof provider.resolveCompletionItem !== "function") {
      this._resolveCache = Promise.resolve();
      this._isResolved = true;
    }
  }
  // ---- resolving
  get isResolved() {
    return !!this._isResolved;
  }
  resolve(token) {
    return __awaiter(this, void 0, void 0, function* () {
      if (!this._resolveCache) {
        const sub = token.onCancellationRequested(() => {
          this._resolveCache = void 0;
          this._isResolved = false;
        });
        this._resolveCache = Promise.resolve(this.provider.resolveCompletionItem(this.completion, token)).then((value) => {
          Object.assign(this.completion, value);
          this._isResolved = true;
          sub.dispose();
        }, (err) => {
          if (errors.isCancellationError(err)) {
            this._resolveCache = void 0;
            this._isResolved = false;
          }
        });
      }
      return this._resolveCache;
    });
  }
}
class CompletionOptions {
  constructor(snippetSortOrder = 2, kindFilter = /* @__PURE__ */ new Set(), providerFilter = /* @__PURE__ */ new Set(), showDeprecated = true) {
    this.snippetSortOrder = snippetSortOrder;
    this.kindFilter = kindFilter;
    this.providerFilter = providerFilter;
    this.showDeprecated = showDeprecated;
  }
}
CompletionOptions.default = new CompletionOptions();
let _snippetSuggestSupport;
function getSnippetSuggestSupport() {
  return _snippetSuggestSupport;
}
class CompletionItemModel {
  constructor(items, needsClipboard, durations, disposable) {
    this.items = items;
    this.needsClipboard = needsClipboard;
    this.durations = durations;
    this.disposable = disposable;
  }
}
function provideSuggestionItems(registry, model, position2, options = CompletionOptions.default, context = {
  triggerKind: 0
  /* Invoke */
}, token = cancellation.CancellationToken.None) {
  return __awaiter(this, void 0, void 0, function* () {
    const sw = new stopwatch.StopWatch(true);
    position2 = position2.clone();
    const word = model.getWordAtPosition(position2);
    const defaultReplaceRange = word ? new range.Range(position2.lineNumber, word.startColumn, position2.lineNumber, word.endColumn) : range.Range.fromPositions(position2);
    const defaultRange = { replace: defaultReplaceRange, insert: defaultReplaceRange.setEndPosition(position2.lineNumber, position2.column) };
    const result = [];
    const disposables = new lifecycle.DisposableStore();
    const durations = [];
    let needsClipboard = false;
    const onCompletionList = (provider, container, sw2) => {
      var _a, _b, _c;
      let didAddResult = false;
      if (!container) {
        return didAddResult;
      }
      for (let suggestion of container.suggestions) {
        if (!options.kindFilter.has(suggestion.kind)) {
          if (!options.showDeprecated && ((_a = suggestion === null || suggestion === void 0 ? void 0 : suggestion.tags) === null || _a === void 0 ? void 0 : _a.includes(
            1
            /* Deprecated */
          ))) {
            continue;
          }
          if (!suggestion.range) {
            suggestion.range = defaultRange;
          }
          if (!suggestion.sortText) {
            suggestion.sortText = typeof suggestion.label === "string" ? suggestion.label : suggestion.label.label;
          }
          if (!needsClipboard && suggestion.insertTextRules && suggestion.insertTextRules & 4) {
            needsClipboard = snippetParser.SnippetParser.guessNeedsClipboard(suggestion.insertText);
          }
          result.push(new CompletionItem(position2, suggestion, container, provider));
          didAddResult = true;
        }
      }
      if (lifecycle.isDisposable(container)) {
        disposables.add(container);
      }
      durations.push({
        providerName: (_b = provider._debugDisplayName) !== null && _b !== void 0 ? _b : "unknown_provider",
        elapsedProvider: (_c = container.duration) !== null && _c !== void 0 ? _c : -1,
        elapsedOverall: sw2.elapsed()
      });
      return didAddResult;
    };
    const snippetCompletions = (() => __awaiter(this, void 0, void 0, function* () {
      {
        return;
      }
    }))();
    for (let providerGroup of registry.orderedGroups(model)) {
      let didAddResult = false;
      yield Promise.all(providerGroup.map((provider) => __awaiter(this, void 0, void 0, function* () {
        if (options.providerFilter.size > 0 && !options.providerFilter.has(provider)) {
          return;
        }
        try {
          const sw2 = new stopwatch.StopWatch(true);
          const list = yield provider.provideCompletionItems(model, position2, context, token);
          didAddResult = onCompletionList(provider, list, sw2) || didAddResult;
        } catch (err) {
          errors.onUnexpectedExternalError(err);
        }
      })));
      if (didAddResult || token.isCancellationRequested) {
        break;
      }
    }
    yield snippetCompletions;
    if (token.isCancellationRequested) {
      disposables.dispose();
      return Promise.reject(errors.canceled());
    }
    return new CompletionItemModel(result.sort(getSuggestionComparator(options.snippetSortOrder)), needsClipboard, { entries: durations, elapsed: sw.elapsed() }, disposables);
  });
}
function defaultComparator(a, b) {
  if (a.sortTextLow && b.sortTextLow) {
    if (a.sortTextLow < b.sortTextLow) {
      return -1;
    } else if (a.sortTextLow > b.sortTextLow) {
      return 1;
    }
  }
  if (a.completion.label < b.completion.label) {
    return -1;
  } else if (a.completion.label > b.completion.label) {
    return 1;
  }
  return a.completion.kind - b.completion.kind;
}
function snippetUpComparator(a, b) {
  if (a.completion.kind !== b.completion.kind) {
    if (a.completion.kind === 27) {
      return -1;
    } else if (b.completion.kind === 27) {
      return 1;
    }
  }
  return defaultComparator(a, b);
}
function snippetDownComparator(a, b) {
  if (a.completion.kind !== b.completion.kind) {
    if (a.completion.kind === 27) {
      return 1;
    } else if (b.completion.kind === 27) {
      return -1;
    }
  }
  return defaultComparator(a, b);
}
const _snippetComparators = /* @__PURE__ */ new Map();
_snippetComparators.set(0, snippetUpComparator);
_snippetComparators.set(2, snippetDownComparator);
_snippetComparators.set(1, defaultComparator);
function getSuggestionComparator(snippetConfig) {
  return _snippetComparators.get(snippetConfig);
}
commands.CommandsRegistry.registerCommand("_executeCompletionItemProvider", (accessor, ...args) => __awaiter(void 0, void 0, void 0, function* () {
  const [uri$1, position$1, triggerCharacter, maxItemsToResolve] = args;
  types.assertType(uri.URI.isUri(uri$1));
  types.assertType(position.Position.isIPosition(position$1));
  types.assertType(typeof triggerCharacter === "string" || !triggerCharacter);
  types.assertType(typeof maxItemsToResolve === "number" || !maxItemsToResolve);
  const { completionProvider } = accessor.get(languageFeatures.ILanguageFeaturesService);
  const ref = yield accessor.get(resolverService.ITextModelService).createModelReference(uri$1);
  try {
    const result = {
      incomplete: false,
      suggestions: []
    };
    const resolving = [];
    const completions = yield provideSuggestionItems(completionProvider, ref.object.textEditorModel, position.Position.lift(position$1), void 0, {
      triggerCharacter,
      triggerKind: triggerCharacter ? 1 : 0
      /* Invoke */
    });
    for (const item of completions.items) {
      if (resolving.length < (maxItemsToResolve !== null && maxItemsToResolve !== void 0 ? maxItemsToResolve : 0)) {
        resolving.push(item.resolve(cancellation.CancellationToken.None));
      }
      result.incomplete = result.incomplete || item.container.incomplete;
      result.suggestions.push(item.completion);
    }
    try {
      yield Promise.all(resolving);
      return result;
    } finally {
      setTimeout(() => completions.disposable.dispose(), 100);
    }
  } finally {
    ref.dispose();
  }
}));
let _onlyOnceProvider;
let _onlyOnceSuggestions = [];
function showSimpleSuggestions(accessor, editor, suggestions) {
  const { completionProvider } = accessor.get(languageFeatures.ILanguageFeaturesService);
  if (!_onlyOnceProvider) {
    _onlyOnceProvider = new class {
      provideCompletionItems() {
        let suggestions2 = _onlyOnceSuggestions.slice(0);
        let result = { suggestions: suggestions2 };
        _onlyOnceSuggestions.length = 0;
        return result;
      }
    }();
    completionProvider.register("*", _onlyOnceProvider);
  }
  setTimeout(() => {
    var _a;
    _onlyOnceSuggestions.push(...suggestions);
    (_a = editor.getContribution("editor.contrib.suggestController")) === null || _a === void 0 ? void 0 : _a.triggerSuggest((/* @__PURE__ */ new Set()).add(_onlyOnceProvider));
  }, 0);
}
exports.CompletionItem = CompletionItem;
exports.CompletionItemModel = CompletionItemModel;
exports.CompletionOptions = CompletionOptions;
exports.Context = Context;
exports.getSnippetSuggestSupport = getSnippetSuggestSupport;
exports.getSuggestionComparator = getSuggestionComparator;
exports.provideSuggestionItems = provideSuggestionItems;
exports.showSimpleSuggestions = showSimpleSuggestions;
exports.suggestWidgetStatusbarMenu = suggestWidgetStatusbarMenu;
