"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const nls = require("../../nls.js");
const uri = require("../../base/common/uri.js");
const codeEditorService = require("./services/codeEditorService.js");
const position = require("../common/core/position.js");
const model = require("../common/services/model.js");
const resolverService = require("../common/services/resolverService.js");
const actions = require("../../platform/actions/common/actions.js");
const commands = require("../../platform/commands/common/commands.js");
const contextkey = require("../../platform/contextkey/common/contextkey.js");
const instantiation = require("../../platform/instantiation/common/instantiation.js");
const keybindingsRegistry = require("../../platform/keybinding/common/keybindingsRegistry.js");
const platform = require("../../platform/registry/common/platform.js");
const telemetry = require("../../platform/telemetry/common/telemetry.js");
const types = require("../../base/common/types.js");
const log = require("../../platform/log/common/log.js");
class Command {
  constructor(opts) {
    this.id = opts.id;
    this.precondition = opts.precondition;
    this._kbOpts = opts.kbOpts;
    this._menuOpts = opts.menuOpts;
    this._description = opts.description;
  }
  register() {
    if (Array.isArray(this._menuOpts)) {
      this._menuOpts.forEach(this._registerMenuItem, this);
    } else if (this._menuOpts) {
      this._registerMenuItem(this._menuOpts);
    }
    if (this._kbOpts) {
      const kbOptsArr = Array.isArray(this._kbOpts) ? this._kbOpts : [this._kbOpts];
      for (const kbOpts of kbOptsArr) {
        let kbWhen = kbOpts.kbExpr;
        if (this.precondition) {
          if (kbWhen) {
            kbWhen = contextkey.ContextKeyExpr.and(kbWhen, this.precondition);
          } else {
            kbWhen = this.precondition;
          }
        }
        const desc = {
          id: this.id,
          weight: kbOpts.weight,
          args: kbOpts.args,
          when: kbWhen,
          primary: kbOpts.primary,
          secondary: kbOpts.secondary,
          win: kbOpts.win,
          linux: kbOpts.linux,
          mac: kbOpts.mac
        };
        keybindingsRegistry.KeybindingsRegistry.registerKeybindingRule(desc);
      }
    }
    commands.CommandsRegistry.registerCommand({
      id: this.id,
      handler: (accessor, args) => this.runCommand(accessor, args),
      description: this._description
    });
  }
  _registerMenuItem(item) {
    actions.MenuRegistry.appendMenuItem(item.menuId, {
      group: item.group,
      command: {
        id: this.id,
        title: item.title,
        icon: item.icon,
        precondition: this.precondition
      },
      when: item.when,
      order: item.order
    });
  }
}
class MultiCommand extends Command {
  constructor() {
    super(...arguments);
    this._implementations = [];
  }
  /**
   * A higher priority gets to be looked at first
   */
  addImplementation(priority, name, implementation) {
    this._implementations.push({ priority, name, implementation });
    this._implementations.sort((a, b) => b.priority - a.priority);
    return {
      dispose: () => {
        for (let i = 0; i < this._implementations.length; i++) {
          if (this._implementations[i].implementation === implementation) {
            this._implementations.splice(i, 1);
            return;
          }
        }
      }
    };
  }
  runCommand(accessor, args) {
    const logService = accessor.get(log.ILogService);
    logService.trace(`Executing Command '${this.id}' which has ${this._implementations.length} bound.`);
    for (const impl of this._implementations) {
      const result = impl.implementation(accessor, args);
      if (result) {
        logService.trace(`Command '${this.id}' was handled by '${impl.name}'.`);
        if (typeof result === "boolean") {
          return;
        }
        return result;
      }
    }
    logService.trace(`The Command '${this.id}' was not handled by any implementation.`);
  }
}
class ProxyCommand extends Command {
  constructor(command, opts) {
    super(opts);
    this.command = command;
  }
  runCommand(accessor, args) {
    return this.command.runCommand(accessor, args);
  }
}
class EditorCommand extends Command {
  /**
   * Create a command class that is bound to a certain editor contribution.
   */
  static bindToContribution(controllerGetter) {
    return class EditorControllerCommandImpl extends EditorCommand {
      constructor(opts) {
        super(opts);
        this._callback = opts.handler;
      }
      runEditorCommand(accessor, editor, args) {
        const controller = controllerGetter(editor);
        if (controller) {
          this._callback(controller, args);
        }
      }
    };
  }
  runCommand(accessor, args) {
    const codeEditorService$1 = accessor.get(codeEditorService.ICodeEditorService);
    const editor = codeEditorService$1.getFocusedCodeEditor() || codeEditorService$1.getActiveCodeEditor();
    if (!editor) {
      return;
    }
    return editor.invokeWithinContext((editorAccessor) => {
      const kbService = editorAccessor.get(contextkey.IContextKeyService);
      if (!kbService.contextMatchesRules(types.withNullAsUndefined(this.precondition))) {
        return;
      }
      return this.runEditorCommand(editorAccessor, editor, args);
    });
  }
}
class EditorAction extends EditorCommand {
  constructor(opts) {
    super(EditorAction.convertOptions(opts));
    this.label = opts.label;
    this.alias = opts.alias;
  }
  static convertOptions(opts) {
    let menuOpts;
    if (Array.isArray(opts.menuOpts)) {
      menuOpts = opts.menuOpts;
    } else if (opts.menuOpts) {
      menuOpts = [opts.menuOpts];
    } else {
      menuOpts = [];
    }
    function withDefaults(item) {
      if (!item.menuId) {
        item.menuId = actions.MenuId.EditorContext;
      }
      if (!item.title) {
        item.title = opts.label;
      }
      item.when = contextkey.ContextKeyExpr.and(opts.precondition, item.when);
      return item;
    }
    if (Array.isArray(opts.contextMenuOpts)) {
      menuOpts.push(...opts.contextMenuOpts.map(withDefaults));
    } else if (opts.contextMenuOpts) {
      menuOpts.push(withDefaults(opts.contextMenuOpts));
    }
    opts.menuOpts = menuOpts;
    return opts;
  }
  runEditorCommand(accessor, editor, args) {
    this.reportTelemetry(accessor, editor);
    return this.run(accessor, editor, args || {});
  }
  reportTelemetry(accessor, editor) {
    accessor.get(telemetry.ITelemetryService).publicLog2("editorActionInvoked", { name: this.label, id: this.id });
  }
}
class MultiEditorAction extends EditorAction {
  constructor() {
    super(...arguments);
    this._implementations = [];
  }
  /**
   * A higher priority gets to be looked at first
   */
  addImplementation(priority, implementation) {
    this._implementations.push([priority, implementation]);
    this._implementations.sort((a, b) => b[0] - a[0]);
    return {
      dispose: () => {
        for (let i = 0; i < this._implementations.length; i++) {
          if (this._implementations[i][1] === implementation) {
            this._implementations.splice(i, 1);
            return;
          }
        }
      }
    };
  }
  run(accessor, editor, args) {
    for (const impl of this._implementations) {
      const result = impl[1](accessor, editor, args);
      if (result) {
        if (typeof result === "boolean") {
          return;
        }
        return result;
      }
    }
  }
}
function registerModelAndPositionCommand(id, handler) {
  commands.CommandsRegistry.registerCommand(id, function(accessor, ...args) {
    const instaService = accessor.get(instantiation.IInstantiationService);
    const [resource, position$1] = args;
    types.assertType(uri.URI.isUri(resource));
    types.assertType(position.Position.isIPosition(position$1));
    const model$1 = accessor.get(model.IModelService).getModel(resource);
    if (model$1) {
      const editorPosition = position.Position.lift(position$1);
      return instaService.invokeFunction(handler, model$1, editorPosition, ...args.slice(2));
    }
    return accessor.get(resolverService.ITextModelService).createModelReference(resource).then((reference) => {
      return new Promise((resolve, reject) => {
        try {
          const result = instaService.invokeFunction(handler, reference.object.textEditorModel, position.Position.lift(position$1), args.slice(2));
          resolve(result);
        } catch (err) {
          reject(err);
        }
      }).finally(() => {
        reference.dispose();
      });
    });
  });
}
function registerEditorCommand(editorCommand) {
  EditorContributionRegistry.INSTANCE.registerEditorCommand(editorCommand);
  return editorCommand;
}
function registerEditorAction(ctor) {
  const action = new ctor();
  EditorContributionRegistry.INSTANCE.registerEditorAction(action);
  return action;
}
function registerMultiEditorAction(action) {
  EditorContributionRegistry.INSTANCE.registerEditorAction(action);
  return action;
}
function registerInstantiatedEditorAction(editorAction) {
  EditorContributionRegistry.INSTANCE.registerEditorAction(editorAction);
}
function registerEditorContribution(id, ctor) {
  EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor);
}
exports.EditorExtensionsRegistry = void 0;
(function(EditorExtensionsRegistry) {
  function getEditorCommand(commandId) {
    return EditorContributionRegistry.INSTANCE.getEditorCommand(commandId);
  }
  EditorExtensionsRegistry.getEditorCommand = getEditorCommand;
  function getEditorActions() {
    return EditorContributionRegistry.INSTANCE.getEditorActions();
  }
  EditorExtensionsRegistry.getEditorActions = getEditorActions;
  function getEditorContributions() {
    return EditorContributionRegistry.INSTANCE.getEditorContributions();
  }
  EditorExtensionsRegistry.getEditorContributions = getEditorContributions;
  function getSomeEditorContributions(ids) {
    return EditorContributionRegistry.INSTANCE.getEditorContributions().filter((c) => ids.indexOf(c.id) >= 0);
  }
  EditorExtensionsRegistry.getSomeEditorContributions = getSomeEditorContributions;
  function getDiffEditorContributions() {
    return EditorContributionRegistry.INSTANCE.getDiffEditorContributions();
  }
  EditorExtensionsRegistry.getDiffEditorContributions = getDiffEditorContributions;
})(exports.EditorExtensionsRegistry || (exports.EditorExtensionsRegistry = {}));
const Extensions = {
  EditorCommonContributions: "editor.contributions"
};
class EditorContributionRegistry {
  constructor() {
    this.editorContributions = [];
    this.diffEditorContributions = [];
    this.editorActions = [];
    this.editorCommands = /* @__PURE__ */ Object.create(null);
  }
  registerEditorContribution(id, ctor) {
    this.editorContributions.push({ id, ctor });
  }
  getEditorContributions() {
    return this.editorContributions.slice(0);
  }
  getDiffEditorContributions() {
    return this.diffEditorContributions.slice(0);
  }
  registerEditorAction(action) {
    action.register();
    this.editorActions.push(action);
  }
  getEditorActions() {
    return this.editorActions.slice(0);
  }
  registerEditorCommand(editorCommand) {
    editorCommand.register();
    this.editorCommands[editorCommand.id] = editorCommand;
  }
  getEditorCommand(commandId) {
    return this.editorCommands[commandId] || null;
  }
}
EditorContributionRegistry.INSTANCE = new EditorContributionRegistry();
platform.Registry.add(Extensions.EditorCommonContributions, EditorContributionRegistry.INSTANCE);
function registerCommand(command) {
  command.register();
  return command;
}
const UndoCommand = registerCommand(new MultiCommand({
  id: "undo",
  precondition: void 0,
  kbOpts: {
    weight: 0,
    primary: 2048 | 56
    /* KeyZ */
  },
  menuOpts: [{
    menuId: actions.MenuId.MenubarEditMenu,
    group: "1_do",
    title: nls.localize({ key: "miUndo", comment: ["&& denotes a mnemonic"] }, "&&Undo"),
    order: 1
  }, {
    menuId: actions.MenuId.CommandPalette,
    group: "",
    title: nls.localize("undo", "Undo"),
    order: 1
  }]
}));
registerCommand(new ProxyCommand(UndoCommand, { id: "default:undo", precondition: void 0 }));
const RedoCommand = registerCommand(new MultiCommand({
  id: "redo",
  precondition: void 0,
  kbOpts: {
    weight: 0,
    primary: 2048 | 55,
    secondary: [
      2048 | 1024 | 56
      /* KeyZ */
    ],
    mac: {
      primary: 2048 | 1024 | 56
      /* KeyZ */
    }
  },
  menuOpts: [{
    menuId: actions.MenuId.MenubarEditMenu,
    group: "1_do",
    title: nls.localize({ key: "miRedo", comment: ["&& denotes a mnemonic"] }, "&&Redo"),
    order: 2
  }, {
    menuId: actions.MenuId.CommandPalette,
    group: "",
    title: nls.localize("redo", "Redo"),
    order: 1
  }]
}));
registerCommand(new ProxyCommand(RedoCommand, { id: "default:redo", precondition: void 0 }));
const SelectAllCommand = registerCommand(new MultiCommand({
  id: "editor.action.selectAll",
  precondition: void 0,
  kbOpts: {
    weight: 0,
    kbExpr: null,
    primary: 2048 | 31
    /* KeyA */
  },
  menuOpts: [{
    menuId: actions.MenuId.MenubarSelectionMenu,
    group: "1_basic",
    title: nls.localize({ key: "miSelectAll", comment: ["&& denotes a mnemonic"] }, "&&Select All"),
    order: 1
  }, {
    menuId: actions.MenuId.CommandPalette,
    group: "",
    title: nls.localize("selectAll", "Select All"),
    order: 1
  }]
}));
exports.Command = Command;
exports.EditorAction = EditorAction;
exports.EditorCommand = EditorCommand;
exports.MultiCommand = MultiCommand;
exports.MultiEditorAction = MultiEditorAction;
exports.ProxyCommand = ProxyCommand;
exports.RedoCommand = RedoCommand;
exports.SelectAllCommand = SelectAllCommand;
exports.UndoCommand = UndoCommand;
exports.registerEditorAction = registerEditorAction;
exports.registerEditorCommand = registerEditorCommand;
exports.registerEditorContribution = registerEditorContribution;
exports.registerInstantiatedEditorAction = registerInstantiatedEditorAction;
exports.registerModelAndPositionCommand = registerModelAndPositionCommand;
exports.registerMultiEditorAction = registerMultiEditorAction;
