mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 18:57:34 +00:00
LibMarkdown: Implement introspection of the document tree
This commit is contained in:
parent
aca01932bd
commit
24e7196158
23 changed files with 319 additions and 0 deletions
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/RecursionDecision.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
|
#include <LibMarkdown/Forward.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const = 0;
|
virtual String render_to_html(bool tight = false) const = 0;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const = 0;
|
virtual String render_for_terminal(size_t view_width = 0) const = 0;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/BlockQuote.h>
|
#include <LibMarkdown/BlockQuote.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -24,6 +25,15 @@ String BlockQuote::render_for_terminal(size_t view_width) const
|
||||||
return m_contents->render_for_terminal(view_width);
|
return m_contents->render_for_terminal(view_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision BlockQuote::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return m_contents->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<BlockQuote> BlockQuote::parse(LineIterator& lines)
|
OwnPtr<BlockQuote> BlockQuote::parse(LineIterator& lines)
|
||||||
{
|
{
|
||||||
lines.push_context(LineIterator::Context::block_quote());
|
lines.push_context(LineIterator::Context::block_quote());
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
|
|
||||||
static OwnPtr<BlockQuote> parse(LineIterator& lines);
|
static OwnPtr<BlockQuote> parse(LineIterator& lines);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibJS/MarkupGenerator.h>
|
#include <LibJS/MarkupGenerator.h>
|
||||||
#include <LibMarkdown/CodeBlock.h>
|
#include <LibMarkdown/CodeBlock.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
#include <LibRegex/Regex.h>
|
#include <LibRegex/Regex.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
@ -54,6 +55,22 @@ String CodeBlock::render_for_terminal(size_t) const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision CodeBlock::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
rd = visitor.visit(m_code);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
// Don't recurse on m_language and m_style.
|
||||||
|
|
||||||
|
// Normalize return value.
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
static Regex<ECMA262> style_spec_re("\\s*([\\*_]*)\\s*([^\\*_\\s]*).*");
|
static Regex<ECMA262> style_spec_re("\\s*([\\*_]*)\\s*([^\\*_\\s]*).*");
|
||||||
|
|
||||||
OwnPtr<CodeBlock> CodeBlock::parse(LineIterator& lines)
|
OwnPtr<CodeBlock> CodeBlock::parse(LineIterator& lines)
|
||||||
|
|
|
@ -25,6 +25,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
static OwnPtr<CodeBlock> parse(LineIterator& lines);
|
static OwnPtr<CodeBlock> parse(LineIterator& lines);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <LibMarkdown/List.h>
|
#include <LibMarkdown/List.h>
|
||||||
#include <LibMarkdown/Paragraph.h>
|
#include <LibMarkdown/Paragraph.h>
|
||||||
#include <LibMarkdown/Table.h>
|
#include <LibMarkdown/Table.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -50,6 +51,21 @@ String ContainerBlock::render_for_terminal(size_t view_width) const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision ContainerBlock::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
for (auto const& block : m_blocks) {
|
||||||
|
rd = block.walk(visitor);
|
||||||
|
if (rd == RecursionDecision::Break)
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BlockType>
|
template<typename BlockType>
|
||||||
static bool try_parse_block(LineIterator& lines, NonnullOwnPtrVector<Block>& blocks)
|
static bool try_parse_block(LineIterator& lines, NonnullOwnPtrVector<Block>& blocks)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
|
|
||||||
static OwnPtr<ContainerBlock> parse(LineIterator& lines);
|
static OwnPtr<ContainerBlock> parse(LineIterator& lines);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/Document.h>
|
#include <LibMarkdown/Document.h>
|
||||||
#include <LibMarkdown/LineIterator.h>
|
#include <LibMarkdown/LineIterator.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -41,6 +42,15 @@ String Document::render_for_terminal(size_t view_width) const
|
||||||
return m_container->render_for_terminal(view_width);
|
return m_container->render_for_terminal(view_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Document::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return m_container->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<Document> Document::parse(const StringView& str)
|
OwnPtr<Document> Document::parse(const StringView& str)
|
||||||
{
|
{
|
||||||
const Vector<StringView> lines_vec = str.lines();
|
const Vector<StringView> lines_vec = str.lines();
|
||||||
|
|
|
@ -23,6 +23,17 @@ public:
|
||||||
String render_to_inline_html() const;
|
String render_to_inline_html() const;
|
||||||
String render_for_terminal(size_t view_width = 0) const;
|
String render_for_terminal(size_t view_width = 0) const;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk recursively through the document tree. Returning `RecursionDecision::Recurse` from
|
||||||
|
* `Visitor::visit` proceeds with the next element of the pre-order walk, usually a child element.
|
||||||
|
* Returning `RecursionDecision::Continue` skips the subtree, and usually proceeds with the next
|
||||||
|
* sibling. Returning `RecursionDecision::Break` breaks the recursion, with no further calls to
|
||||||
|
* any of the `Visitor::visit` methods.
|
||||||
|
*
|
||||||
|
* Note that `walk()` will only return `RecursionDecision::Continue` or `RecursionDecision::Break`.
|
||||||
|
*/
|
||||||
|
RecursionDecision walk(Visitor&) const;
|
||||||
|
|
||||||
static OwnPtr<Document> parse(const StringView&);
|
static OwnPtr<Document> parse(const StringView&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
26
Userland/Libraries/LibMarkdown/Forward.h
Normal file
26
Userland/Libraries/LibMarkdown/Forward.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Markdown {
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class Document;
|
||||||
|
class Text;
|
||||||
|
|
||||||
|
class BlockQuote;
|
||||||
|
class CodeBlock;
|
||||||
|
class ContainerBlock;
|
||||||
|
class Heading;
|
||||||
|
class HoriziontalRule;
|
||||||
|
class List;
|
||||||
|
class Paragraph;
|
||||||
|
class Table;
|
||||||
|
|
||||||
|
class Visitor;
|
||||||
|
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/Heading.h>
|
#include <LibMarkdown/Heading.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -35,6 +36,15 @@ String Heading::render_for_terminal(size_t) const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Heading::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return m_text.walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<Heading> Heading::parse(LineIterator& lines)
|
OwnPtr<Heading> Heading::parse(LineIterator& lines)
|
||||||
{
|
{
|
||||||
if (lines.is_end())
|
if (lines.is_end())
|
||||||
|
|
|
@ -27,6 +27,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
static OwnPtr<Heading> parse(LineIterator& lines);
|
static OwnPtr<Heading> parse(LineIterator& lines);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/HorizontalRule.h>
|
#include <LibMarkdown/HorizontalRule.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -24,6 +25,15 @@ String HorizontalRule::render_for_terminal(size_t view_width) const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision HorizontalRule::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
// Normalize return value.
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<HorizontalRule> HorizontalRule::parse(LineIterator& lines)
|
OwnPtr<HorizontalRule> HorizontalRule::parse(LineIterator& lines)
|
||||||
{
|
{
|
||||||
if (lines.is_end())
|
if (lines.is_end())
|
||||||
|
|
|
@ -23,6 +23,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
static OwnPtr<HorizontalRule> parse(LineIterator& lines);
|
static OwnPtr<HorizontalRule> parse(LineIterator& lines);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/List.h>
|
#include <LibMarkdown/List.h>
|
||||||
#include <LibMarkdown/Paragraph.h>
|
#include <LibMarkdown/Paragraph.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -54,6 +55,21 @@ String List::render_for_terminal(size_t) const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision List::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
for (auto const& block : m_items) {
|
||||||
|
rd = block->walk(visitor);
|
||||||
|
if (rd == RecursionDecision::Break)
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<List> List::parse(LineIterator& lines)
|
OwnPtr<List> List::parse(LineIterator& lines)
|
||||||
{
|
{
|
||||||
Vector<OwnPtr<ContainerBlock>> items;
|
Vector<OwnPtr<ContainerBlock>> items;
|
||||||
|
|
|
@ -26,6 +26,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
|
|
||||||
static OwnPtr<List> parse(LineIterator& lines);
|
static OwnPtr<List> parse(LineIterator& lines);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/Paragraph.h>
|
#include <LibMarkdown/Paragraph.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -34,4 +35,13 @@ String Paragraph::render_for_terminal(size_t) const
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Paragraph::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return m_text.walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Text m_text;
|
Text m_text;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/Table.h>
|
#include <LibMarkdown/Table.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -96,6 +97,21 @@ String Table::render_to_html(bool) const
|
||||||
return builder.to_string();
|
return builder.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Table::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
for (auto const& column : m_columns) {
|
||||||
|
rd = column.walk(visitor);
|
||||||
|
if (rd == RecursionDecision::Break)
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
OwnPtr<Table> Table::parse(LineIterator& lines)
|
OwnPtr<Table> Table::parse(LineIterator& lines)
|
||||||
{
|
{
|
||||||
auto peek_it = lines;
|
auto peek_it = lines;
|
||||||
|
@ -207,4 +223,23 @@ OwnPtr<Table> Table::parse(LineIterator& lines)
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Table::Column::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
rd = header.walk(visitor);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
for (auto const& row : rows) {
|
||||||
|
rd = row.walk(visitor);
|
||||||
|
if (rd == RecursionDecision::Break)
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
Vector<Text> rows;
|
Vector<Text> rows;
|
||||||
Alignment alignment { Alignment::Left };
|
Alignment alignment { Alignment::Left };
|
||||||
size_t relative_width { 0 };
|
size_t relative_width { 0 };
|
||||||
|
|
||||||
|
RecursionDecision walk(Visitor&) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
Table() { }
|
Table() { }
|
||||||
|
@ -34,6 +36,7 @@ public:
|
||||||
|
|
||||||
virtual String render_to_html(bool tight = false) const override;
|
virtual String render_to_html(bool tight = false) const override;
|
||||||
virtual String render_for_terminal(size_t view_width = 0) const override;
|
virtual String render_for_terminal(size_t view_width = 0) const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
static OwnPtr<Table> parse(LineIterator& lines);
|
static OwnPtr<Table> parse(LineIterator& lines);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <AK/ScopeGuard.h>
|
#include <AK/ScopeGuard.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibMarkdown/Text.h>
|
#include <LibMarkdown/Text.h>
|
||||||
|
#include <LibMarkdown/Visitor.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -39,6 +40,15 @@ size_t Text::EmphasisNode::terminal_length() const
|
||||||
return child->terminal_length();
|
return child->terminal_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::EmphasisNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return child->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
void Text::CodeNode::render_to_html(StringBuilder& builder) const
|
void Text::CodeNode::render_to_html(StringBuilder& builder) const
|
||||||
{
|
{
|
||||||
builder.append("<code>");
|
builder.append("<code>");
|
||||||
|
@ -58,6 +68,15 @@ size_t Text::CodeNode::terminal_length() const
|
||||||
return code->terminal_length();
|
return code->terminal_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::CodeNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return code->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
void Text::BreakNode::render_to_html(StringBuilder& builder) const
|
void Text::BreakNode::render_to_html(StringBuilder& builder) const
|
||||||
{
|
{
|
||||||
builder.append("<br />");
|
builder.append("<br />");
|
||||||
|
@ -72,6 +91,15 @@ size_t Text::BreakNode::terminal_length() const
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::BreakNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
// Normalize return value
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
void Text::TextNode::render_to_html(StringBuilder& builder) const
|
void Text::TextNode::render_to_html(StringBuilder& builder) const
|
||||||
{
|
{
|
||||||
builder.append(escape_html_entities(text));
|
builder.append(escape_html_entities(text));
|
||||||
|
@ -95,6 +123,18 @@ size_t Text::TextNode::terminal_length() const
|
||||||
return text.length();
|
return text.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::TextNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
rd = visitor.visit(text);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
// Normalize return value
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
void Text::LinkNode::render_to_html(StringBuilder& builder) const
|
void Text::LinkNode::render_to_html(StringBuilder& builder) const
|
||||||
{
|
{
|
||||||
if (is_image) {
|
if (is_image) {
|
||||||
|
@ -134,6 +174,17 @@ size_t Text::LinkNode::terminal_length() const
|
||||||
return text->terminal_length();
|
return text->terminal_length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::LinkNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
// Don't recurse on href.
|
||||||
|
|
||||||
|
return text->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
void Text::MultiNode::render_to_html(StringBuilder& builder) const
|
void Text::MultiNode::render_to_html(StringBuilder& builder) const
|
||||||
{
|
{
|
||||||
for (auto& child : children) {
|
for (auto& child : children) {
|
||||||
|
@ -157,6 +208,21 @@ size_t Text::MultiNode::terminal_length() const
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::MultiNode::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
for (auto const& child : children) {
|
||||||
|
rd = child.walk(visitor);
|
||||||
|
if (rd == RecursionDecision::Break)
|
||||||
|
return rd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RecursionDecision::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
size_t Text::terminal_length() const
|
size_t Text::terminal_length() const
|
||||||
{
|
{
|
||||||
return m_node->terminal_length();
|
return m_node->terminal_length();
|
||||||
|
@ -176,6 +242,15 @@ String Text::render_for_terminal() const
|
||||||
return builder.build().trim(" \n\t");
|
return builder.build().trim(" \n\t");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecursionDecision Text::walk(Visitor& visitor) const
|
||||||
|
{
|
||||||
|
RecursionDecision rd = visitor.visit(*this);
|
||||||
|
if (rd != RecursionDecision::Recurse)
|
||||||
|
return rd;
|
||||||
|
|
||||||
|
return m_node->walk(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
Text Text::parse(StringView const& str)
|
Text Text::parse(StringView const& str)
|
||||||
{
|
{
|
||||||
Text text;
|
Text text;
|
||||||
|
|
|
@ -10,7 +10,9 @@
|
||||||
#include <AK/Noncopyable.h>
|
#include <AK/Noncopyable.h>
|
||||||
#include <AK/NonnullOwnPtrVector.h>
|
#include <AK/NonnullOwnPtrVector.h>
|
||||||
#include <AK/OwnPtr.h>
|
#include <AK/OwnPtr.h>
|
||||||
|
#include <AK/RecursionDecision.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
#include <LibMarkdown/Forward.h>
|
||||||
|
|
||||||
namespace Markdown {
|
namespace Markdown {
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const = 0;
|
virtual void render_to_html(StringBuilder& builder) const = 0;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const = 0;
|
virtual void render_for_terminal(StringBuilder& builder) const = 0;
|
||||||
virtual size_t terminal_length() const = 0;
|
virtual size_t terminal_length() const = 0;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const = 0;
|
||||||
|
|
||||||
virtual ~Node() { }
|
virtual ~Node() { }
|
||||||
};
|
};
|
||||||
|
@ -39,6 +42,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CodeNode : public Node {
|
class CodeNode : public Node {
|
||||||
|
@ -53,6 +57,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BreakNode : public Node {
|
class BreakNode : public Node {
|
||||||
|
@ -60,6 +65,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TextNode : public Node {
|
class TextNode : public Node {
|
||||||
|
@ -82,6 +88,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LinkNode : public Node {
|
class LinkNode : public Node {
|
||||||
|
@ -100,6 +107,7 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MultiNode : public Node {
|
class MultiNode : public Node {
|
||||||
|
@ -109,12 +117,14 @@ public:
|
||||||
virtual void render_to_html(StringBuilder& builder) const override;
|
virtual void render_to_html(StringBuilder& builder) const override;
|
||||||
virtual void render_for_terminal(StringBuilder& builder) const override;
|
virtual void render_for_terminal(StringBuilder& builder) const override;
|
||||||
virtual size_t terminal_length() const override;
|
virtual size_t terminal_length() const override;
|
||||||
|
virtual RecursionDecision walk(Visitor&) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t terminal_length() const;
|
size_t terminal_length() const;
|
||||||
|
|
||||||
String render_to_html() const;
|
String render_to_html() const;
|
||||||
String render_for_terminal() const;
|
String render_for_terminal() const;
|
||||||
|
RecursionDecision walk(Visitor&) const;
|
||||||
|
|
||||||
static Text parse(StringView const&);
|
static Text parse(StringView const&);
|
||||||
|
|
||||||
|
|
50
Userland/Libraries/LibMarkdown/Visitor.h
Normal file
50
Userland/Libraries/LibMarkdown/Visitor.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/RecursionDecision.h>
|
||||||
|
#include <LibMarkdown/BlockQuote.h>
|
||||||
|
#include <LibMarkdown/CodeBlock.h>
|
||||||
|
#include <LibMarkdown/Document.h>
|
||||||
|
#include <LibMarkdown/Heading.h>
|
||||||
|
#include <LibMarkdown/HorizontalRule.h>
|
||||||
|
#include <LibMarkdown/List.h>
|
||||||
|
#include <LibMarkdown/Paragraph.h>
|
||||||
|
#include <LibMarkdown/Table.h>
|
||||||
|
|
||||||
|
namespace Markdown {
|
||||||
|
|
||||||
|
class Visitor {
|
||||||
|
public:
|
||||||
|
Visitor() = default;
|
||||||
|
virtual ~Visitor() = default;
|
||||||
|
|
||||||
|
virtual RecursionDecision visit(Document const&) { return RecursionDecision::Recurse; }
|
||||||
|
|
||||||
|
virtual RecursionDecision visit(BlockQuote const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(CodeBlock const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(ContainerBlock const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Heading const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(HorizontalRule const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(List const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Paragraph const&) { return RecursionDecision::Recurse; }
|
||||||
|
|
||||||
|
virtual RecursionDecision visit(Table const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Table::Column const&) { return RecursionDecision::Recurse; }
|
||||||
|
|
||||||
|
virtual RecursionDecision visit(Text const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::BreakNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::CodeNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::EmphasisNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::LinkNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::MultiNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
virtual RecursionDecision visit(Text::TextNode const&) { return RecursionDecision::Recurse; }
|
||||||
|
|
||||||
|
virtual RecursionDecision visit(String const&) { return RecursionDecision::Recurse; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue