"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const main = require("../vscode-languageserver-types/lib-esm-main.cjs");
const regexpUtils = require("./lib-utils-regexp-utils.cjs");
require("./lib-utils-uri-utils.cjs");
const index = require("../vscode-uri/lib-esm-index.cjs");
function parseJSDoc(node, start, options) {
  let opts;
  let position;
  if (typeof node === "string") {
    position = start;
    opts = options;
  } else {
    position = node.range.start;
    opts = start;
  }
  if (!position) {
    position = main.Position.create(0, 0);
  }
  const lines = getLines(node);
  const normalizedOptions = normalizeOptions(opts);
  const tokens = tokenize({
    lines,
    position,
    options: normalizedOptions
  });
  return parseJSDocComment({
    index: 0,
    tokens,
    position
  });
}
function isJSDoc(node, options) {
  const normalizedOptions = normalizeOptions(options);
  const lines = getLines(node);
  if (lines.length === 0) {
    return false;
  }
  const first = lines[0];
  const last = lines[lines.length - 1];
  const firstRegex = normalizedOptions.start;
  const lastRegex = normalizedOptions.end;
  return Boolean(firstRegex === null || firstRegex === void 0 ? void 0 : firstRegex.exec(first)) && Boolean(lastRegex === null || lastRegex === void 0 ? void 0 : lastRegex.exec(last));
}
function getLines(node) {
  let content = "";
  if (typeof node === "string") {
    content = node;
  } else {
    content = node.text;
  }
  const lines = content.split(regexpUtils.NEWLINE_REGEXP);
  return lines;
}
const tagRegex = /\s*(@([\p{L}][\p{L}\p{N}]*)?)/uy;
const inlineTagRegex = /\{(@[\p{L}][\p{L}\p{N}]*)(\s*)([^\r\n}]+)?\}/gu;
function tokenize(context) {
  var _a, _b, _c;
  const tokens = [];
  let currentLine = context.position.line;
  let currentCharacter = context.position.character;
  for (let i = 0; i < context.lines.length; i++) {
    const first = i === 0;
    const last = i === context.lines.length - 1;
    let line = context.lines[i];
    let index2 = 0;
    if (first && context.options.start) {
      const match = (_a = context.options.start) === null || _a === void 0 ? void 0 : _a.exec(line);
      if (match) {
        index2 = match.index + match[0].length;
      }
    } else {
      const match = (_b = context.options.line) === null || _b === void 0 ? void 0 : _b.exec(line);
      if (match) {
        index2 = match.index + match[0].length;
      }
    }
    if (last) {
      const match = (_c = context.options.end) === null || _c === void 0 ? void 0 : _c.exec(line);
      if (match) {
        line = line.substring(0, match.index);
      }
    }
    line = line.substring(0, lastCharacter(line));
    const whitespaceEnd = skipWhitespace(line, index2);
    if (whitespaceEnd >= line.length) {
      if (tokens.length > 0) {
        const position = main.Position.create(currentLine, currentCharacter);
        tokens.push({
          type: "break",
          content: "",
          range: main.Range.create(position, position)
        });
      }
    } else {
      tagRegex.lastIndex = index2;
      const tagMatch = tagRegex.exec(line);
      if (tagMatch) {
        const fullMatch = tagMatch[0];
        const value = tagMatch[1];
        const start = main.Position.create(currentLine, currentCharacter + index2);
        const end = main.Position.create(currentLine, currentCharacter + index2 + fullMatch.length);
        tokens.push({
          type: "tag",
          content: value,
          range: main.Range.create(start, end)
        });
        index2 += fullMatch.length;
        index2 = skipWhitespace(line, index2);
      }
      if (index2 < line.length) {
        const rest = line.substring(index2);
        const inlineTagMatches = Array.from(rest.matchAll(inlineTagRegex));
        tokens.push(...buildInlineTokens(inlineTagMatches, rest, currentLine, currentCharacter + index2));
      }
    }
    currentLine++;
    currentCharacter = 0;
  }
  if (tokens.length > 0 && tokens[tokens.length - 1].type === "break") {
    return tokens.slice(0, -1);
  }
  return tokens;
}
function buildInlineTokens(tags, line, lineIndex, characterIndex) {
  const tokens = [];
  if (tags.length === 0) {
    const start = main.Position.create(lineIndex, characterIndex);
    const end = main.Position.create(lineIndex, characterIndex + line.length);
    tokens.push({
      type: "text",
      content: line,
      range: main.Range.create(start, end)
    });
  } else {
    let lastIndex = 0;
    for (const match of tags) {
      const matchIndex = match.index;
      const startContent = line.substring(lastIndex, matchIndex);
      if (startContent.length > 0) {
        tokens.push({
          type: "text",
          content: line.substring(lastIndex, matchIndex),
          range: main.Range.create(main.Position.create(lineIndex, lastIndex + characterIndex), main.Position.create(lineIndex, matchIndex + characterIndex))
        });
      }
      let offset = startContent.length + 1;
      const tagName = match[1];
      tokens.push({
        type: "inline-tag",
        content: tagName,
        range: main.Range.create(main.Position.create(lineIndex, lastIndex + offset + characterIndex), main.Position.create(lineIndex, lastIndex + offset + tagName.length + characterIndex))
      });
      offset += tagName.length;
      if (match.length === 4) {
        offset += match[2].length;
        const value = match[3];
        tokens.push({
          type: "text",
          content: value,
          range: main.Range.create(main.Position.create(lineIndex, lastIndex + offset + characterIndex), main.Position.create(lineIndex, lastIndex + offset + value.length + characterIndex))
        });
      } else {
        tokens.push({
          type: "text",
          content: "",
          range: main.Range.create(main.Position.create(lineIndex, lastIndex + offset + characterIndex), main.Position.create(lineIndex, lastIndex + offset + characterIndex))
        });
      }
      lastIndex = matchIndex + match[0].length;
    }
    const endContent = line.substring(lastIndex);
    if (endContent.length > 0) {
      tokens.push({
        type: "text",
        content: endContent,
        range: main.Range.create(main.Position.create(lineIndex, lastIndex + characterIndex), main.Position.create(lineIndex, lastIndex + characterIndex + endContent.length))
      });
    }
  }
  return tokens;
}
const nonWhitespaceRegex = /\S/;
const whitespaceEndRegex = /\s*$/;
function skipWhitespace(line, index2) {
  const match = line.substring(index2).match(nonWhitespaceRegex);
  if (match) {
    return index2 + match.index;
  } else {
    return line.length;
  }
}
function lastCharacter(line) {
  const match = line.match(whitespaceEndRegex);
  if (match && typeof match.index === "number") {
    return match.index;
  }
  return void 0;
}
function parseJSDocComment(context) {
  var _a, _b, _c, _d;
  const startPosition = main.Position.create(context.position.line, context.position.character);
  if (context.tokens.length === 0) {
    return new JSDocCommentImpl([], main.Range.create(startPosition, startPosition));
  }
  const elements = [];
  while (context.index < context.tokens.length) {
    const element = parseJSDocElement(context, elements[elements.length - 1]);
    if (element) {
      elements.push(element);
    }
  }
  const start = (_b = (_a = elements[0]) === null || _a === void 0 ? void 0 : _a.range.start) !== null && _b !== void 0 ? _b : startPosition;
  const end = (_d = (_c = elements[elements.length - 1]) === null || _c === void 0 ? void 0 : _c.range.end) !== null && _d !== void 0 ? _d : startPosition;
  return new JSDocCommentImpl(elements, main.Range.create(start, end));
}
function parseJSDocElement(context, last) {
  const next = context.tokens[context.index];
  if (next.type === "tag") {
    return parseJSDocTag(context, false);
  } else if (next.type === "text" || next.type === "inline-tag") {
    return parseJSDocText(context);
  } else {
    appendEmptyLine(next, last);
    context.index++;
    return void 0;
  }
}
function appendEmptyLine(token, element) {
  if (element) {
    const line = new JSDocLineImpl("", token.range);
    if ("inlines" in element) {
      element.inlines.push(line);
    } else {
      element.content.inlines.push(line);
    }
  }
}
function parseJSDocText(context) {
  let token = context.tokens[context.index];
  const firstToken = token;
  let lastToken = token;
  const lines = [];
  while (token && token.type !== "break" && token.type !== "tag") {
    lines.push(parseJSDocInline(context));
    lastToken = token;
    token = context.tokens[context.index];
  }
  return new JSDocTextImpl(lines, main.Range.create(firstToken.range.start, lastToken.range.end));
}
function parseJSDocInline(context) {
  const token = context.tokens[context.index];
  if (token.type === "inline-tag") {
    return parseJSDocTag(context, true);
  } else {
    return parseJSDocLine(context);
  }
}
function parseJSDocTag(context, inline) {
  const tagToken = context.tokens[context.index++];
  const name = tagToken.content.substring(1);
  const nextToken = context.tokens[context.index];
  if ((nextToken === null || nextToken === void 0 ? void 0 : nextToken.type) === "text") {
    if (inline) {
      const docLine = parseJSDocLine(context);
      return new JSDocTagImpl(name, new JSDocTextImpl([docLine], docLine.range), inline, main.Range.create(tagToken.range.start, docLine.range.end));
    } else {
      const textDoc = parseJSDocText(context);
      return new JSDocTagImpl(name, textDoc, inline, main.Range.create(tagToken.range.start, textDoc.range.end));
    }
  } else {
    const range = tagToken.range;
    return new JSDocTagImpl(name, new JSDocTextImpl([], range), inline, range);
  }
}
function parseJSDocLine(context) {
  const token = context.tokens[context.index++];
  return new JSDocLineImpl(token.content, token.range);
}
function normalizeOptions(options) {
  if (!options) {
    return normalizeOptions({
      start: "/**",
      end: "*/",
      line: "*"
    });
  }
  const { start, end, line } = options;
  return {
    start: normalizeOption(start, true),
    end: normalizeOption(end, false),
    line: normalizeOption(line, true)
  };
}
function normalizeOption(option, start) {
  if (typeof option === "string" || typeof option === "object") {
    const escaped = typeof option === "string" ? regexpUtils.escapeRegExp(option) : option.source;
    if (start) {
      return new RegExp(`^\\s*${escaped}`);
    } else {
      return new RegExp(`\\s*${escaped}\\s*$`);
    }
  } else {
    return option;
  }
}
class JSDocCommentImpl {
  constructor(elements, range) {
    this.elements = elements;
    this.range = range;
  }
  getTag(name) {
    return this.getAllTags().find((e) => e.name === name);
  }
  getTags(name) {
    return this.getAllTags().filter((e) => e.name === name);
  }
  getAllTags() {
    return this.elements.filter((e) => "name" in e);
  }
  toString() {
    let value = "";
    for (const element of this.elements) {
      if (value.length === 0) {
        value = element.toString();
      } else {
        const text = element.toString();
        value += fillNewlines(value) + text;
      }
    }
    return value.trim();
  }
  toMarkdown(options) {
    let value = "";
    for (const element of this.elements) {
      if (value.length === 0) {
        value = element.toMarkdown(options);
      } else {
        const text = element.toMarkdown(options);
        value += fillNewlines(value) + text;
      }
    }
    return value.trim();
  }
}
class JSDocTagImpl {
  constructor(name, content, inline, range) {
    this.name = name;
    this.content = content;
    this.inline = inline;
    this.range = range;
  }
  toString() {
    let text = `@${this.name}`;
    const content = this.content.toString();
    if (this.content.inlines.length === 1) {
      text = `${text} ${content}`;
    } else if (this.content.inlines.length > 1) {
      text = `${text}
${content}`;
    }
    if (this.inline) {
      return `{${text}}`;
    } else {
      return text;
    }
  }
  toMarkdown(options) {
    var _a, _b;
    return (_b = (_a = options === null || options === void 0 ? void 0 : options.renderTag) === null || _a === void 0 ? void 0 : _a.call(options, this)) !== null && _b !== void 0 ? _b : this.toMarkdownDefault(options);
  }
  toMarkdownDefault(options) {
    const content = this.content.toMarkdown(options);
    if (this.inline) {
      const rendered = renderInlineTag(this.name, content, options !== null && options !== void 0 ? options : {});
      if (typeof rendered === "string") {
        return rendered;
      }
    }
    let marker = "";
    if ((options === null || options === void 0 ? void 0 : options.tag) === "italic" || (options === null || options === void 0 ? void 0 : options.tag) === void 0) {
      marker = "*";
    } else if ((options === null || options === void 0 ? void 0 : options.tag) === "bold") {
      marker = "**";
    } else if ((options === null || options === void 0 ? void 0 : options.tag) === "bold-italic") {
      marker = "***";
    }
    let text = `${marker}@${this.name}${marker}`;
    if (this.content.inlines.length === 1) {
      text = `${text} — ${content}`;
    } else if (this.content.inlines.length > 1) {
      text = `${text}
${content}`;
    }
    if (this.inline) {
      return `{${text}}`;
    } else {
      return text;
    }
  }
}
function renderInlineTag(tag, content, options) {
  var _a, _b;
  if (tag === "linkplain" || tag === "linkcode" || tag === "link") {
    const index2 = content.indexOf(" ");
    let display = content;
    if (index2 > 0) {
      const displayStart = skipWhitespace(content, index2);
      display = content.substring(displayStart);
      content = content.substring(0, index2);
    }
    if (tag === "linkcode" || tag === "link" && options.link === "code") {
      display = `\`${display}\``;
    }
    const renderedLink = (_b = (_a = options.renderLink) === null || _a === void 0 ? void 0 : _a.call(options, content, display)) !== null && _b !== void 0 ? _b : renderLinkDefault(content, display);
    return renderedLink;
  }
  return void 0;
}
function renderLinkDefault(content, display) {
  try {
    index.URI.parse(content, true);
    return `[${display}](${content})`;
  } catch (_a) {
    return content;
  }
}
class JSDocTextImpl {
  constructor(lines, range) {
    this.inlines = lines;
    this.range = range;
  }
  toString() {
    let text = "";
    for (let i = 0; i < this.inlines.length; i++) {
      const inline = this.inlines[i];
      const next = this.inlines[i + 1];
      text += inline.toString();
      if (next && next.range.start.line > inline.range.start.line) {
        text += "\n";
      }
    }
    return text;
  }
  toMarkdown(options) {
    let text = "";
    for (let i = 0; i < this.inlines.length; i++) {
      const inline = this.inlines[i];
      const next = this.inlines[i + 1];
      text += inline.toMarkdown(options);
      if (next && next.range.start.line > inline.range.start.line) {
        text += "\n";
      }
    }
    return text;
  }
}
class JSDocLineImpl {
  constructor(text, range) {
    this.text = text;
    this.range = range;
  }
  toString() {
    return this.text;
  }
  toMarkdown() {
    return this.text;
  }
}
function fillNewlines(text) {
  if (text.endsWith("\n")) {
    return "\n";
  } else {
    return "\n\n";
  }
}
exports.isJSDoc = isJSDoc;
exports.parseJSDoc = parseJSDoc;
//# sourceMappingURL=lib-documentation-jsdoc.cjs.map
