"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const arrays = require("../../../../base/common/arrays.js");
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const iterator = require("../../../../base/common/iterator.js");
const map = require("../../../../base/common/map.js");
const position = require("../../../common/core/position.js");
const range = require("../../../common/core/range.js");
const languageFeatureDebounce = require("../../../common/services/languageFeatureDebounce.js");
const instantiation = require("../../../../platform/instantiation/common/instantiation.js");
const extensions = require("../../../../platform/instantiation/common/extensions.js");
const model = require("../../../common/services/model.js");
const lifecycle = require("../../../../base/common/lifecycle.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 TreeElement {
  remove() {
    if (this.parent) {
      this.parent.children.delete(this.id);
    }
  }
  static findId(candidate, container) {
    let candidateId;
    if (typeof candidate === "string") {
      candidateId = `${container.id}/${candidate}`;
    } else {
      candidateId = `${container.id}/${candidate.name}`;
      if (container.children.get(candidateId) !== void 0) {
        candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`;
      }
    }
    let id = candidateId;
    for (let i = 0; container.children.get(id) !== void 0; i++) {
      id = `${candidateId}_${i}`;
    }
    return id;
  }
  static empty(element) {
    return element.children.size === 0;
  }
}
class OutlineElement extends TreeElement {
  constructor(id, parent, symbol) {
    super();
    this.id = id;
    this.parent = parent;
    this.symbol = symbol;
    this.children = /* @__PURE__ */ new Map();
  }
}
class OutlineGroup extends TreeElement {
  constructor(id, parent, label, order) {
    super();
    this.id = id;
    this.parent = parent;
    this.label = label;
    this.order = order;
    this.children = /* @__PURE__ */ new Map();
  }
}
class OutlineModel extends TreeElement {
  constructor(uri) {
    super();
    this.uri = uri;
    this.id = "root";
    this.parent = void 0;
    this._groups = /* @__PURE__ */ new Map();
    this.children = /* @__PURE__ */ new Map();
    this.id = "root";
    this.parent = void 0;
  }
  static create(registry, textModel, token) {
    const cts = new cancellation.CancellationTokenSource(token);
    const result = new OutlineModel(textModel.uri);
    const provider = registry.ordered(textModel);
    const promises = provider.map((provider2, index) => {
      var _a;
      let id = TreeElement.findId(`provider_${index}`, result);
      let group = new OutlineGroup(id, result, (_a = provider2.displayName) !== null && _a !== void 0 ? _a : "Unknown Outline Provider", index);
      return Promise.resolve(provider2.provideDocumentSymbols(textModel, cts.token)).then((result2) => {
        for (const info of result2 || []) {
          OutlineModel._makeOutlineElement(info, group);
        }
        return group;
      }, (err) => {
        errors.onUnexpectedExternalError(err);
        return group;
      }).then((group2) => {
        if (!TreeElement.empty(group2)) {
          result._groups.set(id, group2);
        } else {
          group2.remove();
        }
      });
    });
    const listener = registry.onDidChange(() => {
      const newProvider = registry.ordered(textModel);
      if (!arrays.equals(newProvider, provider)) {
        cts.cancel();
      }
    });
    return Promise.all(promises).then(() => {
      if (cts.token.isCancellationRequested && !token.isCancellationRequested) {
        return OutlineModel.create(registry, textModel, token);
      } else {
        return result._compact();
      }
    }).finally(() => {
      listener.dispose();
    });
  }
  static _makeOutlineElement(info, container) {
    let id = TreeElement.findId(info, container);
    let res = new OutlineElement(id, container, info);
    if (info.children) {
      for (const childInfo of info.children) {
        OutlineModel._makeOutlineElement(childInfo, res);
      }
    }
    container.children.set(res.id, res);
  }
  _compact() {
    let count = 0;
    for (const [key, group] of this._groups) {
      if (group.children.size === 0) {
        this._groups.delete(key);
      } else {
        count += 1;
      }
    }
    if (count !== 1) {
      this.children = this._groups;
    } else {
      let group = iterator.Iterable.first(this._groups.values());
      for (let [, child] of group.children) {
        child.parent = this;
        this.children.set(child.id, child);
      }
    }
    return this;
  }
  getTopLevelSymbols() {
    const roots = [];
    for (const child of this.children.values()) {
      if (child instanceof OutlineElement) {
        roots.push(child.symbol);
      } else {
        roots.push(...iterator.Iterable.map(child.children.values(), (child2) => child2.symbol));
      }
    }
    return roots.sort((a, b) => range.Range.compareRangesUsingStarts(a.range, b.range));
  }
  asListOfDocumentSymbols() {
    const roots = this.getTopLevelSymbols();
    const bucket = [];
    OutlineModel._flattenDocumentSymbols(bucket, roots, "");
    return bucket.sort((a, b) => position.Position.compare(range.Range.getStartPosition(a.range), range.Range.getStartPosition(b.range)) || position.Position.compare(range.Range.getEndPosition(b.range), range.Range.getEndPosition(a.range)));
  }
  static _flattenDocumentSymbols(bucket, entries, overrideContainerLabel) {
    for (const entry of entries) {
      bucket.push({
        kind: entry.kind,
        tags: entry.tags,
        name: entry.name,
        detail: entry.detail,
        containerName: entry.containerName || overrideContainerLabel,
        range: entry.range,
        selectionRange: entry.selectionRange,
        children: void 0
        // we flatten it...
      });
      if (entry.children) {
        OutlineModel._flattenDocumentSymbols(bucket, entry.children, entry.name);
      }
    }
  }
}
const IOutlineModelService = instantiation.createDecorator("IOutlineModelService");
exports.OutlineModelService = class OutlineModelService {
  constructor(_languageFeaturesService, debounces, modelService) {
    this._languageFeaturesService = _languageFeaturesService;
    this._disposables = new lifecycle.DisposableStore();
    this._cache = new map.LRUCache(10, 0.7);
    this._debounceInformation = debounces.for(_languageFeaturesService.documentSymbolProvider, "DocumentSymbols", { min: 350 });
    this._disposables.add(modelService.onModelRemoved((textModel) => {
      this._cache.delete(textModel.id);
    }));
  }
  dispose() {
    this._disposables.dispose();
  }
  getOrCreate(textModel, token) {
    return __awaiter(this, void 0, void 0, function* () {
      const registry = this._languageFeaturesService.documentSymbolProvider;
      const provider = registry.ordered(textModel);
      let data = this._cache.get(textModel.id);
      if (!data || data.versionId !== textModel.getVersionId() || !arrays.equals(data.provider, provider)) {
        let source = new cancellation.CancellationTokenSource();
        data = {
          versionId: textModel.getVersionId(),
          provider,
          promiseCnt: 0,
          source,
          promise: OutlineModel.create(registry, textModel, source.token),
          model: void 0
        };
        this._cache.set(textModel.id, data);
        const now = Date.now();
        data.promise.then((outlineModel) => {
          data.model = outlineModel;
          this._debounceInformation.update(textModel, Date.now() - now);
        }).catch((_err) => {
          this._cache.delete(textModel.id);
        });
      }
      if (data.model) {
        return data.model;
      }
      data.promiseCnt += 1;
      const listener = token.onCancellationRequested(() => {
        if (--data.promiseCnt === 0) {
          data.source.cancel();
          this._cache.delete(textModel.id);
        }
      });
      try {
        return yield data.promise;
      } finally {
        listener.dispose();
      }
    });
  }
};
exports.OutlineModelService = __decorate([
  __param(0, languageFeatures.ILanguageFeaturesService),
  __param(1, languageFeatureDebounce.ILanguageFeatureDebounceService),
  __param(2, model.IModelService)
], exports.OutlineModelService);
extensions.registerSingleton(IOutlineModelService, exports.OutlineModelService, true);
exports.IOutlineModelService = IOutlineModelService;
exports.OutlineElement = OutlineElement;
exports.OutlineGroup = OutlineGroup;
exports.OutlineModel = OutlineModel;
exports.TreeElement = TreeElement;
