/* * Copyright (c) 2019-2020, Sergey Bugaev * Copyright (c) 2022, Peter Elliott * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include namespace Markdown { DeprecatedString CodeBlock::render_to_html(bool) const { StringBuilder builder; builder.append("
"sv);

    if (m_style.length() >= 2)
        builder.append(""sv);
    else if (m_style.length() >= 2)
        builder.append(""sv);

    if (m_language.is_empty())
        builder.append(""sv);
    else
        builder.appendff("", escape_html_entities(m_language));

    if (m_language == "js") {
        auto html_or_error = JS::MarkupGenerator::html_from_source(m_code);
        if (html_or_error.is_error()) {
            warnln("Could not render js code to html: {}", html_or_error.error());
            builder.append(escape_html_entities(m_code));
        } else {
            builder.append(html_or_error.release_value());
        }
    } else {
        builder.append(escape_html_entities(m_code));
    }

    builder.append(""sv);

    if (m_style.length() >= 2)
        builder.append(""sv);
    else if (m_style.length() >= 2)
        builder.append(""sv);

    builder.append("
\n"sv); return builder.to_deprecated_string(); } Vector CodeBlock::render_lines_for_terminal(size_t) const { Vector lines; // Do not indent too much if we are in the synopsis auto indentation = " "sv; if (m_current_section != nullptr) { auto current_section_name = m_current_section->render_lines_for_terminal()[0]; if (current_section_name.contains("SYNOPSIS"sv)) indentation = " "sv; } for (auto const& line : m_code.split('\n')) lines.append(DeprecatedString::formatted("{}{}", indentation, line)); lines.append(""); return lines; } 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 open_fence_re("^ {0,3}(([\\`\\~])\\2{2,})\\s*([\\*_]*)\\s*([^\\*_\\s]*).*$"); static Regex close_fence_re("^ {0,3}(([\\`\\~])\\2{2,})\\s*$"); static Optional line_block_prefix(StringView const& line) { int characters = 0; int indents = 0; for (char ch : line) { if (indents == 4) break; if (ch == ' ') { ++characters; ++indents; } else if (ch == '\t') { ++characters; indents = 4; } else { break; } } if (indents == 4) return characters; return {}; } OwnPtr CodeBlock::parse(LineIterator& lines, Heading* current_section) { if (lines.is_end()) return {}; StringView line = *lines; if (open_fence_re.match(line).success) return parse_backticks(lines, current_section); if (line_block_prefix(line).has_value()) return parse_indent(lines); return {}; } OwnPtr CodeBlock::parse_backticks(LineIterator& lines, Heading* current_section) { StringView line = *lines; // Our Markdown extension: we allow // specifying a style and a language // for a code block, like so: // // ```**sh** // $ echo hello friends! // ```` // // The code block will be made bold, // and if possible syntax-highlighted // as appropriate for a shell script. auto matches = open_fence_re.match(line).capture_group_matches[0]; auto fence = matches[0].view.string_view(); auto style = matches[2].view.string_view(); auto language = matches[3].view.string_view(); ++lines; StringBuilder builder; while (true) { if (lines.is_end()) break; line = *lines; ++lines; auto close_match = close_fence_re.match(line); if (close_match.success) { auto close_fence = close_match.capture_group_matches[0][0].view.string_view(); if (close_fence[0] == fence[0] && close_fence.length() >= fence.length()) break; } builder.append(line); builder.append('\n'); } return make(language, style, builder.to_deprecated_string(), current_section); } OwnPtr CodeBlock::parse_indent(LineIterator& lines) { StringBuilder builder; while (true) { if (lines.is_end()) break; StringView line = *lines; auto prefix_length = line_block_prefix(line); if (!prefix_length.has_value()) break; line = line.substring_view(prefix_length.value()); ++lines; builder.append(line); builder.append('\n'); } return make("", "", builder.to_deprecated_string(), nullptr); } }