import { DeferredPromise } from "../../../../base/common/async.mjs";
import { CancellationTokenSource } from "../../../../base/common/cancellation.mjs";
import { Codicon } from "../../../../base/common/codicons.mjs";
import { pieceToQuery, scoreFuzzy2, prepareQuery } from "../../../../base/common/fuzzyScorer.mjs";
import { Disposable, DisposableStore, toDisposable } from "../../../../base/common/lifecycle.mjs";
import { trim, format } from "../../../../base/common/strings.mjs";
import { Range } from "../../../common/core/range.mjs";
import { SymbolKinds } from "../../../common/languages.mjs";
import { IOutlineModelService } from "../../documentSymbols/browser/outlineModel.mjs";
import { AbstractEditorNavigationQuickAccessProvider } from "./editorNavigationQuickAccess.mjs";
import { localize } from "../../../../nls.mjs";
import { ILanguageFeaturesService } from "../../../common/services/languageFeatures.mjs";
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());
  });
};
let AbstractGotoSymbolQuickAccessProvider = class AbstractGotoSymbolQuickAccessProvider2 extends AbstractEditorNavigationQuickAccessProvider {
  constructor(_languageFeaturesService, _outlineModelService, options = /* @__PURE__ */ Object.create(null)) {
    super(options);
    this._languageFeaturesService = _languageFeaturesService;
    this._outlineModelService = _outlineModelService;
    this.options = options;
    this.options.canAcceptInBackground = true;
  }
  provideWithoutTextEditor(picker) {
    this.provideLabelPick(picker, localize("cannotRunGotoSymbolWithoutEditor", "To go to a symbol, first open a text editor with symbol information."));
    return Disposable.None;
  }
  provideWithTextEditor(context, picker, token) {
    const editor = context.editor;
    const model = this.getModel(editor);
    if (!model) {
      return Disposable.None;
    }
    if (this._languageFeaturesService.documentSymbolProvider.has(model)) {
      return this.doProvideWithEditorSymbols(context, model, picker, token);
    }
    return this.doProvideWithoutEditorSymbols(context, model, picker, token);
  }
  doProvideWithoutEditorSymbols(context, model, picker, token) {
    const disposables = new DisposableStore();
    this.provideLabelPick(picker, localize("cannotRunGotoSymbolWithoutSymbolProvider", "The active text editor does not provide symbol information."));
    (() => __awaiter(this, void 0, void 0, function* () {
      const result = yield this.waitForLanguageSymbolRegistry(model, disposables);
      if (!result || token.isCancellationRequested) {
        return;
      }
      disposables.add(this.doProvideWithEditorSymbols(context, model, picker, token));
    }))();
    return disposables;
  }
  provideLabelPick(picker, label) {
    picker.items = [{
      label,
      index: 0,
      kind: 14
      /* String */
    }];
    picker.ariaLabel = label;
  }
  waitForLanguageSymbolRegistry(model, disposables) {
    return __awaiter(this, void 0, void 0, function* () {
      if (this._languageFeaturesService.documentSymbolProvider.has(model)) {
        return true;
      }
      const symbolProviderRegistryPromise = new DeferredPromise();
      const symbolProviderListener = disposables.add(this._languageFeaturesService.documentSymbolProvider.onDidChange(() => {
        if (this._languageFeaturesService.documentSymbolProvider.has(model)) {
          symbolProviderListener.dispose();
          symbolProviderRegistryPromise.complete(true);
        }
      }));
      disposables.add(toDisposable(() => symbolProviderRegistryPromise.complete(false)));
      return symbolProviderRegistryPromise.p;
    });
  }
  doProvideWithEditorSymbols(context, model, picker, token) {
    const editor = context.editor;
    const disposables = new DisposableStore();
    disposables.add(picker.onDidAccept((event) => {
      const [item] = picker.selectedItems;
      if (item && item.range) {
        this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, preserveFocus: event.inBackground });
        if (!event.inBackground) {
          picker.hide();
        }
      }
    }));
    disposables.add(picker.onDidTriggerItemButton(({ item }) => {
      if (item && item.range) {
        this.gotoLocation(context, { range: item.range.selection, keyMods: picker.keyMods, forceSideBySide: true });
        picker.hide();
      }
    }));
    const symbolsPromise = this.getDocumentSymbols(model, token);
    let picksCts = void 0;
    const updatePickerItems = () => __awaiter(this, void 0, void 0, function* () {
      picksCts === null || picksCts === void 0 ? void 0 : picksCts.dispose(true);
      picker.busy = false;
      picksCts = new CancellationTokenSource(token);
      picker.busy = true;
      try {
        const query = prepareQuery(picker.value.substr(AbstractGotoSymbolQuickAccessProvider2.PREFIX.length).trim());
        const items = yield this.doGetSymbolPicks(symbolsPromise, query, void 0, picksCts.token);
        if (token.isCancellationRequested) {
          return;
        }
        if (items.length > 0) {
          picker.items = items;
        } else {
          if (query.original.length > 0) {
            this.provideLabelPick(picker, localize("noMatchingSymbolResults", "No matching editor symbols"));
          } else {
            this.provideLabelPick(picker, localize("noSymbolResults", "No editor symbols"));
          }
        }
      } finally {
        if (!token.isCancellationRequested) {
          picker.busy = false;
        }
      }
    });
    disposables.add(picker.onDidChangeValue(() => updatePickerItems()));
    updatePickerItems();
    let ignoreFirstActiveEvent = true;
    disposables.add(picker.onDidChangeActive(() => {
      const [item] = picker.activeItems;
      if (item && item.range) {
        if (ignoreFirstActiveEvent) {
          ignoreFirstActiveEvent = false;
          return;
        }
        editor.revealRangeInCenter(
          item.range.selection,
          0
          /* Smooth */
        );
        this.addDecorations(editor, item.range.decoration);
      }
    }));
    return disposables;
  }
  doGetSymbolPicks(symbolsPromise, query, options, token) {
    return __awaiter(this, void 0, void 0, function* () {
      const symbols = yield symbolsPromise;
      if (token.isCancellationRequested) {
        return [];
      }
      const filterBySymbolKind = query.original.indexOf(AbstractGotoSymbolQuickAccessProvider2.SCOPE_PREFIX) === 0;
      const filterPos = filterBySymbolKind ? 1 : 0;
      let symbolQuery;
      let containerQuery;
      if (query.values && query.values.length > 1) {
        symbolQuery = pieceToQuery(query.values[0]);
        containerQuery = pieceToQuery(query.values.slice(1));
      } else {
        symbolQuery = query;
      }
      const filteredSymbolPicks = [];
      for (let index = 0; index < symbols.length; index++) {
        const symbol = symbols[index];
        const symbolLabel = trim(symbol.name);
        const symbolLabelWithIcon = `$(${SymbolKinds.toIcon(symbol.kind).id}) ${symbolLabel}`;
        const symbolLabelIconOffset = symbolLabelWithIcon.length - symbolLabel.length;
        let containerLabel = symbol.containerName;
        if (options === null || options === void 0 ? void 0 : options.extraContainerLabel) {
          if (containerLabel) {
            containerLabel = `${options.extraContainerLabel} • ${containerLabel}`;
          } else {
            containerLabel = options.extraContainerLabel;
          }
        }
        let symbolScore = void 0;
        let symbolMatches = void 0;
        let containerScore = void 0;
        let containerMatches = void 0;
        if (query.original.length > filterPos) {
          let skipContainerQuery = false;
          if (symbolQuery !== query) {
            [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, Object.assign(Object.assign({}, query), {
              values: void 0
              /* disable multi-query support */
            }), filterPos, symbolLabelIconOffset);
            if (typeof symbolScore === "number") {
              skipContainerQuery = true;
            }
          }
          if (typeof symbolScore !== "number") {
            [symbolScore, symbolMatches] = scoreFuzzy2(symbolLabelWithIcon, symbolQuery, filterPos, symbolLabelIconOffset);
            if (typeof symbolScore !== "number") {
              continue;
            }
          }
          if (!skipContainerQuery && containerQuery) {
            if (containerLabel && containerQuery.original.length > 0) {
              [containerScore, containerMatches] = scoreFuzzy2(containerLabel, containerQuery);
            }
            if (typeof containerScore !== "number") {
              continue;
            }
            if (typeof symbolScore === "number") {
              symbolScore += containerScore;
            }
          }
        }
        const deprecated = symbol.tags && symbol.tags.indexOf(
          1
          /* Deprecated */
        ) >= 0;
        filteredSymbolPicks.push({
          index,
          kind: symbol.kind,
          score: symbolScore,
          label: symbolLabelWithIcon,
          ariaLabel: symbolLabel,
          description: containerLabel,
          highlights: deprecated ? void 0 : {
            label: symbolMatches,
            description: containerMatches
          },
          range: {
            selection: Range.collapseToStart(symbol.selectionRange),
            decoration: symbol.range
          },
          strikethrough: deprecated,
          buttons: (() => {
            var _a, _b;
            const openSideBySideDirection = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.openSideBySideDirection) ? (_b = this.options) === null || _b === void 0 ? void 0 : _b.openSideBySideDirection() : void 0;
            if (!openSideBySideDirection) {
              return void 0;
            }
            return [
              {
                iconClass: openSideBySideDirection === "right" ? Codicon.splitHorizontal.classNames : Codicon.splitVertical.classNames,
                tooltip: openSideBySideDirection === "right" ? localize("openToSide", "Open to the Side") : localize("openToBottom", "Open to the Bottom")
              }
            ];
          })()
        });
      }
      const sortedFilteredSymbolPicks = filteredSymbolPicks.sort((symbolA, symbolB) => filterBySymbolKind ? this.compareByKindAndScore(symbolA, symbolB) : this.compareByScore(symbolA, symbolB));
      let symbolPicks = [];
      if (filterBySymbolKind) {
        let updateLastSeparatorLabel = function() {
          if (lastSeparator && typeof lastSymbolKind === "number" && lastSymbolKindCounter > 0) {
            lastSeparator.label = format(NLS_SYMBOL_KIND_CACHE[lastSymbolKind] || FALLBACK_NLS_SYMBOL_KIND, lastSymbolKindCounter);
          }
        };
        let lastSymbolKind = void 0;
        let lastSeparator = void 0;
        let lastSymbolKindCounter = 0;
        for (const symbolPick of sortedFilteredSymbolPicks) {
          if (lastSymbolKind !== symbolPick.kind) {
            updateLastSeparatorLabel();
            lastSymbolKind = symbolPick.kind;
            lastSymbolKindCounter = 1;
            lastSeparator = { type: "separator" };
            symbolPicks.push(lastSeparator);
          } else {
            lastSymbolKindCounter++;
          }
          symbolPicks.push(symbolPick);
        }
        updateLastSeparatorLabel();
      } else if (sortedFilteredSymbolPicks.length > 0) {
        symbolPicks = [
          { label: localize("symbols", "symbols ({0})", filteredSymbolPicks.length), type: "separator" },
          ...sortedFilteredSymbolPicks
        ];
      }
      return symbolPicks;
    });
  }
  compareByScore(symbolA, symbolB) {
    if (typeof symbolA.score !== "number" && typeof symbolB.score === "number") {
      return 1;
    } else if (typeof symbolA.score === "number" && typeof symbolB.score !== "number") {
      return -1;
    }
    if (typeof symbolA.score === "number" && typeof symbolB.score === "number") {
      if (symbolA.score > symbolB.score) {
        return -1;
      } else if (symbolA.score < symbolB.score) {
        return 1;
      }
    }
    if (symbolA.index < symbolB.index) {
      return -1;
    } else if (symbolA.index > symbolB.index) {
      return 1;
    }
    return 0;
  }
  compareByKindAndScore(symbolA, symbolB) {
    const kindA = NLS_SYMBOL_KIND_CACHE[symbolA.kind] || FALLBACK_NLS_SYMBOL_KIND;
    const kindB = NLS_SYMBOL_KIND_CACHE[symbolB.kind] || FALLBACK_NLS_SYMBOL_KIND;
    const result = kindA.localeCompare(kindB);
    if (result === 0) {
      return this.compareByScore(symbolA, symbolB);
    }
    return result;
  }
  getDocumentSymbols(document, token) {
    return __awaiter(this, void 0, void 0, function* () {
      const model = yield this._outlineModelService.getOrCreate(document, token);
      return token.isCancellationRequested ? [] : model.asListOfDocumentSymbols();
    });
  }
};
AbstractGotoSymbolQuickAccessProvider.PREFIX = "@";
AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX = ":";
AbstractGotoSymbolQuickAccessProvider.PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`;
AbstractGotoSymbolQuickAccessProvider = __decorate([
  __param(0, ILanguageFeaturesService),
  __param(1, IOutlineModelService)
], AbstractGotoSymbolQuickAccessProvider);
const FALLBACK_NLS_SYMBOL_KIND = localize("property", "properties ({0})");
const NLS_SYMBOL_KIND_CACHE = {
  [
    5
    /* Method */
  ]: localize("method", "methods ({0})"),
  [
    11
    /* Function */
  ]: localize("function", "functions ({0})"),
  [
    8
    /* Constructor */
  ]: localize("_constructor", "constructors ({0})"),
  [
    12
    /* Variable */
  ]: localize("variable", "variables ({0})"),
  [
    4
    /* Class */
  ]: localize("class", "classes ({0})"),
  [
    22
    /* Struct */
  ]: localize("struct", "structs ({0})"),
  [
    23
    /* Event */
  ]: localize("event", "events ({0})"),
  [
    24
    /* Operator */
  ]: localize("operator", "operators ({0})"),
  [
    10
    /* Interface */
  ]: localize("interface", "interfaces ({0})"),
  [
    2
    /* Namespace */
  ]: localize("namespace", "namespaces ({0})"),
  [
    3
    /* Package */
  ]: localize("package", "packages ({0})"),
  [
    25
    /* TypeParameter */
  ]: localize("typeParameter", "type parameters ({0})"),
  [
    1
    /* Module */
  ]: localize("modules", "modules ({0})"),
  [
    6
    /* Property */
  ]: localize("property", "properties ({0})"),
  [
    9
    /* Enum */
  ]: localize("enum", "enumerations ({0})"),
  [
    21
    /* EnumMember */
  ]: localize("enumMember", "enumeration members ({0})"),
  [
    14
    /* String */
  ]: localize("string", "strings ({0})"),
  [
    0
    /* File */
  ]: localize("file", "files ({0})"),
  [
    17
    /* Array */
  ]: localize("array", "arrays ({0})"),
  [
    15
    /* Number */
  ]: localize("number", "numbers ({0})"),
  [
    16
    /* Boolean */
  ]: localize("boolean", "booleans ({0})"),
  [
    18
    /* Object */
  ]: localize("object", "objects ({0})"),
  [
    19
    /* Key */
  ]: localize("key", "keys ({0})"),
  [
    7
    /* Field */
  ]: localize("field", "fields ({0})"),
  [
    13
    /* Constant */
  ]: localize("constant", "constants ({0})")
};
export {
  AbstractGotoSymbolQuickAccessProvider
};
