"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 uri = require("../../../../base/common/uri.js");
const editorState = require("../../editorState/browser/editorState.js");
const range = require("../../../common/core/range.js");
const selection = require("../../../common/core/selection.js");
const model = require("../../../common/services/model.js");
const commands = require("../../../../platform/commands/common/commands.js");
const progress = require("../../../../platform/progress/common/progress.js");
const types = require("./types.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());
  });
};
const codeActionCommandId = "editor.action.codeAction";
const refactorCommandId = "editor.action.refactor";
const sourceActionCommandId = "editor.action.sourceAction";
const organizeImportsCommandId = "editor.action.organizeImports";
const fixAllCommandId = "editor.action.fixAll";
class CodeActionItem {
  constructor(action, provider) {
    this.action = action;
    this.provider = provider;
  }
  resolve(token) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      if (((_a = this.provider) === null || _a === void 0 ? void 0 : _a.resolveCodeAction) && !this.action.edit) {
        let action;
        try {
          action = yield this.provider.resolveCodeAction(this.action, token);
        } catch (err) {
          errors.onUnexpectedExternalError(err);
        }
        if (action) {
          this.action.edit = action.edit;
        }
      }
      return this;
    });
  }
}
class ManagedCodeActionSet extends lifecycle.Disposable {
  constructor(actions, documentation, disposables) {
    super();
    this.documentation = documentation;
    this._register(disposables);
    this.allActions = [...actions].sort(ManagedCodeActionSet.codeActionsComparator);
    this.validActions = this.allActions.filter(({ action }) => !action.disabled);
  }
  static codeActionsComparator({ action: a }, { action: b }) {
    if (a.isPreferred && !b.isPreferred) {
      return -1;
    } else if (!a.isPreferred && b.isPreferred) {
      return 1;
    }
    if (arrays.isNonEmptyArray(a.diagnostics)) {
      if (arrays.isNonEmptyArray(b.diagnostics)) {
        return a.diagnostics[0].message.localeCompare(b.diagnostics[0].message);
      } else {
        return -1;
      }
    } else if (arrays.isNonEmptyArray(b.diagnostics)) {
      return 1;
    } else {
      return 0;
    }
  }
  get hasAutoFix() {
    return this.validActions.some(({ action: fix }) => !!fix.kind && types.CodeActionKind.QuickFix.contains(new types.CodeActionKind(fix.kind)) && !!fix.isPreferred);
  }
}
const emptyCodeActionsResponse = { actions: [], documentation: void 0 };
function getCodeActions(registry, model2, rangeOrSelection, trigger, progress2, token) {
  var _a;
  const filter = trigger.filter || {};
  const codeActionContext = {
    only: (_a = filter.include) === null || _a === void 0 ? void 0 : _a.value,
    trigger: trigger.type
  };
  const cts = new editorState.TextModelCancellationTokenSource(model2, token);
  const providers = getCodeActionProviders(registry, model2, filter);
  const disposables = new lifecycle.DisposableStore();
  const promises = providers.map((provider) => __awaiter(this, void 0, void 0, function* () {
    try {
      progress2.report(provider);
      const providedCodeActions = yield provider.provideCodeActions(model2, rangeOrSelection, codeActionContext, cts.token);
      if (providedCodeActions) {
        disposables.add(providedCodeActions);
      }
      if (cts.token.isCancellationRequested) {
        return emptyCodeActionsResponse;
      }
      const filteredActions = ((providedCodeActions === null || providedCodeActions === void 0 ? void 0 : providedCodeActions.actions) || []).filter((action) => action && types.filtersAction(filter, action));
      const documentation = getDocumentation(provider, filteredActions, filter.include);
      return {
        actions: filteredActions.map((action) => new CodeActionItem(action, provider)),
        documentation
      };
    } catch (err) {
      if (errors.isCancellationError(err)) {
        throw err;
      }
      errors.onUnexpectedExternalError(err);
      return emptyCodeActionsResponse;
    }
  }));
  const listener = registry.onDidChange(() => {
    const newProviders = registry.all(model2);
    if (!arrays.equals(newProviders, providers)) {
      cts.cancel();
    }
  });
  return Promise.all(promises).then((actions) => {
    const allActions = arrays.flatten(actions.map((x) => x.actions));
    const allDocumentation = arrays.coalesce(actions.map((x) => x.documentation));
    return new ManagedCodeActionSet(allActions, allDocumentation, disposables);
  }).finally(() => {
    listener.dispose();
    cts.dispose();
  });
}
function getCodeActionProviders(registry, model2, filter) {
  return registry.all(model2).filter((provider) => {
    if (!provider.providedCodeActionKinds) {
      return true;
    }
    return provider.providedCodeActionKinds.some((kind) => types.mayIncludeActionsOfKind(filter, new types.CodeActionKind(kind)));
  });
}
function getDocumentation(provider, providedCodeActions, only) {
  if (!provider.documentation) {
    return void 0;
  }
  const documentation = provider.documentation.map((entry) => ({ kind: new types.CodeActionKind(entry.kind), command: entry.command }));
  if (only) {
    let currentBest;
    for (const entry of documentation) {
      if (entry.kind.contains(only)) {
        if (!currentBest) {
          currentBest = entry;
        } else {
          if (currentBest.kind.contains(entry.kind)) {
            currentBest = entry;
          }
        }
      }
    }
    if (currentBest) {
      return currentBest === null || currentBest === void 0 ? void 0 : currentBest.command;
    }
  }
  for (const action of providedCodeActions) {
    if (!action.kind) {
      continue;
    }
    for (const entry of documentation) {
      if (entry.kind.contains(new types.CodeActionKind(action.kind))) {
        return entry.command;
      }
    }
  }
  return void 0;
}
commands.CommandsRegistry.registerCommand("_executeCodeActionProvider", function(accessor, resource, rangeOrSelection, kind, itemResolveCount) {
  return __awaiter(this, void 0, void 0, function* () {
    if (!(resource instanceof uri.URI)) {
      throw errors.illegalArgument();
    }
    const { codeActionProvider } = accessor.get(languageFeatures.ILanguageFeaturesService);
    const model$1 = accessor.get(model.IModelService).getModel(resource);
    if (!model$1) {
      throw errors.illegalArgument();
    }
    const validatedRangeOrSelection = selection.Selection.isISelection(rangeOrSelection) ? selection.Selection.liftSelection(rangeOrSelection) : range.Range.isIRange(rangeOrSelection) ? model$1.validateRange(rangeOrSelection) : void 0;
    if (!validatedRangeOrSelection) {
      throw errors.illegalArgument();
    }
    const include = typeof kind === "string" ? new types.CodeActionKind(kind) : void 0;
    const codeActionSet = yield getCodeActions(codeActionProvider, model$1, validatedRangeOrSelection, { type: 1, filter: { includeSourceActions: true, include } }, progress.Progress.None, cancellation.CancellationToken.None);
    const resolving = [];
    const resolveCount = Math.min(codeActionSet.validActions.length, typeof itemResolveCount === "number" ? itemResolveCount : 0);
    for (let i = 0; i < resolveCount; i++) {
      resolving.push(codeActionSet.validActions[i].resolve(cancellation.CancellationToken.None));
    }
    try {
      yield Promise.all(resolving);
      return codeActionSet.validActions.map((item) => item.action);
    } finally {
      setTimeout(() => codeActionSet.dispose(), 100);
    }
  });
});
exports.CodeActionItem = CodeActionItem;
exports.codeActionCommandId = codeActionCommandId;
exports.fixAllCommandId = fixAllCommandId;
exports.getCodeActions = getCodeActions;
exports.organizeImportsCommandId = organizeImportsCommandId;
exports.refactorCommandId = refactorCommandId;
exports.sourceActionCommandId = sourceActionCommandId;
