// The formatter intentionally operates on partially written source. Its small
// scanner values are dynamically shaped, while the public API below remains typed.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck

export interface FormatWrnOptions {
  insertSpaces?: boolean;
  tabSize?: number;
  printWidth?: number;
  multilineAttributes?: boolean;
}

const VOID_ELEMENTS = new Set([
  "area",
  "base",
  "br",
  "col",
  "embed",
  "hr",
  "img",
  "input",
  "link",
  "meta",
  "param",
  "source",
  "track",
  "wbr",
]);

function splitPropDeclarations(value) {
  const declarations = [];
  let start = 0;
  let index = 0;
  let quote = null;
  let escaped = false;
  let square = 0;
  let brace = 0;
  let paren = 0;
  let segmentHasColon = false;
  let segmentHasEquals = false;

  const isIdentifierStart = (character) => /[A-Za-z_]/.test(character || "");
  const isIdentifierPart = (character) => /[A-Za-z0-9_]/.test(character || "");

  const beginsDeclaration = (position) => {
    let cursor = position;
    while (cursor < value.length && /[ \t]/.test(value[cursor])) cursor += 1;
    if (value.slice(cursor).startsWith("@event")) {
      cursor += "@event".length;
      if (!/\s/.test(value[cursor] || "")) return false;
      while (cursor < value.length && /\s/.test(value[cursor])) cursor += 1;
      if (!isIdentifierStart(value[cursor])) return false;
      cursor += 1;
      while (cursor < value.length && isIdentifierPart(value[cursor])) cursor += 1;
      while (cursor < value.length && /[ \t]/.test(value[cursor])) cursor += 1;
      return value[cursor] === "=" ? "=" : null;
    }
    if (!isIdentifierStart(value[cursor])) return false;
    cursor += 1;
    while (cursor < value.length && isIdentifierPart(value[cursor])) cursor += 1;
    if (value[cursor] === "?") cursor += 1;
    while (cursor < value.length && /[ \t]/.test(value[cursor])) cursor += 1;
    return value[cursor] === "=" || value[cursor] === ":" ? value[cursor] : null;
  };

  while (index < value.length) {
    const character = value[index];

    if (quote !== null) {
      if (escaped) escaped = false;
      else if (character === "\\") escaped = true;
      else if (character === quote) quote = null;
      index += 1;
      continue;
    }

    if (character === '"' || character === "'" || character === "`") {
      quote = character;
      index += 1;
      continue;
    }

    if (character === "[") square += 1;
    else if (character === "]" && square > 0) square -= 1;
    else if (character === "{") brace += 1;
    else if (character === "}" && brace > 0) brace -= 1;
    else if (character === "(") paren += 1;
    else if (character === ")" && paren > 0) paren -= 1;

    const topLevel = square === 0 && brace === 0 && paren === 0;
    if (topLevel && character === ":") segmentHasColon = true;
    if (topLevel && character === "=") segmentHasEquals = true;

    const candidateDelimiter = topLevel && /\s/.test(character) ? beginsDeclaration(index) : null;
    const beginsNext =
      candidateDelimiter === ":" ||
      (candidateDelimiter === "=" && (segmentHasEquals || !segmentHasColon));

    if (
      topLevel &&
      /\s/.test(character) &&
      value.slice(start, index).trim() !== "@event" &&
      beginsNext
    ) {
      const declaration = value.slice(start, index).trim();
      if (declaration) declarations.push(declaration);
      while (index < value.length && /\s/.test(value[index])) index += 1;
      start = index;
      segmentHasColon = false;
      segmentHasEquals = false;
      continue;
    }

    index += 1;
  }

  const declaration = value.slice(start).trim();
  if (declaration) declarations.push(declaration);
  return declarations;
}

function splitOutputDeclarations(value) {
  const declarations = [];
  let start = 0;
  let paren = 0;
  let angle = 0;
  let square = 0;
  let quote = null;
  let escaped = false;
  const startsOutput = (position) => {
    let cursor = position;
    while (cursor < value.length && /\s/.test(value[cursor])) cursor += 1;
    if (!/[A-Za-z_$]/.test(value[cursor] || "")) return false;
    cursor += 1;
    while (cursor < value.length && /[A-Za-z0-9_$]/.test(value[cursor] || "")) cursor += 1;
    while (cursor < value.length && /\s/.test(value[cursor])) cursor += 1;
    return value[cursor] === "(";
  };
  for (let index = 0; index < value.length; index += 1) {
    const character = value[index];
    if (quote !== null) {
      if (escaped) escaped = false;
      else if (character === "\\") escaped = true;
      else if (character === quote) quote = null;
      continue;
    }
    if (character === '"' || character === "'" || character === "`") {
      quote = character;
      continue;
    }
    if (character === "(") paren += 1;
    else if (character === ")" && paren > 0) paren -= 1;
    else if (character === "[") square += 1;
    else if (character === "]" && square > 0) square -= 1;
    else if (character === "<") angle += 1;
    else if (character === ">" && angle > 0) angle -= 1;
    if (paren === 0 && square === 0 && angle === 0 && /\s/.test(character) && startsOutput(index)) {
      const declaration = value.slice(start, index).trim();
      if (declaration) declarations.push(declaration);
      while (index < value.length && /\s/.test(value[index])) index += 1;
      start = index;
      index -= 1;
    }
  }
  const finalDeclaration = value.slice(start).trim();
  if (finalDeclaration) declarations.push(finalDeclaration);
  return declarations;
}

function formatInlineDeclarationBlock(value, unit, depth) {
  const match = /^(props|state|computed|outputs)\s*\{([\s\S]*)\}$/.exec(value.trim());
  if (!match) return null;
  const declarations =
    match[1] === "outputs"
      ? splitOutputDeclarations(match[2].trim())
      : splitPropDeclarations(match[2].trim());
  return [
    `${unit.repeat(depth)}${match[1]} {`,
    ...declarations.map((declaration) => `${unit.repeat(depth + 1)}${declaration}`),
    `${unit.repeat(depth)}}`,
  ];
}

function formatInlinePropsBlock(value, unit, depth) {
  const match = /^props\s*\{([\s\S]*)\}$/.exec(value.trim());
  if (!match) return null;

  const declarations = splitPropDeclarations(match[1].trim());
  if (declarations.length === 0) {
    return [`${unit.repeat(depth)}props {`, `${unit.repeat(depth)}}`];
  }

  return [
    `${unit.repeat(depth)}props {`,
    ...declarations.map((declaration) => `${unit.repeat(depth + 1)}${declaration}`),
    `${unit.repeat(depth)}}`,
  ];
}

function findOpeningTagEnd(value) {
  let quote = null;
  let escaped = false;

  for (let index = 0; index < value.length; index += 1) {
    const character = value[index];

    if (quote !== null) {
      if (escaped) {
        escaped = false;
        continue;
      }

      if (character === "\\") {
        escaped = true;
        continue;
      }

      if (character === quote) {
        quote = null;
      }

      continue;
    }

    if (character === '"' || character === "'") {
      quote = character;
      continue;
    }

    if (character === ">") {
      return index;
    }
  }

  return -1;
}

function parseAttributes(value) {
  const attributes = [];
  let index = 0;

  while (index < value.length) {
    while (index < value.length && /\s/.test(value[index])) index += 1;
    if (index >= value.length) break;

    const start = index;
    while (index < value.length && !/[\s=]/.test(value[index])) index += 1;
    while (index < value.length && /\s/.test(value[index])) index += 1;

    if (value[index] === "=") {
      index += 1;
      while (index < value.length && /\s/.test(value[index])) index += 1;

      const quote = value[index];
      if (quote === '"' || quote === "'") {
        index += 1;
        let escaped = false;
        while (index < value.length) {
          const character = value[index++];
          if (escaped) escaped = false;
          else if (character === "\\") escaped = true;
          else if (character === quote) break;
        }
      } else if (value[index] === "{") {
        let depth = 0;
        let expressionQuote = null;
        let escaped = false;
        while (index < value.length) {
          const character = value[index++];
          if (expressionQuote !== null) {
            if (escaped) escaped = false;
            else if (character === "\\") escaped = true;
            else if (character === expressionQuote) expressionQuote = null;
            continue;
          }
          if (character === '"' || character === "'" || character === "`") {
            expressionQuote = character;
          } else if (character === "{") {
            depth += 1;
          } else if (character === "}" && --depth === 0) {
            break;
          }
        }
      } else {
        while (index < value.length && !/\s/.test(value[index])) index += 1;
      }
    }

    const attribute = value.slice(start, index).trim();
    if (attribute) attributes.push(attribute);
  }

  return attributes;
}

function parseStructuredAttribute(attribute) {
  const match = /^([^\s=]+)\s*=\s*\{([\s\S]*)\}$/.exec(attribute);
  if (!match) return null;

  const expression = match[2].trim();
  if (!expression.startsWith("[") && !expression.startsWith("{")) return null;

  try {
    return {
      name: match[1],
      value: JSON.parse(expression),
    };
  } catch {
    return null;
  }
}

