"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const async = require("../../../../../base/common/async.js");
const errors = require("../../../../../base/common/errors.js");
const keyCodes = require("../../../../../base/common/keyCodes.js");
const lifecycle = require("../../../../../base/common/lifecycle.js");
const codeEditorService = require("../../../../browser/services/codeEditorService.js");
const position = require("../../../../common/core/position.js");
const range = require("../../../../common/core/range.js");
const peekView = require("../../../peekView/browser/peekView.js");
const nls = require("../../../../../nls.js");
const commands = require("../../../../../platform/commands/common/commands.js");
const configuration = require("../../../../../platform/configuration/common/configuration.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 listService = require("../../../../../platform/list/browser/listService.js");
const notification = require("../../../../../platform/notification/common/notification.js");
const storage = require("../../../../../platform/storage/common/storage.js");
const referencesModel = require("../referencesModel.js");
const referencesWidget = require("./referencesWidget.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());
  });
};
const ctxReferenceSearchVisible = new contextkey.RawContextKey("referenceSearchVisible", false, nls.localize("referenceSearchVisible", "Whether reference peek is visible, like 'Peek References' or 'Peek Definition'"));
exports.ReferencesController = class ReferencesController {
  constructor(_defaultTreeKeyboardSupport, _editor, contextKeyService, _editorService, _notificationService, _instantiationService, _storageService, _configurationService) {
    this._defaultTreeKeyboardSupport = _defaultTreeKeyboardSupport;
    this._editor = _editor;
    this._editorService = _editorService;
    this._notificationService = _notificationService;
    this._instantiationService = _instantiationService;
    this._storageService = _storageService;
    this._configurationService = _configurationService;
    this._disposables = new lifecycle.DisposableStore();
    this._requestIdPool = 0;
    this._ignoreModelChangeEvent = false;
    this._referenceSearchVisible = ctxReferenceSearchVisible.bindTo(contextKeyService);
  }
  static get(editor) {
    return editor.getContribution(ReferencesController.ID);
  }
  dispose() {
    var _a, _b;
    this._referenceSearchVisible.reset();
    this._disposables.dispose();
    (_a = this._widget) === null || _a === void 0 ? void 0 : _a.dispose();
    (_b = this._model) === null || _b === void 0 ? void 0 : _b.dispose();
    this._widget = void 0;
    this._model = void 0;
  }
  toggleWidget(range2, modelPromise, peekMode) {
    let widgetPosition;
    if (this._widget) {
      widgetPosition = this._widget.position;
    }
    this.closeWidget();
    if (!!widgetPosition && range2.containsPosition(widgetPosition)) {
      return;
    }
    this._peekMode = peekMode;
    this._referenceSearchVisible.set(true);
    this._disposables.add(this._editor.onDidChangeModelLanguage(() => {
      this.closeWidget();
    }));
    this._disposables.add(this._editor.onDidChangeModel(() => {
      if (!this._ignoreModelChangeEvent) {
        this.closeWidget();
      }
    }));
    const storageKey = "peekViewLayout";
    const data = referencesWidget.LayoutData.fromJSON(this._storageService.get(storageKey, 0, "{}"));
    this._widget = this._instantiationService.createInstance(referencesWidget.ReferenceWidget, this._editor, this._defaultTreeKeyboardSupport, data);
    this._widget.setTitle(nls.localize("labelLoading", "Loading..."));
    this._widget.show(range2);
    this._disposables.add(this._widget.onDidClose(() => {
      modelPromise.cancel();
      if (this._widget) {
        this._storageService.store(
          storageKey,
          JSON.stringify(this._widget.layoutData),
          0,
          1
          /* MACHINE */
        );
        this._widget = void 0;
      }
      this.closeWidget();
    }));
    this._disposables.add(this._widget.onDidSelectReference((event) => {
      let { element, kind } = event;
      if (!element) {
        return;
      }
      switch (kind) {
        case "open":
          if (event.source !== "editor" || !this._configurationService.getValue("editor.stablePeek")) {
            this.openReference(element, false, false);
          }
          break;
        case "side":
          this.openReference(element, true, false);
          break;
        case "goto":
          if (peekMode) {
            this._gotoReference(element);
          } else {
            this.openReference(element, false, true);
          }
          break;
      }
    }));
    const requestId = ++this._requestIdPool;
    modelPromise.then((model) => {
      var _a;
      if (requestId !== this._requestIdPool || !this._widget) {
        model.dispose();
        return void 0;
      }
      (_a = this._model) === null || _a === void 0 ? void 0 : _a.dispose();
      this._model = model;
      return this._widget.setModel(this._model).then(() => {
        if (this._widget && this._model && this._editor.hasModel()) {
          if (!this._model.isEmpty) {
            this._widget.setMetaTitle(nls.localize("metaTitle.N", "{0} ({1})", this._model.title, this._model.references.length));
          } else {
            this._widget.setMetaTitle("");
          }
          let uri = this._editor.getModel().uri;
          let pos = new position.Position(range2.startLineNumber, range2.startColumn);
          let selection = this._model.nearestReference(uri, pos);
          if (selection) {
            return this._widget.setSelection(selection).then(() => {
              if (this._widget && this._editor.getOption(
                77
                /* peekWidgetDefaultFocus */
              ) === "editor") {
                this._widget.focusOnPreviewEditor();
              }
            });
          }
        }
        return void 0;
      });
    }, (error) => {
      this._notificationService.error(error);
    });
  }
  changeFocusBetweenPreviewAndReferences() {
    if (!this._widget) {
      return;
    }
    if (this._widget.isPreviewEditorFocused()) {
      this._widget.focusOnReferenceTree();
    } else {
      this._widget.focusOnPreviewEditor();
    }
  }
  goToNextOrPreviousReference(fwd) {
    return __awaiter(this, void 0, void 0, function* () {
      if (!this._editor.hasModel() || !this._model || !this._widget) {
        return;
      }
      const currentPosition = this._widget.position;
      if (!currentPosition) {
        return;
      }
      const source = this._model.nearestReference(this._editor.getModel().uri, currentPosition);
      if (!source) {
        return;
      }
      const target = this._model.nextOrPreviousReference(source, fwd);
      const editorFocus = this._editor.hasTextFocus();
      const previewEditorFocus = this._widget.isPreviewEditorFocused();
      yield this._widget.setSelection(target);
      yield this._gotoReference(target);
      if (editorFocus) {
        this._editor.focus();
      } else if (this._widget && previewEditorFocus) {
        this._widget.focusOnPreviewEditor();
      }
    });
  }
  revealReference(reference) {
    return __awaiter(this, void 0, void 0, function* () {
      if (!this._editor.hasModel() || !this._model || !this._widget) {
        return;
      }
      yield this._widget.revealReference(reference);
    });
  }
  closeWidget(focusEditor = true) {
    var _a, _b;
    (_a = this._widget) === null || _a === void 0 ? void 0 : _a.dispose();
    (_b = this._model) === null || _b === void 0 ? void 0 : _b.dispose();
    this._referenceSearchVisible.reset();
    this._disposables.clear();
    this._widget = void 0;
    this._model = void 0;
    if (focusEditor) {
      this._editor.focus();
    }
    this._requestIdPool += 1;
  }
  _gotoReference(ref) {
    if (this._widget) {
      this._widget.hide();
    }
    this._ignoreModelChangeEvent = true;
    const range$1 = range.Range.lift(ref.range).collapseToStart();
    return this._editorService.openCodeEditor({
      resource: ref.uri,
      options: {
        selection: range$1,
        selectionSource: "code.jump"
        /* JUMP */
      }
    }, this._editor).then((openedEditor) => {
      var _a;
      this._ignoreModelChangeEvent = false;
      if (!openedEditor || !this._widget) {
        this.closeWidget();
        return;
      }
      if (this._editor === openedEditor) {
        this._widget.show(range$1);
        this._widget.focusOnReferenceTree();
      } else {
        const other = ReferencesController.get(openedEditor);
        const model = this._model.clone();
        this.closeWidget();
        openedEditor.focus();
        other === null || other === void 0 ? void 0 : other.toggleWidget(range$1, async.createCancelablePromise((_) => Promise.resolve(model)), (_a = this._peekMode) !== null && _a !== void 0 ? _a : false);
      }
    }, (err) => {
      this._ignoreModelChangeEvent = false;
      errors.onUnexpectedError(err);
    });
  }
  openReference(ref, sideBySide, pinned) {
    if (!sideBySide) {
      this.closeWidget();
    }
    const { uri, range: range2 } = ref;
    this._editorService.openCodeEditor({
      resource: uri,
      options: { selection: range2, selectionSource: "code.jump", pinned }
    }, this._editor, sideBySide);
  }
};
exports.ReferencesController.ID = "editor.contrib.referencesController";
exports.ReferencesController = __decorate([
  __param(2, contextkey.IContextKeyService),
  __param(3, codeEditorService.ICodeEditorService),
  __param(4, notification.INotificationService),
  __param(5, instantiation.IInstantiationService),
  __param(6, storage.IStorageService),
  __param(7, configuration.IConfigurationService)
], exports.ReferencesController);
function withController(accessor, fn) {
  const outerEditor = peekView.getOuterEditor(accessor);
  if (!outerEditor) {
    return;
  }
  const controller = exports.ReferencesController.get(outerEditor);
  if (controller) {
    fn(controller);
  }
}
keybindingsRegistry.KeybindingsRegistry.registerCommandAndKeybindingRule({
  id: "togglePeekWidgetFocus",
  weight: 100,
  primary: keyCodes.KeyChord(
    2048 | 41,
    60
    /* F2 */
  ),
  when: contextkey.ContextKeyExpr.or(ctxReferenceSearchVisible, peekView.PeekContext.inPeekEditor),
  handler(accessor) {
    withController(accessor, (controller) => {
      controller.changeFocusBetweenPreviewAndReferences();
    });
  }
});
keybindingsRegistry.KeybindingsRegistry.registerCommandAndKeybindingRule({
  id: "goToNextReference",
  weight: 100 - 10,
  primary: 62,
  secondary: [
    70
    /* F12 */
  ],
  when: contextkey.ContextKeyExpr.or(ctxReferenceSearchVisible, peekView.PeekContext.inPeekEditor),
  handler(accessor) {
    withController(accessor, (controller) => {
      controller.goToNextOrPreviousReference(true);
    });
  }
});
keybindingsRegistry.KeybindingsRegistry.registerCommandAndKeybindingRule({
  id: "goToPreviousReference",
  weight: 100 - 10,
  primary: 1024 | 62,
  secondary: [
    1024 | 70
    /* F12 */
  ],
  when: contextkey.ContextKeyExpr.or(ctxReferenceSearchVisible, peekView.PeekContext.inPeekEditor),
  handler(accessor) {
    withController(accessor, (controller) => {
      controller.goToNextOrPreviousReference(false);
    });
  }
});
commands.CommandsRegistry.registerCommandAlias("goToNextReferenceFromEmbeddedEditor", "goToNextReference");
commands.CommandsRegistry.registerCommandAlias("goToPreviousReferenceFromEmbeddedEditor", "goToPreviousReference");
commands.CommandsRegistry.registerCommandAlias("closeReferenceSearchEditor", "closeReferenceSearch");
commands.CommandsRegistry.registerCommand("closeReferenceSearch", (accessor) => withController(accessor, (controller) => controller.closeWidget()));
keybindingsRegistry.KeybindingsRegistry.registerKeybindingRule({
  id: "closeReferenceSearch",
  weight: 100 - 101,
  primary: 9,
  secondary: [
    1024 | 9
    /* Escape */
  ],
  when: contextkey.ContextKeyExpr.and(peekView.PeekContext.inPeekEditor, contextkey.ContextKeyExpr.not("config.editor.stablePeek"))
});
keybindingsRegistry.KeybindingsRegistry.registerKeybindingRule({
  id: "closeReferenceSearch",
  weight: 200 + 50,
  primary: 9,
  secondary: [
    1024 | 9
    /* Escape */
  ],
  when: contextkey.ContextKeyExpr.and(ctxReferenceSearchVisible, contextkey.ContextKeyExpr.not("config.editor.stablePeek"))
});
keybindingsRegistry.KeybindingsRegistry.registerCommandAndKeybindingRule({
  id: "revealReference",
  weight: 200,
  primary: 3,
  mac: {
    primary: 3,
    secondary: [
      2048 | 18
      /* DownArrow */
    ]
  },
  when: contextkey.ContextKeyExpr.and(ctxReferenceSearchVisible, listService.WorkbenchListFocusContextKey, listService.WorkbenchTreeElementCanCollapse.negate(), listService.WorkbenchTreeElementCanExpand.negate()),
  handler(accessor) {
    var _a;
    const listService$1 = accessor.get(listService.IListService);
    const focus = (_a = listService$1.lastFocusedList) === null || _a === void 0 ? void 0 : _a.getFocus();
    if (Array.isArray(focus) && focus[0] instanceof referencesModel.OneReference) {
      withController(accessor, (controller) => controller.revealReference(focus[0]));
    }
  }
});
keybindingsRegistry.KeybindingsRegistry.registerCommandAndKeybindingRule({
  id: "openReferenceToSide",
  weight: 100,
  primary: 2048 | 3,
  mac: {
    primary: 256 | 3
    /* Enter */
  },
  when: contextkey.ContextKeyExpr.and(ctxReferenceSearchVisible, listService.WorkbenchListFocusContextKey, listService.WorkbenchTreeElementCanCollapse.negate(), listService.WorkbenchTreeElementCanExpand.negate()),
  handler(accessor) {
    var _a;
    const listService$1 = accessor.get(listService.IListService);
    const focus = (_a = listService$1.lastFocusedList) === null || _a === void 0 ? void 0 : _a.getFocus();
    if (Array.isArray(focus) && focus[0] instanceof referencesModel.OneReference) {
      withController(accessor, (controller) => controller.openReference(focus[0], true, true));
    }
  }
});
commands.CommandsRegistry.registerCommand("openReference", (accessor) => {
  var _a;
  const listService$1 = accessor.get(listService.IListService);
  const focus = (_a = listService$1.lastFocusedList) === null || _a === void 0 ? void 0 : _a.getFocus();
  if (Array.isArray(focus) && focus[0] instanceof referencesModel.OneReference) {
    withController(accessor, (controller) => controller.openReference(focus[0], false, true));
  }
});
exports.ctxReferenceSearchVisible = ctxReferenceSearchVisible;
