/* * Copyright (c) 2019-2020, Sergey Bugaev * Copyright (c) 2021, Peter Elliott * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace Markdown { class Text final { public: class Node { public: virtual void render_to_html(StringBuilder& builder) const = 0; virtual void render_for_terminal(StringBuilder& builder) const = 0; virtual size_t terminal_length() const = 0; virtual RecursionDecision walk(Visitor&) const = 0; virtual ~Node() { } }; class EmphasisNode : public Node { public: bool strong; NonnullOwnPtr child; EmphasisNode(bool strong, NonnullOwnPtr child) : strong(strong) , child(move(child)) { } virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class CodeNode : public Node { public: NonnullOwnPtr code; CodeNode(NonnullOwnPtr code) : code(move(code)) { } virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class BreakNode : public Node { public: virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class TextNode : public Node { public: String text; bool collapsible; TextNode(StringView text) : text(text) , collapsible(true) { } TextNode(StringView text, bool collapsible) : text(text) , collapsible(collapsible) { } virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class LinkNode : public Node { public: bool is_image; NonnullOwnPtr text; String href; LinkNode(bool is_image, NonnullOwnPtr text, String href) : is_image(is_image) , text(move(text)) , href(move(href)) { } virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class MultiNode : public Node { public: NonnullOwnPtrVector children; virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; size_t terminal_length() const; String render_to_html() const; String render_for_terminal() const; RecursionDecision walk(Visitor&) const; static Text parse(StringView); private: struct Token { String data; // Flanking basically means that a delimiter run has a non-whitespace, // non-punctuation character on the corresponding side. For a more exact // definition, see the CommonMark spec. bool left_flanking; bool right_flanking; bool punct_before; bool punct_after; // is_run indicates that this token is a 'delimiter run'. A delimiter // run occurs when several of the same sytactical character ('`', '_', // or '*') occur in a row. bool is_run; char run_char() const { VERIFY(is_run); return data[0]; } char run_length() const { VERIFY(is_run); return data.length(); } bool is_space() const { return data[0] == ' '; } bool operator==(StringView str) const { return str == data; } }; static Vector tokenize(StringView); static bool can_open(Token const& opening); static bool can_close_for(Token const& opening, Token const& closing); static NonnullOwnPtr parse_sequence(Vector::ConstIterator& tokens, bool in_link); static NonnullOwnPtr parse_break(Vector::ConstIterator& tokens); static NonnullOwnPtr parse_newline(Vector::ConstIterator& tokens); static NonnullOwnPtr parse_emph(Vector::ConstIterator& tokens, bool in_link); static NonnullOwnPtr parse_code(Vector::ConstIterator& tokens); static NonnullOwnPtr parse_link(Vector::ConstIterator& tokens); OwnPtr m_node; }; }