function formatAttribute(attribute, indentation, unit) {
  const structured = parseStructuredAttribute(attribute);
  if (!structured) return [`${indentation}${attribute}`];

  const jsonLines = JSON.stringify(structured.value, null, unit).split("\n");
  if (jsonLines.length === 1) {
    return [`${indentation}${structured.name}={${jsonLines[0]}}`];
  }

  return [
    `${indentation}${structured.name}={${jsonLines[0]}`,
    ...jsonLines.slice(1, -1).map((line) => `${indentation}${line}`),
    `${indentation}${jsonLines.at(-1)}}`,
  ];
}

function escapeRegExp(value) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

function parseOpeningTag(value) {
  const endIndex = findOpeningTagEnd(value);

  if (endIndex === -1) {
    return null;
  }

  const openingPart = value.slice(0, endIndex + 1);
  const remainder = value.slice(endIndex + 1).trim();

  const match = /^<([A-Za-z][\w$:.-]*)([\s\S]*?)(\/?)>$/.exec(openingPart);

  if (!match) {
    return null;
  }

  const tagName = match[1];
  const attributes = parseAttributes(match[2].trim());
  const selfClosing = match[3] === "/";

  const escapedTagName = escapeRegExp(tagName);

  const immediateClosing = new RegExp(`^<\\/${escapedTagName}\\s*>`, "i").test(remainder);

  const trailingClosingMatch = new RegExp(`^([\\s\\S]*?)<\\/${escapedTagName}\\s*>$`, "i").exec(
    remainder,
  );

  const trailingClosing = trailingClosingMatch !== null;

  const inlineContent = trailingClosing ? trailingClosingMatch[1].trim() : "";

  const inlineClosing = trailingClosing && inlineContent.length === 0;

  const closesInRemainder = immediateClosing || trailingClosing;

  return {
    tagName,
    attributes,
    selfClosing,
    inlineClosing,
    immediateClosing,
    trailingClosing,
    inlineContent,
    closesInRemainder,
    remainder,
  };
}

function formatOpeningTag(value, unit, depth, printWidth = 100, multilineAttributes = true) {
  const parsed = parseOpeningTag(value);

  if (!parsed) {
    return {
      lines: [`${unit.repeat(depth)}${value.trim()}`],
      opensElement: false,
    };
  }

  const baseIndent = unit.repeat(depth);
  const childIndent = unit.repeat(depth + 1);

  const normalizedOpening =
    `<${parsed.tagName}` +
    `${parsed.attributes.length ? ` ${parsed.attributes.join(" ")}` : ""}` +
    `${parsed.selfClosing ? " /" : ""}>`;

  const normalizedSingleLine = `${normalizedOpening}${parsed.remainder}`;

  const shouldBreak =
    value.includes("\n") ||
    (multilineAttributes && parsed.attributes.length > 0) ||
    baseIndent.length + normalizedSingleLine.length > printWidth;

  const opensElement =
    !parsed.selfClosing &&
    !parsed.closesInRemainder &&
    !VOID_ELEMENTS.has(parsed.tagName.toLowerCase());

  if (!shouldBreak) {
    return {
      lines: [`${baseIndent}${normalizedSingleLine}`],
      opensElement,
    };
  }

  if (parsed.attributes.length === 0 && !parsed.selfClosing) {
    const lines = [`${baseIndent}<${parsed.tagName}>`];
    if (parsed.trailingClosing) {
      if (parsed.inlineContent) lines.push(`${childIndent}${parsed.inlineContent}`);
      lines.push(`${baseIndent}</${parsed.tagName}>`);
    } else if (parsed.remainder) {
      lines.push(`${childIndent}${parsed.remainder}`);
    }
    return { lines, opensElement };
  }

  const lines = [
    `${baseIndent}<${parsed.tagName}`,
    ...parsed.attributes.flatMap((attribute) => formatAttribute(attribute, childIndent, unit)),
  ];

  if (parsed.selfClosing) {
    lines.push(`${baseIndent}/>`);
    return {
      lines,
      opensElement,
    };
  }

  lines.push(`${baseIndent}>`);

  if (parsed.trailingClosing) {
    if (parsed.inlineContent) {
      lines.push(`${childIndent}${parsed.inlineContent}`);
    }

    lines.push(`${baseIndent}</${parsed.tagName}>`);
  } else if (parsed.remainder) {
    lines.push(`${parsed.immediateClosing ? baseIndent : childIndent}${parsed.remainder}`);
  }

  return {
    lines,
    opensElement,
  };
}

