"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 lifecycle = require("../../../../base/common/lifecycle.js");
const types = require("../../../../base/common/types.js");
const uri = require("../../../../base/common/uri.js");
const range = require("../../../common/core/range.js");
const model = require("../../../common/services/model.js");
const commands = require("../../../../platform/commands/common/commands.js");
const languageFeatures = require("../../../common/services/languageFeatures.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());
  });
};
class Link {
  constructor(link, provider) {
    this._link = link;
    this._provider = provider;
  }
  toJSON() {
    return {
      range: this.range,
      url: this.url,
      tooltip: this.tooltip
    };
  }
  get range() {
    return this._link.range;
  }
  get url() {
    return this._link.url;
  }
  get tooltip() {
    return this._link.tooltip;
  }
  resolve(token) {
    return __awaiter(this, void 0, void 0, function* () {
      if (this._link.url) {
        return this._link.url;
      }
      if (typeof this._provider.resolveLink === "function") {
        return Promise.resolve(this._provider.resolveLink(this._link, token)).then((value) => {
          this._link = value || this._link;
          if (this._link.url) {
            return this.resolve(token);
          }
          return Promise.reject(new Error("missing"));
        });
      }
      return Promise.reject(new Error("missing"));
    });
  }
}
class LinksList {
  constructor(tuples) {
    this._disposables = new lifecycle.DisposableStore();
    let links = [];
    for (const [list, provider] of tuples) {
      const newLinks = list.links.map((link) => new Link(link, provider));
      links = LinksList._union(links, newLinks);
      if (lifecycle.isDisposable(list)) {
        this._disposables.add(list);
      }
    }
    this.links = links;
  }
  dispose() {
    this._disposables.dispose();
    this.links.length = 0;
  }
  static _union(oldLinks, newLinks) {
    let result = [];
    let oldIndex;
    let oldLen;
    let newIndex;
    let newLen;
    for (oldIndex = 0, newIndex = 0, oldLen = oldLinks.length, newLen = newLinks.length; oldIndex < oldLen && newIndex < newLen; ) {
      const oldLink = oldLinks[oldIndex];
      const newLink = newLinks[newIndex];
      if (range.Range.areIntersectingOrTouching(oldLink.range, newLink.range)) {
        oldIndex++;
        continue;
      }
      const comparisonResult = range.Range.compareRangesUsingStarts(oldLink.range, newLink.range);
      if (comparisonResult < 0) {
        result.push(oldLink);
        oldIndex++;
      } else {
        result.push(newLink);
        newIndex++;
      }
    }
    for (; oldIndex < oldLen; oldIndex++) {
      result.push(oldLinks[oldIndex]);
    }
    for (; newIndex < newLen; newIndex++) {
      result.push(newLinks[newIndex]);
    }
    return result;
  }
}
function getLinks(providers, model2, token) {
  const lists = [];
  const promises = providers.ordered(model2).reverse().map((provider, i) => {
    return Promise.resolve(provider.provideLinks(model2, token)).then((result) => {
      if (result) {
        lists[i] = [result, provider];
      }
    }, errors.onUnexpectedExternalError);
  });
  return Promise.all(promises).then(() => {
    const result = new LinksList(arrays.coalesce(lists));
    if (!token.isCancellationRequested) {
      return result;
    }
    result.dispose();
    return new LinksList([]);
  });
}
commands.CommandsRegistry.registerCommand("_executeLinkProvider", (accessor, ...args) => __awaiter(void 0, void 0, void 0, function* () {
  let [uri$1, resolveCount] = args;
  types.assertType(uri$1 instanceof uri.URI);
  if (typeof resolveCount !== "number") {
    resolveCount = 0;
  }
  const { linkProvider } = accessor.get(languageFeatures.ILanguageFeaturesService);
  const model$1 = accessor.get(model.IModelService).getModel(uri$1);
  if (!model$1) {
    return [];
  }
  const list = yield getLinks(linkProvider, model$1, cancellation.CancellationToken.None);
  if (!list) {
    return [];
  }
  for (let i = 0; i < Math.min(resolveCount, list.links.length); i++) {
    yield list.links[i].resolve(cancellation.CancellationToken.None);
  }
  const result = list.links.slice(0);
  list.dispose();
  return result;
}));
exports.Link = Link;
exports.LinksList = LinksList;
exports.getLinks = getLinks;
