"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const editorExtensions = require("../../../browser/editorExtensions.js");
const referencesModel = require("./referencesModel.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());
  });
};
function getLocationLinks(model, position, registry, provide) {
  const provider = registry.ordered(model);
  const promises = provider.map((provider2) => {
    return Promise.resolve(provide(provider2, model, position)).then(void 0, (err) => {
      errors.onUnexpectedExternalError(err);
      return void 0;
    });
  });
  return Promise.all(promises).then((values) => {
    const result = [];
    for (let value of values) {
      if (Array.isArray(value)) {
        result.push(...value);
      } else if (value) {
        result.push(value);
      }
    }
    return result;
  });
}
function getDefinitionsAtPosition(registry, model, position, token) {
  return getLocationLinks(model, position, registry, (provider, model2, position2) => {
    return provider.provideDefinition(model2, position2, token);
  });
}
function getDeclarationsAtPosition(registry, model, position, token) {
  return getLocationLinks(model, position, registry, (provider, model2, position2) => {
    return provider.provideDeclaration(model2, position2, token);
  });
}
function getImplementationsAtPosition(registry, model, position, token) {
  return getLocationLinks(model, position, registry, (provider, model2, position2) => {
    return provider.provideImplementation(model2, position2, token);
  });
}
function getTypeDefinitionsAtPosition(registry, model, position, token) {
  return getLocationLinks(model, position, registry, (provider, model2, position2) => {
    return provider.provideTypeDefinition(model2, position2, token);
  });
}
function getReferencesAtPosition(registry, model, position, compact, token) {
  return getLocationLinks(model, position, registry, (provider, model2, position2) => __awaiter(this, void 0, void 0, function* () {
    const result = yield provider.provideReferences(model2, position2, { includeDeclaration: true }, token);
    if (!compact || !result || result.length !== 2) {
      return result;
    }
    const resultWithoutDeclaration = yield provider.provideReferences(model2, position2, { includeDeclaration: false }, token);
    if (resultWithoutDeclaration && resultWithoutDeclaration.length === 1) {
      return resultWithoutDeclaration;
    }
    return result;
  }));
}
function _sortedAndDeduped(callback) {
  return __awaiter(this, void 0, void 0, function* () {
    const rawLinks = yield callback();
    const model = new referencesModel.ReferencesModel(rawLinks, "");
    const modelLinks = model.references.map((ref) => ref.link);
    model.dispose();
    return modelLinks;
  });
}
editorExtensions.registerModelAndPositionCommand("_executeDefinitionProvider", (accessor, model, position) => {
  const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
  const promise = getDefinitionsAtPosition(languageFeaturesService.definitionProvider, model, position, cancellation.CancellationToken.None);
  return _sortedAndDeduped(() => promise);
});
editorExtensions.registerModelAndPositionCommand("_executeTypeDefinitionProvider", (accessor, model, position) => {
  const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
  const promise = getTypeDefinitionsAtPosition(languageFeaturesService.typeDefinitionProvider, model, position, cancellation.CancellationToken.None);
  return _sortedAndDeduped(() => promise);
});
editorExtensions.registerModelAndPositionCommand("_executeDeclarationProvider", (accessor, model, position) => {
  const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
  const promise = getDeclarationsAtPosition(languageFeaturesService.declarationProvider, model, position, cancellation.CancellationToken.None);
  return _sortedAndDeduped(() => promise);
});
editorExtensions.registerModelAndPositionCommand("_executeReferenceProvider", (accessor, model, position) => {
  const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
  const promise = getReferencesAtPosition(languageFeaturesService.referenceProvider, model, position, false, cancellation.CancellationToken.None);
  return _sortedAndDeduped(() => promise);
});
editorExtensions.registerModelAndPositionCommand("_executeImplementationProvider", (accessor, model, position) => {
  const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
  const promise = getImplementationsAtPosition(languageFeaturesService.implementationProvider, model, position, cancellation.CancellationToken.None);
  return _sortedAndDeduped(() => promise);
});
exports.getDeclarationsAtPosition = getDeclarationsAtPosition;
exports.getDefinitionsAtPosition = getDefinitionsAtPosition;
exports.getImplementationsAtPosition = getImplementationsAtPosition;
exports.getReferencesAtPosition = getReferencesAtPosition;
exports.getTypeDefinitionsAtPosition = getTypeDefinitionsAtPosition;
