/* * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> * Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org> * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include <AK/ByteString.h> #include <AK/Noncopyable.h> #include <AK/OwnPtr.h> #include <AK/RecursionDecision.h> #include <AK/Vector.h> #include <LibMarkdown/Forward.h> 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 void render_for_raw_print(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<Node> child; EmphasisNode(bool strong, NonnullOwnPtr<Node> 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 void render_for_raw_print(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class CodeNode : public Node { public: NonnullOwnPtr<Node> code; CodeNode(NonnullOwnPtr<Node> code) : code(move(code)) { } virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual void render_for_raw_print(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 void render_for_raw_print(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class TextNode : public Node { public: ByteString 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 void render_for_raw_print(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<Node> text; ByteString href; Optional<int> image_width; Optional<int> image_height; LinkNode(bool is_image, NonnullOwnPtr<Node> text, ByteString href, Optional<int> image_width, Optional<int> 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 void render_for_raw_print(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class MultiNode : public Node { public: Vector<NonnullOwnPtr<Node>> children; virtual void render_to_html(StringBuilder& builder) const override; virtual void render_for_terminal(StringBuilder& builder) const override; virtual void render_for_raw_print(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; class StrikeThroughNode : public Node { public: NonnullOwnPtr<Node> striked_text; StrikeThroughNode(NonnullOwnPtr<Node> 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 void render_for_raw_print(StringBuilder& builder) const override; virtual size_t terminal_length() const override; virtual RecursionDecision walk(Visitor&) const override; }; size_t terminal_length() const; ByteString render_to_html() const; ByteString render_for_terminal() const; ByteString render_for_raw_print() const; RecursionDecision walk(Visitor&) const; static Text parse(StringView); private: struct Token { ByteString 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<Token> tokenize(StringView); static bool can_open(Token const& opening); static bool can_close_for(Token const& opening, Token const& closing); static NonnullOwnPtr<MultiNode> parse_sequence(Vector<Token>::ConstIterator& tokens, bool in_link); static NonnullOwnPtr<Node> parse_break(Vector<Token>::ConstIterator& tokens); static NonnullOwnPtr<Node> parse_newline(Vector<Token>::ConstIterator& tokens); static NonnullOwnPtr<Node> parse_emph(Vector<Token>::ConstIterator& tokens, bool in_link); static NonnullOwnPtr<Node> parse_code(Vector<Token>::ConstIterator& tokens); static NonnullOwnPtr<Node> parse_link(Vector<Token>::ConstIterator& tokens); static NonnullOwnPtr<Node> parse_strike_through(Vector<Token>::ConstIterator& tokens); OwnPtr<Node> m_node; }; }