import "./inspectTokens.css.mjs";
import { reset, $, append } from "../../../../base/browser/dom.mjs";
import { Color } from "../../../../base/common/color.mjs";
import { Disposable } from "../../../../base/common/lifecycle.mjs";
import { registerEditorContribution, registerEditorAction, EditorAction } from "../../../browser/editorExtensions.mjs";
import { TokenizationRegistry, TokenMetadata } from "../../../common/languages.mjs";
import { NullState, nullTokenize, nullTokenizeEncoded } from "../../../common/languages/nullTokenize.mjs";
import { ILanguageService } from "../../../common/languages/language.mjs";
import { IStandaloneThemeService } from "../../common/standaloneTheme.mjs";
import { editorHoverBorder, editorHoverBackground, editorHoverForeground } from "../../../../platform/theme/common/colorRegistry.mjs";
import { registerThemingParticipant } from "../../../../platform/theme/common/themeService.mjs";
import { InspectTokensNLS } from "../../../common/standaloneStrings.mjs";
import { ColorScheme } from "../../../../platform/theme/common/theme.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);
  };
};
let InspectTokensController = class InspectTokensController2 extends Disposable {
  constructor(editor, standaloneColorService, languageService) {
    super();
    this._editor = editor;
    this._languageService = languageService;
    this._widget = null;
    this._register(this._editor.onDidChangeModel((e) => this.stop()));
    this._register(this._editor.onDidChangeModelLanguage((e) => this.stop()));
    this._register(TokenizationRegistry.onDidChange((e) => this.stop()));
    this._register(this._editor.onKeyUp((e) => e.keyCode === 9 && this.stop()));
  }
  static get(editor) {
    return editor.getContribution(InspectTokensController2.ID);
  }
  dispose() {
    this.stop();
    super.dispose();
  }
  launch() {
    if (this._widget) {
      return;
    }
    if (!this._editor.hasModel()) {
      return;
    }
    this._widget = new InspectTokensWidget(this._editor, this._languageService);
  }
  stop() {
    if (this._widget) {
      this._widget.dispose();
      this._widget = null;
    }
  }
};
InspectTokensController.ID = "editor.contrib.inspectTokens";
InspectTokensController = __decorate([
  __param(1, IStandaloneThemeService),
  __param(2, ILanguageService)
], InspectTokensController);
class InspectTokens extends EditorAction {
  constructor() {
    super({
      id: "editor.action.inspectTokens",
      label: InspectTokensNLS.inspectTokensAction,
      alias: "Developer: Inspect Tokens",
      precondition: void 0
    });
  }
  run(accessor, editor) {
    const controller = InspectTokensController.get(editor);
    if (controller) {
      controller.launch();
    }
  }
}
function renderTokenText(tokenText) {
  let result = "";
  for (let charIndex = 0, len = tokenText.length; charIndex < len; charIndex++) {
    const charCode = tokenText.charCodeAt(charIndex);
    switch (charCode) {
      case 9:
        result += "→";
        break;
      case 32:
        result += "·";
        break;
      default:
        result += String.fromCharCode(charCode);
    }
  }
  return result;
}
function getSafeTokenizationSupport(languageIdCodec, languageId) {
  const tokenizationSupport = TokenizationRegistry.get(languageId);
  if (tokenizationSupport) {
    return tokenizationSupport;
  }
  const encodedLanguageId = languageIdCodec.encodeLanguageId(languageId);
  return {
    getInitialState: () => NullState,
    tokenize: (line, hasEOL, state) => nullTokenize(languageId, state),
    tokenizeEncoded: (line, hasEOL, state) => nullTokenizeEncoded(encodedLanguageId, state)
  };
}
class InspectTokensWidget extends Disposable {
  constructor(editor, languageService) {
    super();
    this.allowEditorOverflow = true;
    this._editor = editor;
    this._languageService = languageService;
    this._model = this._editor.getModel();
    this._domNode = document.createElement("div");
    this._domNode.className = "tokens-inspect-widget";
    this._tokenizationSupport = getSafeTokenizationSupport(this._languageService.languageIdCodec, this._model.getLanguageId());
    this._compute(this._editor.getPosition());
    this._register(this._editor.onDidChangeCursorPosition((e) => this._compute(this._editor.getPosition())));
    this._editor.addContentWidget(this);
  }
  dispose() {
    this._editor.removeContentWidget(this);
    super.dispose();
  }
  getId() {
    return InspectTokensWidget._ID;
  }
  _compute(position) {
    const data = this._getTokensAtLine(position.lineNumber);
    let token1Index = 0;
    for (let i = data.tokens1.length - 1; i >= 0; i--) {
      const t = data.tokens1[i];
      if (position.column - 1 >= t.offset) {
        token1Index = i;
        break;
      }
    }
    let token2Index = 0;
    for (let i = data.tokens2.length >>> 1; i >= 0; i--) {
      if (position.column - 1 >= data.tokens2[i << 1]) {
        token2Index = i;
        break;
      }
    }
    const lineContent = this._model.getLineContent(position.lineNumber);
    let tokenText = "";
    if (token1Index < data.tokens1.length) {
      const tokenStartIndex = data.tokens1[token1Index].offset;
      const tokenEndIndex = token1Index + 1 < data.tokens1.length ? data.tokens1[token1Index + 1].offset : lineContent.length;
      tokenText = lineContent.substring(tokenStartIndex, tokenEndIndex);
    }
    reset(this._domNode, $("h2.tm-token", void 0, renderTokenText(tokenText), $("span.tm-token-length", void 0, `${tokenText.length} ${tokenText.length === 1 ? "char" : "chars"}`)));
    append(this._domNode, $("hr.tokens-inspect-separator", { "style": "clear:both" }));
    const metadata = (token2Index << 1) + 1 < data.tokens2.length ? this._decodeMetadata(data.tokens2[(token2Index << 1) + 1]) : null;
    append(this._domNode, $("table.tm-metadata-table", void 0, $("tbody", void 0, $("tr", void 0, $("td.tm-metadata-key", void 0, "language"), $("td.tm-metadata-value", void 0, `${metadata ? metadata.languageId : "-?-"}`)), $("tr", void 0, $("td.tm-metadata-key", void 0, "token type"), $("td.tm-metadata-value", void 0, `${metadata ? this._tokenTypeToString(metadata.tokenType) : "-?-"}`)), $("tr", void 0, $("td.tm-metadata-key", void 0, "font style"), $("td.tm-metadata-value", void 0, `${metadata ? this._fontStyleToString(metadata.fontStyle) : "-?-"}`)), $("tr", void 0, $("td.tm-metadata-key", void 0, "foreground"), $("td.tm-metadata-value", void 0, `${metadata ? Color.Format.CSS.formatHex(metadata.foreground) : "-?-"}`)), $("tr", void 0, $("td.tm-metadata-key", void 0, "background"), $("td.tm-metadata-value", void 0, `${metadata ? Color.Format.CSS.formatHex(metadata.background) : "-?-"}`)))));
    append(this._domNode, $("hr.tokens-inspect-separator"));
    if (token1Index < data.tokens1.length) {
      append(this._domNode, $("span.tm-token-type", void 0, data.tokens1[token1Index].type));
    }
    this._editor.layoutContentWidget(this);
  }
  _decodeMetadata(metadata) {
    const colorMap = TokenizationRegistry.getColorMap();
    const languageId = TokenMetadata.getLanguageId(metadata);
    const tokenType = TokenMetadata.getTokenType(metadata);
    const fontStyle = TokenMetadata.getFontStyle(metadata);
    const foreground = TokenMetadata.getForeground(metadata);
    const background = TokenMetadata.getBackground(metadata);
    return {
      languageId: this._languageService.languageIdCodec.decodeLanguageId(languageId),
      tokenType,
      fontStyle,
      foreground: colorMap[foreground],
      background: colorMap[background]
    };
  }
  _tokenTypeToString(tokenType) {
    switch (tokenType) {
      case 0:
        return "Other";
      case 1:
        return "Comment";
      case 2:
        return "String";
      case 3:
        return "RegEx";
      default:
        return "??";
    }
  }
  _fontStyleToString(fontStyle) {
    let r = "";
    if (fontStyle & 1) {
      r += "italic ";
    }
    if (fontStyle & 2) {
      r += "bold ";
    }
    if (fontStyle & 4) {
      r += "underline ";
    }
    if (fontStyle & 8) {
      r += "strikethrough ";
    }
    if (r.length === 0) {
      r = "---";
    }
    return r;
  }
  _getTokensAtLine(lineNumber) {
    const stateBeforeLine = this._getStateBeforeLine(lineNumber);
    const tokenizationResult1 = this._tokenizationSupport.tokenize(this._model.getLineContent(lineNumber), true, stateBeforeLine);
    const tokenizationResult2 = this._tokenizationSupport.tokenizeEncoded(this._model.getLineContent(lineNumber), true, stateBeforeLine);
    return {
      startState: stateBeforeLine,
      tokens1: tokenizationResult1.tokens,
      tokens2: tokenizationResult2.tokens,
      endState: tokenizationResult1.endState
    };
  }
  _getStateBeforeLine(lineNumber) {
    let state = this._tokenizationSupport.getInitialState();
    for (let i = 1; i < lineNumber; i++) {
      const tokenizationResult = this._tokenizationSupport.tokenize(this._model.getLineContent(i), true, state);
      state = tokenizationResult.endState;
    }
    return state;
  }
  getDomNode() {
    return this._domNode;
  }
  getPosition() {
    return {
      position: this._editor.getPosition(),
      preference: [
        2,
        1
        /* ABOVE */
      ]
    };
  }
}
InspectTokensWidget._ID = "editor.contrib.inspectTokensWidget";
registerEditorContribution(InspectTokensController.ID, InspectTokensController);
registerEditorAction(InspectTokens);
registerThemingParticipant((theme, collector) => {
  const border = theme.getColor(editorHoverBorder);
  if (border) {
    const borderWidth = theme.type === ColorScheme.HIGH_CONTRAST ? 2 : 1;
    collector.addRule(`.monaco-editor .tokens-inspect-widget { border: ${borderWidth}px solid ${border}; }`);
    collector.addRule(`.monaco-editor .tokens-inspect-widget .tokens-inspect-separator { background-color: ${border}; }`);
  }
  const background = theme.getColor(editorHoverBackground);
  if (background) {
    collector.addRule(`.monaco-editor .tokens-inspect-widget { background-color: ${background}; }`);
  }
  const foreground = theme.getColor(editorHoverForeground);
  if (foreground) {
    collector.addRule(`.monaco-editor .tokens-inspect-widget { color: ${foreground}; }`);
  }
});
