import { createStyleSheet, isInShadowDOM } from "../../../../base/browser/dom.mjs";
import { RunOnceScheduler, disposableTimeout, createCancelablePromise } from "../../../../base/common/async.mjs";
import { onUnexpectedError, onUnexpectedExternalError } from "../../../../base/common/errors.mjs";
import { hash } from "../../../../base/common/hash.mjs";
import { DisposableStore, toDisposable } from "../../../../base/common/lifecycle.mjs";
import { StableEditorScrollState } from "../../../browser/stableEditorScroll.mjs";
import { registerEditorContribution, registerEditorAction, EditorAction } from "../../../browser/editorExtensions.mjs";
import { EDITOR_FONT_DEFAULTS } from "../../../common/config/editorOptions.mjs";
import { EditorContextKeys } from "../../../common/editorContextKeys.mjs";
import { getCodeLensModel } from "./codelens.mjs";
import { ICodeLensCache } from "./codeLensCache.mjs";
import { CodeLensWidget, CodeLensHelper } from "./codelensWidget.mjs";
import { localize } from "../../../../nls.mjs";
import { ICommandService } from "../../../../platform/commands/common/commands.mjs";
import { INotificationService } from "../../../../platform/notification/common/notification.mjs";
import { IQuickInputService } from "../../../../platform/quickinput/common/quickInput.mjs";
import { ILanguageFeatureDebounceService } from "../../../common/services/languageFeatureDebounce.mjs";
import { ILanguageFeaturesService } from "../../../common/services/languageFeatures.mjs";
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());
  });
};
let CodeLensContribution = class CodeLensContribution2 {
  constructor(_editor, _languageFeaturesService, debounceService, _commandService, _notificationService, _codeLensCache) {
    this._editor = _editor;
    this._languageFeaturesService = _languageFeaturesService;
    this._commandService = _commandService;
    this._notificationService = _notificationService;
    this._codeLensCache = _codeLensCache;
    this._disposables = new DisposableStore();
    this._localToDispose = new DisposableStore();
    this._lenses = [];
    this._oldCodeLensModels = new DisposableStore();
    this._provideCodeLensDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, "CodeLensProvide", { min: 250 });
    this._resolveCodeLensesDebounce = debounceService.for(_languageFeaturesService.codeLensProvider, "CodeLensResolve", { min: 250, salt: "resolve" });
    this._resolveCodeLensesScheduler = new RunOnceScheduler(() => this._resolveCodeLensesInViewport(), this._resolveCodeLensesDebounce.default());
    this._disposables.add(this._editor.onDidChangeModel(() => this._onModelChange()));
    this._disposables.add(this._editor.onDidChangeModelLanguage(() => this._onModelChange()));
    this._disposables.add(this._editor.onDidChangeConfiguration((e) => {
      if (e.hasChanged(
        44
        /* fontInfo */
      ) || e.hasChanged(
        16
        /* codeLensFontSize */
      ) || e.hasChanged(
        15
        /* codeLensFontFamily */
      )) {
        this._updateLensStyle();
      }
      if (e.hasChanged(
        14
        /* codeLens */
      )) {
        this._onModelChange();
      }
    }));
    this._disposables.add(_languageFeaturesService.codeLensProvider.onDidChange(this._onModelChange, this));
    this._onModelChange();
    this._styleClassName = "_" + hash(this._editor.getId()).toString(16);
    this._styleElement = createStyleSheet(isInShadowDOM(this._editor.getContainerDomNode()) ? this._editor.getContainerDomNode() : void 0);
    this._updateLensStyle();
  }
  dispose() {
    var _a;
    this._localDispose();
    this._disposables.dispose();
    this._oldCodeLensModels.dispose();
    (_a = this._currentCodeLensModel) === null || _a === void 0 ? void 0 : _a.dispose();
    this._styleElement.remove();
  }
  _getLayoutInfo() {
    let fontSize = this._editor.getOption(
      16
      /* codeLensFontSize */
    );
    let codeLensHeight;
    if (!fontSize || fontSize < 5) {
      fontSize = this._editor.getOption(
        46
        /* fontSize */
      ) * 0.9 | 0;
      codeLensHeight = this._editor.getOption(
        59
        /* lineHeight */
      );
    } else {
      codeLensHeight = fontSize * Math.max(1.3, this._editor.getOption(
        59
        /* lineHeight */
      ) / this._editor.getOption(
        46
        /* fontSize */
      )) | 0;
    }
    return { codeLensHeight, fontSize };
  }
  _updateLensStyle() {
    const { codeLensHeight, fontSize } = this._getLayoutInfo();
    const fontFamily = this._editor.getOption(
      15
      /* codeLensFontFamily */
    );
    const editorFontInfo = this._editor.getOption(
      44
      /* fontInfo */
    );
    const fontFamilyVar = `--codelens-font-family${this._styleClassName}`;
    const fontFeaturesVar = `--codelens-font-features${this._styleClassName}`;
    let newStyle = `
		.monaco-editor .codelens-decoration.${this._styleClassName} { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontSize * 0.5)}px; font-feature-settings: var(${fontFeaturesVar}) }
		.monaco-editor .codelens-decoration.${this._styleClassName} span.codicon { line-height: ${codeLensHeight}px; font-size: ${fontSize}px; }
		`;
    if (fontFamily) {
      newStyle += `.monaco-editor .codelens-decoration.${this._styleClassName} { font-family: var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}}`;
    }
    this._styleElement.textContent = newStyle;
    this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily !== null && fontFamily !== void 0 ? fontFamily : "inherit");
    this._editor.getContainerDomNode().style.setProperty(fontFeaturesVar, editorFontInfo.fontFeatureSettings);
    this._editor.changeViewZones((accessor) => {
      for (let lens of this._lenses) {
        lens.updateHeight(codeLensHeight, accessor);
      }
    });
  }
  _localDispose() {
    var _a, _b, _c;
    (_a = this._getCodeLensModelPromise) === null || _a === void 0 ? void 0 : _a.cancel();
    this._getCodeLensModelPromise = void 0;
    (_b = this._resolveCodeLensesPromise) === null || _b === void 0 ? void 0 : _b.cancel();
    this._resolveCodeLensesPromise = void 0;
    this._localToDispose.clear();
    this._oldCodeLensModels.clear();
    (_c = this._currentCodeLensModel) === null || _c === void 0 ? void 0 : _c.dispose();
  }
  _onModelChange() {
    this._localDispose();
    const model = this._editor.getModel();
    if (!model) {
      return;
    }
    if (!this._editor.getOption(
      14
      /* codeLens */
    )) {
      return;
    }
    const cachedLenses = this._codeLensCache.get(model);
    if (cachedLenses) {
      this._renderCodeLensSymbols(cachedLenses);
    }
    if (!this._languageFeaturesService.codeLensProvider.has(model)) {
      if (cachedLenses) {
        this._localToDispose.add(disposableTimeout(() => {
          const cachedLensesNow = this._codeLensCache.get(model);
          if (cachedLenses === cachedLensesNow) {
            this._codeLensCache.delete(model);
            this._onModelChange();
          }
        }, 30 * 1e3));
      }
      return;
    }
    for (const provider of this._languageFeaturesService.codeLensProvider.all(model)) {
      if (typeof provider.onDidChange === "function") {
        let registration = provider.onDidChange(() => scheduler.schedule());
        this._localToDispose.add(registration);
      }
    }
    const scheduler = new RunOnceScheduler(() => {
      var _a;
      const t1 = Date.now();
      (_a = this._getCodeLensModelPromise) === null || _a === void 0 ? void 0 : _a.cancel();
      this._getCodeLensModelPromise = createCancelablePromise((token) => getCodeLensModel(this._languageFeaturesService.codeLensProvider, model, token));
      this._getCodeLensModelPromise.then((result) => {
        if (this._currentCodeLensModel) {
          this._oldCodeLensModels.add(this._currentCodeLensModel);
        }
        this._currentCodeLensModel = result;
        this._codeLensCache.put(model, result);
        const newDelay = this._provideCodeLensDebounce.update(model, Date.now() - t1);
        scheduler.delay = newDelay;
        this._renderCodeLensSymbols(result);
        this._resolveCodeLensesInViewportSoon();
      }, onUnexpectedError);
    }, this._provideCodeLensDebounce.get(model));
    this._localToDispose.add(scheduler);
    this._localToDispose.add(toDisposable(() => this._resolveCodeLensesScheduler.cancel()));
    this._localToDispose.add(this._editor.onDidChangeModelContent(() => {
      this._editor.changeDecorations((decorationsAccessor) => {
        this._editor.changeViewZones((viewZonesAccessor) => {
          let toDispose = [];
          let lastLensLineNumber = -1;
          this._lenses.forEach((lens) => {
            if (!lens.isValid() || lastLensLineNumber === lens.getLineNumber()) {
              toDispose.push(lens);
            } else {
              lens.update(viewZonesAccessor);
              lastLensLineNumber = lens.getLineNumber();
            }
          });
          let helper = new CodeLensHelper();
          toDispose.forEach((l) => {
            l.dispose(helper, viewZonesAccessor);
            this._lenses.splice(this._lenses.indexOf(l), 1);
          });
          helper.commit(decorationsAccessor);
        });
      });
      scheduler.schedule();
    }));
    this._localToDispose.add(this._editor.onDidFocusEditorWidget(() => {
      scheduler.schedule();
    }));
    this._localToDispose.add(this._editor.onDidScrollChange((e) => {
      if (e.scrollTopChanged && this._lenses.length > 0) {
        this._resolveCodeLensesInViewportSoon();
      }
    }));
    this._localToDispose.add(this._editor.onDidLayoutChange(() => {
      this._resolveCodeLensesInViewportSoon();
    }));
    this._localToDispose.add(toDisposable(() => {
      if (this._editor.getModel()) {
        const scrollState = StableEditorScrollState.capture(this._editor);
        this._editor.changeDecorations((decorationsAccessor) => {
          this._editor.changeViewZones((viewZonesAccessor) => {
            this._disposeAllLenses(decorationsAccessor, viewZonesAccessor);
          });
        });
        scrollState.restore(this._editor);
      } else {
        this._disposeAllLenses(void 0, void 0);
      }
    }));
    this._localToDispose.add(this._editor.onMouseDown((e) => {
      if (e.target.type !== 9) {
        return;
      }
      let target = e.target.element;
      if ((target === null || target === void 0 ? void 0 : target.tagName) === "SPAN") {
        target = target.parentElement;
      }
      if ((target === null || target === void 0 ? void 0 : target.tagName) === "A") {
        for (const lens of this._lenses) {
          let command = lens.getCommand(target);
          if (command) {
            this._commandService.executeCommand(command.id, ...command.arguments || []).catch((err) => this._notificationService.error(err));
            break;
          }
        }
      }
    }));
    scheduler.schedule();
  }
  _disposeAllLenses(decChangeAccessor, viewZoneChangeAccessor) {
    const helper = new CodeLensHelper();
    for (const lens of this._lenses) {
      lens.dispose(helper, viewZoneChangeAccessor);
    }
    if (decChangeAccessor) {
      helper.commit(decChangeAccessor);
    }
    this._lenses.length = 0;
  }
  _renderCodeLensSymbols(symbols) {
    if (!this._editor.hasModel()) {
      return;
    }
    let maxLineNumber = this._editor.getModel().getLineCount();
    let groups = [];
    let lastGroup;
    for (let symbol of symbols.lenses) {
      let line = symbol.symbol.range.startLineNumber;
      if (line < 1 || line > maxLineNumber) {
        continue;
      } else if (lastGroup && lastGroup[lastGroup.length - 1].symbol.range.startLineNumber === line) {
        lastGroup.push(symbol);
      } else {
        lastGroup = [symbol];
        groups.push(lastGroup);
      }
    }
    const scrollState = StableEditorScrollState.capture(this._editor);
    const layoutInfo = this._getLayoutInfo();
    this._editor.changeDecorations((decorationsAccessor) => {
      this._editor.changeViewZones((viewZoneAccessor) => {
        const helper = new CodeLensHelper();
        let codeLensIndex = 0;
        let groupsIndex = 0;
        while (groupsIndex < groups.length && codeLensIndex < this._lenses.length) {
          let symbolsLineNumber = groups[groupsIndex][0].symbol.range.startLineNumber;
          let codeLensLineNumber = this._lenses[codeLensIndex].getLineNumber();
          if (codeLensLineNumber < symbolsLineNumber) {
            this._lenses[codeLensIndex].dispose(helper, viewZoneAccessor);
            this._lenses.splice(codeLensIndex, 1);
          } else if (codeLensLineNumber === symbolsLineNumber) {
            this._lenses[codeLensIndex].updateCodeLensSymbols(groups[groupsIndex], helper);
            groupsIndex++;
            codeLensIndex++;
          } else {
            this._lenses.splice(codeLensIndex, 0, new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon()));
            codeLensIndex++;
            groupsIndex++;
          }
        }
        while (codeLensIndex < this._lenses.length) {
          this._lenses[codeLensIndex].dispose(helper, viewZoneAccessor);
          this._lenses.splice(codeLensIndex, 1);
        }
        while (groupsIndex < groups.length) {
          this._lenses.push(new CodeLensWidget(groups[groupsIndex], this._editor, this._styleClassName, helper, viewZoneAccessor, layoutInfo.codeLensHeight, () => this._resolveCodeLensesInViewportSoon()));
          groupsIndex++;
        }
        helper.commit(decorationsAccessor);
      });
    });
    scrollState.restore(this._editor);
  }
  _resolveCodeLensesInViewportSoon() {
    const model = this._editor.getModel();
    if (model) {
      this._resolveCodeLensesScheduler.schedule();
    }
  }
  _resolveCodeLensesInViewport() {
    var _a;
    (_a = this._resolveCodeLensesPromise) === null || _a === void 0 ? void 0 : _a.cancel();
    this._resolveCodeLensesPromise = void 0;
    const model = this._editor.getModel();
    if (!model) {
      return;
    }
    const toResolve = [];
    const lenses = [];
    this._lenses.forEach((lens) => {
      const request = lens.computeIfNecessary(model);
      if (request) {
        toResolve.push(request);
        lenses.push(lens);
      }
    });
    if (toResolve.length === 0) {
      return;
    }
    const t1 = Date.now();
    const resolvePromise = createCancelablePromise((token) => {
      const promises = toResolve.map((request, i) => {
        const resolvedSymbols = new Array(request.length);
        const promises2 = request.map((request2, i2) => {
          if (!request2.symbol.command && typeof request2.provider.resolveCodeLens === "function") {
            return Promise.resolve(request2.provider.resolveCodeLens(model, request2.symbol, token)).then((symbol) => {
              resolvedSymbols[i2] = symbol;
            }, onUnexpectedExternalError);
          } else {
            resolvedSymbols[i2] = request2.symbol;
            return Promise.resolve(void 0);
          }
        });
        return Promise.all(promises2).then(() => {
          if (!token.isCancellationRequested && !lenses[i].isDisposed()) {
            lenses[i].updateCommands(resolvedSymbols);
          }
        });
      });
      return Promise.all(promises);
    });
    this._resolveCodeLensesPromise = resolvePromise;
    this._resolveCodeLensesPromise.then(() => {
      const newDelay = this._resolveCodeLensesDebounce.update(model, Date.now() - t1);
      this._resolveCodeLensesScheduler.delay = newDelay;
      if (this._currentCodeLensModel) {
        this._codeLensCache.put(model, this._currentCodeLensModel);
      }
      this._oldCodeLensModels.clear();
      if (resolvePromise === this._resolveCodeLensesPromise) {
        this._resolveCodeLensesPromise = void 0;
      }
    }, (err) => {
      onUnexpectedError(err);
      if (resolvePromise === this._resolveCodeLensesPromise) {
        this._resolveCodeLensesPromise = void 0;
      }
    });
  }
  getModel() {
    return this._currentCodeLensModel;
  }
};
CodeLensContribution.ID = "css.editor.codeLens";
CodeLensContribution = __decorate([
  __param(1, ILanguageFeaturesService),
  __param(2, ILanguageFeatureDebounceService),
  __param(3, ICommandService),
  __param(4, INotificationService),
  __param(5, ICodeLensCache)
], CodeLensContribution);
registerEditorContribution(CodeLensContribution.ID, CodeLensContribution);
registerEditorAction(class ShowLensesInCurrentLine extends EditorAction {
  constructor() {
    super({
      id: "codelens.showLensesInCurrentLine",
      precondition: EditorContextKeys.hasCodeLensProvider,
      label: localize("showLensOnLine", "Show CodeLens Commands For Current Line"),
      alias: "Show CodeLens Commands For Current Line"
    });
  }
  run(accessor, editor) {
    return __awaiter(this, void 0, void 0, function* () {
      if (!editor.hasModel()) {
        return;
      }
      const quickInputService = accessor.get(IQuickInputService);
      const commandService = accessor.get(ICommandService);
      const notificationService = accessor.get(INotificationService);
      const lineNumber = editor.getSelection().positionLineNumber;
      const codelensController = editor.getContribution(CodeLensContribution.ID);
      if (!codelensController) {
        return;
      }
      const model = codelensController.getModel();
      if (!model) {
        return;
      }
      const items = [];
      for (const lens of model.lenses) {
        if (lens.symbol.command && lens.symbol.range.startLineNumber === lineNumber) {
          items.push({
            label: lens.symbol.command.title,
            command: lens.symbol.command
          });
        }
      }
      if (items.length === 0) {
        return;
      }
      const item = yield quickInputService.pick(items, { canPickMany: false });
      if (!item) {
        return;
      }
      if (model.isDisposed) {
        return yield commandService.executeCommand(this.id);
      }
      try {
        yield commandService.executeCommand(item.command.id, ...item.command.arguments || []);
      } catch (err) {
        notificationService.error(err);
      }
    });
  }
});
export {
  CodeLensContribution
};