function isMultilineOpeningTagStart(value) {
  if (!value.startsWith("<")) {
    return false;
  }

  if (
    value.startsWith("</") ||
    value.startsWith("<!--") ||
    value.startsWith("<!") ||
    value.startsWith("<?")
  ) {
    return false;
  }

  return findOpeningTagEnd(value) === -1;
}

function isClosingTag(value) {
  return /^<\/[A-Za-z][\w$:.-]*\s*>/.test(value);
}

/**
 * Preserve compact, already-balanced HTML fragments as one line.
 *
 * A line such as `<span>Page</span><b>→</b><span>API</span>` is valid and
 * intentionally compact. Expanding only its first opening tag makes later
 * formatter passes treat the remaining siblings as children, causing runaway
 * indentation and false closing-tag diagnostics. Balanced fragments are kept
 * intact while normal multiline opening tags continue through the formatter.
 */
function isBalancedInlineHtmlFragment(value) {
  if (!value.startsWith("<") || value.startsWith("</") || value.startsWith("<!--")) {
    return false;
  }

  const stack = [];
  let tagCount = 0;
  let rootCount = 0;
  let hasNestedElement = false;
  let hasOutsideText = false;
  let index = 0;

  while (index < value.length) {
    const tagStart = value.indexOf("<", index);
    if (tagStart === -1) {
      if (stack.length === 0 && value.slice(index).trim()) hasOutsideText = true;
      break;
    }
    if (stack.length === 0 && value.slice(index, tagStart).trim()) hasOutsideText = true;

    if (value.startsWith("<!--", tagStart)) {
      const commentEnd = value.indexOf("-->", tagStart + 4);
      if (commentEnd === -1) return false;
      index = commentEnd + 3;
      continue;
    }

    const relativeEnd = findOpeningTagEnd(value.slice(tagStart));
    if (relativeEnd === -1) return false;
    const tag = value.slice(tagStart, tagStart + relativeEnd + 1);
    const match = /^<\/?([A-Za-z][\w$:.-]*)[\s\S]*?>$/.exec(tag);
    if (!match) {
      index = tagStart + 1;
      continue;
    }

    tagCount += 1;
    const tagName = match[1];
    const normalizedName = /^[a-z]/.test(tagName) ? tagName.toLowerCase() : tagName;
    const closing = tag.startsWith("</");
    const selfClosing = /\/\s*>$/.test(tag);
    const voidElement = VOID_ELEMENTS.has(tagName.toLowerCase());

    if (closing) {
      if (stack.at(-1) !== normalizedName) return false;
      stack.pop();
    } else {
      if (stack.length === 0) rootCount += 1;
      else hasNestedElement = true;
      if (!selfClosing && !voidElement) stack.push(normalizedName);
    }

    index = tagStart + relativeEnd + 1;
  }

  return (
    tagCount >= 2 && stack.length === 0 && (rootCount > 1 || hasNestedElement || hasOutsideText)
  );
}

function isControlBlockOpen(value) {
  return /^\{#(?:if|each)\b[\s\S]*\}$/.test(value);
}

function isControlBlockMiddle(value) {
  return /^\{:(?:else(?:\s+if\b[\s\S]*)?|empty)\}$/.test(value);
}

function isControlBlockClose(value) {
  return /^\{\/(?:if|each)\}$/.test(value);
}

function countLeadingClosingBraces(value) {
  let index = 0;
  let count = 0;

  while (index < value.length) {
    while (index < value.length && /\s/.test(value[index])) {
      index += 1;
    }

    if (value[index] !== "}" && value[index] !== "]") {
      break;
    }

    count += 1;
    index += 1;
  }

  return count;
}

/**
 * Count braces outside strings and HTML comments.
 *
 * This supports WRN blocks, function bodies, lifecycle hooks,
 * watcher bodies and multiline JavaScript object literals.
 */
function countStructuralBraces(value) {
  let openings = 0;
  let closings = 0;

  let quote = null;
  let escaped = false;
  let htmlComment = false;

  for (let index = 0; index < value.length; index += 1) {
    if (!quote && !htmlComment && value.startsWith("<!--", index)) {
      htmlComment = true;
      index += 3;
      continue;
    }

    if (htmlComment && value.startsWith("-->", index)) {
      htmlComment = false;
      index += 2;
      continue;
    }

    if (htmlComment) {
      continue;
    }

    const character = value[index];

    if (quote !== null) {
      if (escaped) {
        escaped = false;
        continue;
      }

      if (character === "\\") {
        escaped = true;
        continue;
      }

      if (character === quote) {
        quote = null;
      }

      continue;
    }

    if (character === '"' || character === "'" || character === "`") {
      quote = character;
      continue;
    }

    if (character === "{") {
      openings += 1;
    } else if (character === "}") {
      closings += 1;
    } else if (character === "[") {
      openings += 1;
    } else if (character === "]") {
      closings += 1;
    }
  }

  return {
    openings,
    closings,
  };
}

function collectOpeningTag(inputLines, startIndex) {
  const collected = [inputLines[startIndex].trim()];

  let index = startIndex;

  while (index + 1 < inputLines.length) {
    const joined = collected.join(" ");

    if (findOpeningTagEnd(joined) !== -1) {
      break;
    }

    index += 1;
    collected.push(inputLines[index].trim());
  }

  return {
    // Preserve the fact that the opening tag was already multiline so a
    // second formatter pass cannot collapse it back to one line.
    value: collected.join("\n"),
    endIndex: index,
  };
}

function isPreservedRawBlockStart(value) {
  return (
    /<pre(?:\s|>)/i.test(value) &&
    !/<\/pre\s*>/i.test(value.slice(0, value.search(/<pre(?:\s|>)/i)))
  );
}

function hasPreservedRawBlockEnd(value) {
  return /<\/pre\s*>/i.test(value);
}

function transformOutsidePreservedRawBlocks(lines, transformLine) {
  const output = [];
  let preserving = false;

  for (const line of lines) {
    if (preserving) {
      output.push(line);
      if (hasPreservedRawBlockEnd(line)) preserving = false;
      continue;
    }

    if (isPreservedRawBlockStart(line)) {
      output.push(line);
      preserving = !hasPreservedRawBlockEnd(line);
      continue;
    }

    output.push(...transformLine(line));
  }

  return output;
}

function collectPreservedRawBlock(lines, startIndex) {
  const collected = [lines[startIndex]];
  let index = startIndex;
  while (!hasPreservedRawBlockEnd(collected.at(-1) || "") && index + 1 < lines.length) {
    index += 1;
    collected.push(lines[index]);
  }
  return { lines: collected, endIndex: index };
}

/**
 * Put WRN template control markers on their own lines before indentation.
 *
 * Authors commonly write compact fragments such as
 * `{#if loading}<span>…</span>{/if}`. Treating that as one line prevents the
 * normal HTML and control-block formatters from seeing its structure.
 */
function expandInlineControlBlocks(lines) {
  const marker =
    /(\{#(?:if|each)\b[^}]*\}|\{:(?:else(?:\s+if\b[^}]*)?|empty)\}|\{\/(?:if|each)\})/g;

  return transformOutsidePreservedRawBlocks(lines, (line) => {
    if (!marker.test(line)) return [line];
    marker.lastIndex = 0;

    const indentation = line.match(/^\s*/)?.[0] ?? "";
    const segments = line
      .split(marker)
      .map((segment) => segment.trim())
      .filter(Boolean);

    return segments.map((segment) => `${indentation}${segment}`);
  });
}

function expandStructuredStateDeclarations(lines, unit) {
  return transformOutsidePreservedRawBlocks(lines, (line) => {
    const match = /^(\s*state\s+[A-Za-z_$][\w$]*\s*=\s*)([\\[{][\s\S]*)$/.exec(line);
    if (!match) return [line];

    try {
      const parsed = JSON.parse(match[2].trim());
      const jsonLines = JSON.stringify(parsed, null, unit).split("\n");
      if (jsonLines.length === 1) return [`${match[1]}${jsonLines[0]}`];

      const leading = match[1].match(/^\s*/)?.[0] ?? "";
      return [
        `${match[1]}${jsonLines[0]}`,
        ...jsonLines.slice(1).map((jsonLine) => `${leading}${jsonLine}`),
      ];
    } catch {
      return [line];
    }
  });
}

function formatWrnPass(source: string, options: FormatWrnOptions = {}): string {
  const unit = options.insertSpaces === false ? "\t" : " ".repeat(options.tabSize ?? 4);

  const printWidth = options.printWidth ?? 100;

  const multilineAttributes = options.multilineAttributes !== false;

  let codeDepth = 0;
  let htmlDepth = 0;
  let controlDepth = 0;
  let index = 0;

  const sourceLines = source.replace(/\r\n/g, "\n").split("\n");
  const inputLines = expandInlineControlBlocks(
    expandStructuredStateDeclarations(sourceLines, unit),
  );

  const output = [];

  let previousWasBlank = false;

  while (index < inputLines.length) {
    const originalLine = inputLines[index];

    let value = originalLine.trim();

    if (value === "") {
      if (!previousWasBlank && output.length > 0) {
        output.push("");
      }

      previousWasBlank = true;
      index += 1;
      continue;
    }

    previousWasBlank = false;

    if (/^import\b/.test(value)) {
      const importLines = [value];
      while (
        !/(?:\bfrom\s+)?["'][^"']+["']\s*;?$/.test(importLines[importLines.length - 1]) &&
        index + 1 < inputLines.length
      ) {
        index += 1;
        importLines.push(inputLines[index].trim());
      }
      output.push(importLines[0], ...importLines.slice(1).map((line) => `${unit}${line}`));
      index += 1;
      continue;
    }

    if (isPreservedRawBlockStart(value)) {
      const collected = collectPreservedRawBlock(inputLines, index);
      const depth = codeDepth + htmlDepth + controlDepth;
      output.push(`${unit.repeat(depth)}${collected.lines[0].trimStart()}`);
      output.push(...collected.lines.slice(1));
      index = collected.endIndex + 1;
      continue;
    }

    if (isMultilineOpeningTagStart(value)) {
      const collected = collectOpeningTag(inputLines, index);

      value = collected.value;
      index = collected.endIndex;
    }

    const inlineDeclaration = formatInlineDeclarationBlock(value, unit, codeDepth + htmlDepth);
    const inlineProps =
      inlineDeclaration ?? formatInlinePropsBlock(value, unit, codeDepth + htmlDepth);
    if (inlineProps) {
      output.push(...inlineProps);
      index += 1;
      continue;
    }

    const leadingClosingBraces = countLeadingClosingBraces(value);

    const closesControlBlock = isControlBlockClose(value);

    const continuesControlBlock = isControlBlockMiddle(value);

    const lineControlDepth =
      closesControlBlock || continuesControlBlock ? Math.max(0, controlDepth - 1) : controlDepth;

    const lineCodeDepth = Math.max(0, codeDepth - leadingClosingBraces);

    let lineHtmlDepth = htmlDepth;

    if (isClosingTag(value)) {
      lineHtmlDepth = Math.max(0, htmlDepth - 1);
    }

    const depth = lineCodeDepth + lineHtmlDepth + lineControlDepth;

    const inlineFragmentFits = unit.repeat(depth).length + value.length <= printWidth;

    if (isBalancedInlineHtmlFragment(value) && inlineFragmentFits) {
      output.push(`${unit.repeat(depth)}${value}`);
    } else if (
      value.startsWith("<") &&
      !value.startsWith("</") &&
      !value.startsWith("<!--") &&
      !value.startsWith("<!") &&
      !value.startsWith("<?")
    ) {
      const formattedTag = formatOpeningTag(value, unit, depth, printWidth, multilineAttributes);

      output.push(...formattedTag.lines);

      if (formattedTag.opensElement) {
        htmlDepth += 1;
      }
    } else {
      output.push(`${unit.repeat(depth)}${value}`);
    }

    if (isClosingTag(value)) {
      htmlDepth = lineHtmlDepth;
    }

    const braces = countStructuralBraces(value);

    codeDepth = Math.max(
      0,
      lineCodeDepth + braces.openings - Math.max(0, braces.closings - leadingClosingBraces),
    );

    if (isControlBlockOpen(value) || continuesControlBlock) {
      controlDepth = lineControlDepth + 1;
    } else if (closesControlBlock) {
      controlDepth = lineControlDepth;
    }

    index += 1;
  }

  while (output.length > 0 && output[output.length - 1] === "") {
    output.pop();
  }

  return `${output.join("\n")}\n`;
}

/** Format to a bounded fixed point so one call is always safe for editor-on-save and migrations. */
export function formatWrn(source: string, options: FormatWrnOptions = {}): string {
  let current = source;
  const seen = new Set<string>();
  for (let pass = 0; pass < 8; pass++) {
    const formatted = formatWrnPass(current, options);
    if (formatted === current) return formatted;
    if (seen.has(formatted)) return [...seen, formatted].sort()[0]!;
    seen.add(current);
    current = formatted;
  }
  return current;
}
