#include "iv/markdown/iv_markdown_document.h" #include namespace Iv::Markdown { namespace { [[nodiscard]] QString FromLatin1(const char *value) { return QString::fromLatin1(value); } [[nodiscard]] QString BoolString(bool value) { return FromLatin1(value ? "true" : "false"); } [[nodiscard]] QString NodeKindName(NodeKind kind) { switch (kind) { case NodeKind::Document: return FromLatin1("Document"); case NodeKind::Paragraph: return FromLatin1("Paragraph"); case NodeKind::Heading: return FromLatin1("Heading"); case NodeKind::Text: return FromLatin1("Text"); case NodeKind::Emphasis: return FromLatin1("Emphasis"); case NodeKind::Strong: return FromLatin1("Strong"); case NodeKind::Strike: return FromLatin1("Strike"); case NodeKind::InlineCode: return FromLatin1("InlineCode"); case NodeKind::CodeBlock: return FromLatin1("CodeBlock"); case NodeKind::Link: return FromLatin1("Link"); case NodeKind::List: return FromLatin1("List"); case NodeKind::ListItem: return FromLatin1("ListItem"); case NodeKind::Blockquote: return FromLatin1("Blockquote"); case NodeKind::ThematicBreak: return FromLatin1("ThematicBreak"); case NodeKind::Table: return FromLatin1("Table"); case NodeKind::TableRow: return FromLatin1("TableRow"); case NodeKind::TableCell: return FromLatin1("TableCell"); case NodeKind::HtmlInline: return FromLatin1("HtmlInline"); case NodeKind::HtmlBlock: return FromLatin1("HtmlBlock"); case NodeKind::FootnoteReference: return FromLatin1("FootnoteReference"); case NodeKind::FootnoteDefinition: return FromLatin1("FootnoteDefinition"); case NodeKind::DisplayMath: return FromLatin1("DisplayMath"); case NodeKind::InlineMath: return FromLatin1("InlineMath"); case NodeKind::SoftBreak: return FromLatin1("SoftBreak"); case NodeKind::LineBreak: return FromLatin1("LineBreak"); case NodeKind::Unsupported: return FromLatin1("Unsupported"); } return FromLatin1("Unsupported"); } [[nodiscard]] QString MathKindName(MathKind kind) { switch (kind) { case MathKind::Inline: return FromLatin1("Inline"); case MathKind::Display: return FromLatin1("Display"); } return FromLatin1("Inline"); } [[nodiscard]] QString ListKindName(ListKind kind) { switch (kind) { case ListKind::Bullet: return FromLatin1("Bullet"); case ListKind::Ordered: return FromLatin1("Ordered"); } return FromLatin1("Bullet"); } [[nodiscard]] QString ListDelimiterName(ListDelimiter delimiter) { switch (delimiter) { case ListDelimiter::None: return FromLatin1("None"); case ListDelimiter::Period: return FromLatin1("Period"); case ListDelimiter::Parenthesis: return FromLatin1("Parenthesis"); } return FromLatin1("None"); } [[nodiscard]] QString HtmlBlockKindName(HtmlBlockKind kind) { switch (kind) { case HtmlBlockKind::None: return FromLatin1("None"); case HtmlBlockKind::Comment: return FromLatin1("Comment"); case HtmlBlockKind::Details: return FromLatin1("Details"); case HtmlBlockKind::Unsupported: return FromLatin1("Unsupported"); } return FromLatin1("None"); } [[nodiscard]] QString TaskStateName(TaskState state) { switch (state) { case TaskState::None: return FromLatin1("None"); case TaskState::Unchecked: return FromLatin1("Unchecked"); case TaskState::Checked: return FromLatin1("Checked"); } return FromLatin1("None"); } [[nodiscard]] QString TableAlignmentName(TableAlignment alignment) { switch (alignment) { case TableAlignment::None: return FromLatin1("None"); case TableAlignment::Left: return FromLatin1("Left"); case TableAlignment::Center: return FromLatin1("Center"); case TableAlignment::Right: return FromLatin1("Right"); } return FromLatin1("None"); } [[nodiscard]] QString RangeString(const SourceRange &range) { if (!range.available) { return FromLatin1("unavailable"); } return FromLatin1("%1:%2-%3:%4[%5,%6]").arg( range.startLine ).arg( range.startColumn ).arg( range.endLine ).arg( range.endColumn ).arg( range.startOffset ).arg( range.endOffset); } [[nodiscard]] QString EscapedValue(QString value) { value.replace(FromLatin1("\\"), FromLatin1("\\\\")); value.replace(FromLatin1("\r"), FromLatin1("\\r")); value.replace(FromLatin1("\n"), FromLatin1("\\n")); value.replace(FromLatin1("\t"), FromLatin1("\\t")); return value; } void AddRequiredStringAttribute( QString *line, const char *name, const QString &value) { line->append(FromLatin1(" ")); line->append(FromLatin1(name)); line->append(FromLatin1("=\"")); line->append(EscapedValue(value)); line->append(FromLatin1("\"")); } void AddStringAttribute(QString *line, const char *name, const QString &value) { if (value.isEmpty()) { return; } AddRequiredStringAttribute(line, name, value); } void AddIntAttribute(QString *line, const char *name, int value) { line->append(FromLatin1(" ")); line->append(FromLatin1(name)); line->append(FromLatin1("=")); line->append(QString::number(value)); } void AddBoolAttribute(QString *line, const char *name, bool value) { if (!value) { return; } line->append(FromLatin1(" ")); line->append(FromLatin1(name)); line->append(FromLatin1("=")); line->append(BoolString(value)); } [[nodiscard]] QString TableAlignmentsString( const std::vector &alignments) { auto names = QStringList(); for (const auto alignment : alignments) { names.append(TableAlignmentName(alignment)); } return names.join(FromLatin1(",")); } void DumpNode( const MarkdownNode &node, int depth, QStringList *lines) { auto line = FromLatin1("node"); AddIntAttribute(&line, "depth", depth); AddStringAttribute(&line, "kind", NodeKindName(node.kind)); AddStringAttribute(&line, "range", RangeString(node.range)); AddIntAttribute(&line, "children", static_cast(node.children.size())); AddStringAttribute(&line, "text", node.text); AddStringAttribute(&line, "url", node.url); AddStringAttribute(&line, "title", node.title); AddStringAttribute(&line, "info", node.info); AddStringAttribute(&line, "raw", node.raw); AddStringAttribute(&line, "anchorId", node.anchorId); AddStringAttribute(&line, "footnoteLabel", node.footnoteLabel); AddStringAttribute(&line, "detailsSummary", node.detailsSummary); AddStringAttribute(&line, "detailsBody", node.detailsBody); AddStringAttribute(&line, "unsupportedKind", node.unsupportedKind); if (node.headingLevel != 0) { AddIntAttribute(&line, "headingLevel", node.headingLevel); } if (node.listStart != 0) { AddIntAttribute(&line, "listStart", node.listStart); } if (node.tableColumn != -1) { AddIntAttribute(&line, "tableColumn", node.tableColumn); } if (node.formulaIndex != -1) { AddIntAttribute(&line, "formulaIndex", node.formulaIndex); } if (node.footnoteOrdinal != 0) { AddIntAttribute(&line, "footnoteOrdinal", node.footnoteOrdinal); } if (node.kind == NodeKind::List) { AddStringAttribute(&line, "listKind", ListKindName(node.listKind)); AddStringAttribute( &line, "listDelimiter", ListDelimiterName(node.listDelimiter)); } if (node.htmlBlockKind != HtmlBlockKind::None) { AddStringAttribute( &line, "htmlBlockKind", HtmlBlockKindName(node.htmlBlockKind)); } if (node.taskState != TaskState::None) { AddStringAttribute(&line, "taskState", TaskStateName(node.taskState)); } AddBoolAttribute(&line, "tight", node.tight); AddBoolAttribute(&line, "autolink", node.autolink); AddBoolAttribute(&line, "tableHeader", node.tableHeader); AddBoolAttribute(&line, "detailsOpen", node.detailsOpen); if (!node.tableAlignments.empty()) { AddStringAttribute( &line, "tableAlignments", TableAlignmentsString(node.tableAlignments)); } lines->append(line); for (const auto &child : node.children) { DumpNode(child, depth + 1, lines); } } } // namespace PreparedDocument EmptyDocument(QString sourceName) { auto document = PreparedDocument(); document.sourceName = std::move(sourceName); document.document.kind = NodeKind::Document; return document; } QString DumpForDebug(const PreparedDocument &document) { auto lines = QStringList(); lines.append(FromLatin1("sourceName=\"%1\"").arg( EscapedValue(document.sourceName))); lines.append(FromLatin1("title=\"%1\"").arg( EscapedValue(document.title))); lines.append(FromLatin1("sourceLength=%1").arg( static_cast(document.sourceText.size()))); lines.append(FromLatin1("empty=%1").arg(BoolString(document.empty))); lines.append(FromLatin1("cmarkNodeCount=%1").arg( document.stats.cmarkNodeCount)); lines.append(FromLatin1("convertedNodeCount=%1").arg( document.stats.convertedNodeCount)); lines.append(FromLatin1("maxDepth=%1").arg( document.stats.maxDepth)); lines.append(FromLatin1("inlineFormulaCount=%1").arg( document.stats.inlineFormulaCount)); lines.append(FromLatin1("displayFormulaCount=%1").arg( document.stats.displayFormulaCount)); lines.append(FromLatin1("tablesSeen=%1").arg( BoolString(document.stats.tablesSeen))); lines.append(FromLatin1("taskListsSeen=%1").arg( BoolString(document.stats.taskListsSeen))); lines.append(FromLatin1("strikethroughSeen=%1").arg( BoolString(document.stats.strikethroughSeen))); lines.append(FromLatin1("autolinksSeen=%1").arg( BoolString(document.stats.autolinksSeen))); lines.append(FromLatin1("footnotesSeen=%1").arg( BoolString(document.stats.footnotesSeen))); lines.append(FromLatin1("warnings=%1").arg( static_cast(document.warnings.size()))); for (auto i = qsizetype(0); i != document.warnings.size(); ++i) { lines.append(FromLatin1("warning index=%1 text=\"%2\"").arg( i + 1 ).arg( EscapedValue(document.warnings[i]))); } lines.append(FromLatin1("nodes=preorder")); DumpNode(document.document, 0, &lines); lines.append(FromLatin1("formulas=%1").arg( static_cast(document.formulas.size()))); for (const auto &formula : document.formulas) { auto line = FromLatin1("formula"); AddIntAttribute(&line, "index", formula.index); AddStringAttribute(&line, "kind", MathKindName(formula.kind)); AddStringAttribute(&line, "range", RangeString(formula.range)); AddRequiredStringAttribute(&line, "parent", formula.parentNodeKind); AddRequiredStringAttribute(&line, "tex", formula.tex); lines.append(line); } return lines.join(FromLatin1("\n")); } } // namespace Iv::Markdown