/* * Copyright (c) 2019-2020, Sergey Bugaev * Copyright (c) 2021, Peter Elliott * Copyright (c) 2022, the SerenityOS developers. * * 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() = default; }; 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: DeprecatedString 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; DeprecatedString href; Optional image_width; Optional image_height; LinkNode(bool is_image, NonnullOwnPtr text, DeprecatedString href, Optional image_width, Optional image_height) : is_image(is_image) , text(move(text)) , href(move(href)) , image_width(image_width) , image_height(image_height) { } bool has_image_dimensions() const { return image_width.has_value() || image_height.has_value(); } 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: Vector> 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; }; class StrikeThroughNode : public Node { public: NonnullOwnPtr striked_text; StrikeThroughNode(NonnullOwnPtr striked_text) : striked_text(move(striked_text)) { } 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; DeprecatedString render_to_html() const; DeprecatedString render_for_terminal() const; RecursionDecision walk(Visitor&) const; static Text parse(StringView); private: struct Token { DeprecatedString 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 syntactical 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); static NonnullOwnPtr parse_strike_through(Vector::ConstIterator& tokens); OwnPtr m_node; }; }