From e4cd91761d0ee153f2ea66a904aa557f7b923dc5 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 4 Jun 2024 07:52:10 +0200 Subject: [PATCH] Everywhere: Remove LibMarkdown This was used to convert markdown into HTML for display in the browser, but no other browser behaves this way, so let's simplify things by removing it. (Yes, we could implement all kinds of "convert to HTML and display" for every file format out there, but that's far outside the scope of a browser engine.) --- Meta/Lagom/CMakeLists.txt | 2 - Meta/Lagom/Fuzzers/FuzzMarkdown.cpp | 19 - Meta/Lagom/Fuzzers/fuzzers.cmake | 2 - Meta/gn/secondary/Ladybird/BUILD.gn | 2 - .../Userland/Libraries/LibMarkdown/BUILD.gn | 24 - .../Userland/Libraries/LibWeb/BUILD.gn | 1 - Tests/CMakeLists.txt | 1 - Tests/LibMarkdown/CMakeLists.txt | 14 - Tests/LibMarkdown/TestCommonmark.cpp | 48 -- Tests/LibMarkdown/TestImageSizeExtension.cpp | 39 - Userland/Libraries/CMakeLists.txt | 1 - Userland/Libraries/LibMarkdown/Block.h | 26 - Userland/Libraries/LibMarkdown/BlockQuote.cpp | 59 -- Userland/Libraries/LibMarkdown/BlockQuote.h | 34 - Userland/Libraries/LibMarkdown/CMakeLists.txt | 18 - Userland/Libraries/LibMarkdown/CodeBlock.cpp | 201 ----- Userland/Libraries/LibMarkdown/CodeBlock.h | 44 -- .../Libraries/LibMarkdown/CommentBlock.cpp | 75 -- Userland/Libraries/LibMarkdown/CommentBlock.h | 34 - .../Libraries/LibMarkdown/ContainerBlock.cpp | 154 ---- .../Libraries/LibMarkdown/ContainerBlock.h | 45 -- Userland/Libraries/LibMarkdown/Document.cpp | 73 -- Userland/Libraries/LibMarkdown/Document.h | 44 -- Userland/Libraries/LibMarkdown/Forward.h | 26 - Userland/Libraries/LibMarkdown/Heading.cpp | 86 --- Userland/Libraries/LibMarkdown/Heading.h | 39 - .../Libraries/LibMarkdown/HorizontalRule.cpp | 55 -- .../Libraries/LibMarkdown/HorizontalRule.h | 29 - .../Libraries/LibMarkdown/LineIterator.cpp | 64 -- Userland/Libraries/LibMarkdown/LineIterator.h | 100 --- Userland/Libraries/LibMarkdown/List.cpp | 169 ---- Userland/Libraries/LibMarkdown/List.h | 41 - Userland/Libraries/LibMarkdown/Paragraph.cpp | 45 -- Userland/Libraries/LibMarkdown/Paragraph.h | 33 - .../LibMarkdown/SyntaxHighlighter.cpp | 103 --- .../Libraries/LibMarkdown/SyntaxHighlighter.h | 23 - Userland/Libraries/LibMarkdown/Table.cpp | 265 ------- Userland/Libraries/LibMarkdown/Table.h | 49 -- Userland/Libraries/LibMarkdown/Text.cpp | 724 ------------------ Userland/Libraries/LibMarkdown/Text.h | 212 ----- Userland/Libraries/LibMarkdown/Visitor.h | 53 -- Userland/Libraries/LibWeb/CMakeLists.txt | 2 +- .../Libraries/LibWeb/DOM/DocumentLoading.cpp | 71 -- 43 files changed, 1 insertion(+), 3148 deletions(-) delete mode 100644 Meta/Lagom/Fuzzers/FuzzMarkdown.cpp delete mode 100644 Meta/gn/secondary/Userland/Libraries/LibMarkdown/BUILD.gn delete mode 100644 Tests/LibMarkdown/CMakeLists.txt delete mode 100644 Tests/LibMarkdown/TestCommonmark.cpp delete mode 100644 Tests/LibMarkdown/TestImageSizeExtension.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Block.h delete mode 100644 Userland/Libraries/LibMarkdown/BlockQuote.cpp delete mode 100644 Userland/Libraries/LibMarkdown/BlockQuote.h delete mode 100644 Userland/Libraries/LibMarkdown/CMakeLists.txt delete mode 100644 Userland/Libraries/LibMarkdown/CodeBlock.cpp delete mode 100644 Userland/Libraries/LibMarkdown/CodeBlock.h delete mode 100644 Userland/Libraries/LibMarkdown/CommentBlock.cpp delete mode 100644 Userland/Libraries/LibMarkdown/CommentBlock.h delete mode 100644 Userland/Libraries/LibMarkdown/ContainerBlock.cpp delete mode 100644 Userland/Libraries/LibMarkdown/ContainerBlock.h delete mode 100644 Userland/Libraries/LibMarkdown/Document.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Document.h delete mode 100644 Userland/Libraries/LibMarkdown/Forward.h delete mode 100644 Userland/Libraries/LibMarkdown/Heading.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Heading.h delete mode 100644 Userland/Libraries/LibMarkdown/HorizontalRule.cpp delete mode 100644 Userland/Libraries/LibMarkdown/HorizontalRule.h delete mode 100644 Userland/Libraries/LibMarkdown/LineIterator.cpp delete mode 100644 Userland/Libraries/LibMarkdown/LineIterator.h delete mode 100644 Userland/Libraries/LibMarkdown/List.cpp delete mode 100644 Userland/Libraries/LibMarkdown/List.h delete mode 100644 Userland/Libraries/LibMarkdown/Paragraph.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Paragraph.h delete mode 100644 Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp delete mode 100644 Userland/Libraries/LibMarkdown/SyntaxHighlighter.h delete mode 100644 Userland/Libraries/LibMarkdown/Table.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Table.h delete mode 100644 Userland/Libraries/LibMarkdown/Text.cpp delete mode 100644 Userland/Libraries/LibMarkdown/Text.h delete mode 100644 Userland/Libraries/LibMarkdown/Visitor.h diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index ef0ebbd0b2..02ac2acd9d 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -437,7 +437,6 @@ if (BUILD_LAGOM) JS Line Locale - Markdown Protocol Regex RIFF @@ -562,7 +561,6 @@ if (BUILD_LAGOM) LibCompress LibGfx LibLocale - LibMarkdown LibSQL LibTest LibTextCodec diff --git a/Meta/Lagom/Fuzzers/FuzzMarkdown.cpp b/Meta/Lagom/Fuzzers/FuzzMarkdown.cpp deleted file mode 100644 index e29a831487..0000000000 --- a/Meta/Lagom/Fuzzers/FuzzMarkdown.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - auto markdown = StringView(static_cast(data), size); - (void)Markdown::Document::parse(markdown); - return 0; -} diff --git a/Meta/Lagom/Fuzzers/fuzzers.cmake b/Meta/Lagom/Fuzzers/fuzzers.cmake index 6ac49a5bb8..3492052235 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -23,7 +23,6 @@ set(FUZZER_TARGETS JsonParser LzmaDecompression LzmaRoundtrip - Markdown MatroskaReader MD5 MP3Loader @@ -91,7 +90,6 @@ set(FUZZER_DEPENDENCIES_JPEGLoader LibGfx) set(FUZZER_DEPENDENCIES_Js LibJS) set(FUZZER_DEPENDENCIES_LzmaDecompression LibArchive LibCompress) set(FUZZER_DEPENDENCIES_LzmaRoundtrip LibCompress) -set(FUZZER_DEPENDENCIES_Markdown LibMarkdown) set(FUZZER_DEPENDENCIES_MatroskaReader LibVideo) set(FUZZER_DEPENDENCIES_MD5 LibCrypto) set(FUZZER_DEPENDENCIES_MP3Loader LibAudio) diff --git a/Meta/gn/secondary/Ladybird/BUILD.gn b/Meta/gn/secondary/Ladybird/BUILD.gn index d04a772169..c78a16c6bd 100644 --- a/Meta/gn/secondary/Ladybird/BUILD.gn +++ b/Meta/gn/secondary/Ladybird/BUILD.gn @@ -392,7 +392,6 @@ if (current_os != "mac") { "//Userland/Libraries/LibImageDecoderClient", "//Userland/Libraries/LibJS", "//Userland/Libraries/LibLine", - "//Userland/Libraries/LibMarkdown", "//Userland/Libraries/LibProtocol", "//Userland/Libraries/LibRIFF", "//Userland/Libraries/LibRegex", @@ -427,7 +426,6 @@ if (current_os != "mac") { "$root_out_dir/lib/liblagom-ipc.dylib", "$root_out_dir/lib/liblagom-js.dylib", "$root_out_dir/lib/liblagom-line.dylib", - "$root_out_dir/lib/liblagom-markdown.dylib", "$root_out_dir/lib/liblagom-protocol.dylib", "$root_out_dir/lib/liblagom-regex.dylib", "$root_out_dir/lib/liblagom-riff.dylib", diff --git a/Meta/gn/secondary/Userland/Libraries/LibMarkdown/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibMarkdown/BUILD.gn deleted file mode 100644 index 1a6928f937..0000000000 --- a/Meta/gn/secondary/Userland/Libraries/LibMarkdown/BUILD.gn +++ /dev/null @@ -1,24 +0,0 @@ -shared_library("LibMarkdown") { - output_name = "markdown" - include_dirs = [ "//Userland/Libraries" ] - sources = [ - "BlockQuote.cpp", - "CodeBlock.cpp", - "CommentBlock.cpp", - "ContainerBlock.cpp", - "Document.cpp", - "Heading.cpp", - "HorizontalRule.cpp", - "LineIterator.cpp", - "List.cpp", - "Paragraph.cpp", - "Table.cpp", - "Text.cpp", - ] - deps = [ - "//AK", - "//Userland/Libraries/LibCore", - "//Userland/Libraries/LibJS", - "//Userland/Libraries/LibRegex", - ] -} diff --git a/Meta/gn/secondary/Userland/Libraries/LibWeb/BUILD.gn b/Meta/gn/secondary/Userland/Libraries/LibWeb/BUILD.gn index 866ad03435..1d5a83d856 100644 --- a/Meta/gn/secondary/Userland/Libraries/LibWeb/BUILD.gn +++ b/Meta/gn/secondary/Userland/Libraries/LibWeb/BUILD.gn @@ -371,7 +371,6 @@ shared_library("LibWeb") { "//Userland/Libraries/LibIPC", "//Userland/Libraries/LibJS", "//Userland/Libraries/LibLocale", - "//Userland/Libraries/LibMarkdown", "//Userland/Libraries/LibRegex", "//Userland/Libraries/LibSyntax", "//Userland/Libraries/LibTLS", diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 77b0506be2..a7ede0bfe5 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -6,7 +6,6 @@ add_subdirectory(LibDiff) add_subdirectory(LibGfx) add_subdirectory(LibJS) add_subdirectory(LibLocale) -add_subdirectory(LibMarkdown) add_subdirectory(LibRegex) add_subdirectory(LibSQL) add_subdirectory(LibTest) diff --git a/Tests/LibMarkdown/CMakeLists.txt b/Tests/LibMarkdown/CMakeLists.txt deleted file mode 100644 index adf499594b..0000000000 --- a/Tests/LibMarkdown/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -include(commonmark_spec) - -set(TEST_SOURCES - TestCommonmark.cpp - TestImageSizeExtension.cpp -) - -foreach(source IN LISTS TEST_SOURCES) - serenity_test("${source}" LibMarkdown LIBS LibMarkdown) -endforeach() - -if (BUILD_LAGOM) - set_tests_properties(TestCommonmark PROPERTIES DISABLED YES) -endif() diff --git a/Tests/LibMarkdown/TestCommonmark.cpp b/Tests/LibMarkdown/TestCommonmark.cpp deleted file mode 100644 index 56372b7924..0000000000 --- a/Tests/LibMarkdown/TestCommonmark.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -TEST_SETUP -{ - auto file_or_error = Core::File::open("/home/anon/Tests/commonmark.spec.json"sv, Core::File::OpenMode::Read); - if (file_or_error.is_error()) - file_or_error = Core::File::open("./commonmark.spec.json"sv, Core::File::OpenMode::Read); - VERIFY(!file_or_error.is_error()); - auto file = file_or_error.release_value(); - auto file_size = MUST(file->size()); - auto content = MUST(ByteBuffer::create_uninitialized(file_size)); - MUST(file->read_until_filled(content.bytes())); - ByteString test_data { content.bytes() }; - - auto tests = JsonParser(test_data).parse().value().as_array(); - for (size_t i = 0; i < tests.size(); ++i) { - auto testcase = tests[i].as_object(); - - auto name = ByteString::formatted("{}_ex{}_{}..{}", - testcase.get("section"sv).value(), - testcase.get("example"sv).value(), - testcase.get("start_line"sv).value(), - testcase.get("end_line"sv).value()); - - ByteString markdown = testcase.get_byte_string("markdown"sv).value(); - ByteString html = testcase.get_byte_string("html"sv).value(); - - Test::TestSuite::the().add_case(adopt_ref(*new Test::TestCase( - name, [markdown, html]() { - auto document = Markdown::Document::parse(markdown); - EXPECT_EQ(document->render_to_inline_html(), html); - }, - false))); - } -} diff --git a/Tests/LibMarkdown/TestImageSizeExtension.cpp b/Tests/LibMarkdown/TestImageSizeExtension.cpp deleted file mode 100644 index 428dc46d9c..0000000000 --- a/Tests/LibMarkdown/TestImageSizeExtension.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, MacDue - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -struct TestCase { - StringView markdown; - StringView expected_html; -}; - -static constexpr Array image_size_tests { - // No image size: - TestCase { .markdown = "![](foo.png)"sv, .expected_html = R"(

)"sv }, - // Only width given: - TestCase { .markdown = "![](foo.png =100x)"sv, .expected_html = R"(

)"sv }, - // Only height given: - TestCase { .markdown = "![](foo.png =x200)"sv, .expected_html = R"(

)"sv }, - // Both width and height given - TestCase { .markdown = "![](foo.png =50x25)"sv, .expected_html = R"(

)"sv }, - // Size contains invalid width - TestCase { .markdown = "![](foo.png =1oox50)"sv, .expected_html = R"(

)"sv }, - // Size contains invalid height - TestCase { .markdown = "![](foo.png =900xfour)"sv, .expected_html = R"(

)"sv }, -}; - -TEST_CASE(test_image_size_markdown_extension) -{ - for (auto const& test_case : image_size_tests) { - auto document = Markdown::Document::parse(test_case.markdown); - auto raw_rendered_html = document->render_to_inline_html(); - auto rendered_html = StringView(raw_rendered_html).trim_whitespace(); - EXPECT_EQ(rendered_html, test_case.expected_html); - } -} diff --git a/Userland/Libraries/CMakeLists.txt b/Userland/Libraries/CMakeLists.txt index 6a9bf7abbd..d23ac123b7 100644 --- a/Userland/Libraries/CMakeLists.txt +++ b/Userland/Libraries/CMakeLists.txt @@ -22,7 +22,6 @@ add_subdirectory(LibKeyboard) add_subdirectory(LibLine) add_subdirectory(LibLocale) add_subdirectory(LibMain) -add_subdirectory(LibMarkdown) add_subdirectory(LibProtocol) add_subdirectory(LibRegex) add_subdirectory(LibRIFF) diff --git a/Userland/Libraries/LibMarkdown/Block.h b/Userland/Libraries/LibMarkdown/Block.h deleted file mode 100644 index c764c3ba18..0000000000 --- a/Userland/Libraries/LibMarkdown/Block.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Markdown { - -class Block { -public: - virtual ~Block() = default; - - virtual ByteString render_to_html(bool tight = false) const = 0; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const = 0; - virtual RecursionDecision walk(Visitor&) const = 0; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/BlockQuote.cpp b/Userland/Libraries/LibMarkdown/BlockQuote.cpp deleted file mode 100644 index 7066a1d71b..0000000000 --- a/Userland/Libraries/LibMarkdown/BlockQuote.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Markdown { - -ByteString BlockQuote::render_to_html(bool) const -{ - StringBuilder builder; - builder.append("
\n"sv); - builder.append(m_contents->render_to_html()); - builder.append("
\n"sv); - return builder.to_byte_string(); -} - -Vector BlockQuote::render_lines_for_terminal(size_t view_width) const -{ - Vector lines; - size_t child_width = view_width < 4 ? 0 : view_width - 4; - for (auto& line : m_contents->render_lines_for_terminal(child_width)) - lines.append(ByteString::formatted(" {}", line)); - - return lines; -} - -RecursionDecision BlockQuote::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - return m_contents->walk(visitor); -} - -OwnPtr
BlockQuote::parse(LineIterator& lines) -{ - lines.push_context(LineIterator::Context::block_quote()); - if (lines.is_end()) { - lines.pop_context(); - return {}; - } - - auto contents = ContainerBlock::parse(lines); - lines.pop_context(); - - if (!contents) - return {}; - - return make
(move(contents)); -} - -} diff --git a/Userland/Libraries/LibMarkdown/BlockQuote.h b/Userland/Libraries/LibMarkdown/BlockQuote.h deleted file mode 100644 index e20e4678d5..0000000000 --- a/Userland/Libraries/LibMarkdown/BlockQuote.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Markdown { - -class BlockQuote final : public Block { -public: - BlockQuote(OwnPtr contents) - : m_contents(move(contents)) - { - } - virtual ~BlockQuote() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - - static OwnPtr
parse(LineIterator& lines); - -private: - OwnPtr m_contents; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/CMakeLists.txt b/Userland/Libraries/LibMarkdown/CMakeLists.txt deleted file mode 100644 index ef316fbdac..0000000000 --- a/Userland/Libraries/LibMarkdown/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -set(SOURCES - BlockQuote.cpp - CodeBlock.cpp - CommentBlock.cpp - ContainerBlock.cpp - Document.cpp - Heading.cpp - HorizontalRule.cpp - LineIterator.cpp - List.cpp - Paragraph.cpp - SyntaxHighlighter.cpp - Table.cpp - Text.cpp -) - -serenity_lib(LibMarkdown markdown) -target_link_libraries(LibMarkdown PRIVATE LibUnicode LibJS LibRegex LibSyntax) diff --git a/Userland/Libraries/LibMarkdown/CodeBlock.cpp b/Userland/Libraries/LibMarkdown/CodeBlock.cpp deleted file mode 100644 index 6f89739028..0000000000 --- a/Userland/Libraries/LibMarkdown/CodeBlock.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * 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 { - -ByteString 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_byte_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', SplitBehavior::KeepEmpty)) - lines.append(ByteString::formatted("{}{}", indentation, line)); - - 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_byte_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_byte_string(), nullptr); -} -} diff --git a/Userland/Libraries/LibMarkdown/CodeBlock.h b/Userland/Libraries/LibMarkdown/CodeBlock.h deleted file mode 100644 index 0667e80eae..0000000000 --- a/Userland/Libraries/LibMarkdown/CodeBlock.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Markdown { - -class CodeBlock final : public Block { -public: - CodeBlock(ByteString const& language, ByteString const& style, ByteString const& code, Heading* current_section) - : m_code(move(code)) - , m_language(language) - , m_style(style) - , m_current_section(current_section) - { - } - virtual ~CodeBlock() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - static OwnPtr parse(LineIterator& lines, Heading* current_section); - -private: - ByteString m_code; - ByteString m_language; - ByteString m_style; - Heading* m_current_section; - - static OwnPtr parse_backticks(LineIterator& lines, Heading* current_section); - static OwnPtr parse_indent(LineIterator& lines); -}; - -} diff --git a/Userland/Libraries/LibMarkdown/CommentBlock.cpp b/Userland/Libraries/LibMarkdown/CommentBlock.cpp deleted file mode 100644 index e7054fbead..0000000000 --- a/Userland/Libraries/LibMarkdown/CommentBlock.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2021, Ben Wiederhake - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Markdown { - -ByteString CommentBlock::render_to_html(bool) const -{ - StringBuilder builder; - - builder.append("\n"sv); - - return builder.to_byte_string(); -} - -Vector CommentBlock::render_lines_for_terminal(size_t) const -{ - return Vector {}; -} - -RecursionDecision CommentBlock::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - // Normalize return value. - return RecursionDecision::Continue; -} - -OwnPtr CommentBlock::parse(LineIterator& lines) -{ - if (lines.is_end()) - return {}; - - constexpr auto comment_start = ""sv; - - StringView line = *lines; - if (!line.starts_with(comment_start)) - return {}; - line = line.substring_view(comment_start.length()); - - StringBuilder builder; - - while (true) { - // Invariant: At the beginning of the loop, `line` is valid and should be added to the builder. - bool ends_here = line.ends_with(comment_end); - if (ends_here) - line = line.substring_view(0, line.length() - comment_end.length()); - builder.append(line); - if (!ends_here) - builder.append('\n'); - - ++lines; - if (lines.is_end() || ends_here) { - break; - } - line = *lines; - } - - return make(builder.to_byte_string()); -} - -} diff --git a/Userland/Libraries/LibMarkdown/CommentBlock.h b/Userland/Libraries/LibMarkdown/CommentBlock.h deleted file mode 100644 index 388d7eab63..0000000000 --- a/Userland/Libraries/LibMarkdown/CommentBlock.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, Ben Wiederhake - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Markdown { - -class CommentBlock final : public Block { -public: - CommentBlock(ByteString const& comment) - : m_comment(comment) - { - } - virtual ~CommentBlock() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - static OwnPtr parse(LineIterator& lines); - -private: - ByteString m_comment; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/ContainerBlock.cpp b/Userland/Libraries/LibMarkdown/ContainerBlock.cpp deleted file mode 100644 index 45ae36ef2b..0000000000 --- a/Userland/Libraries/LibMarkdown/ContainerBlock.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Markdown { - -ByteString ContainerBlock::render_to_html(bool tight) const -{ - StringBuilder builder; - - for (size_t i = 0; i + 1 < m_blocks.size(); ++i) { - auto s = m_blocks[i]->render_to_html(tight); - builder.append(s); - } - - // I don't like this edge case. - if (m_blocks.size() != 0) { - auto& block = m_blocks[m_blocks.size() - 1]; - auto s = block->render_to_html(tight); - if (tight && dynamic_cast(block.ptr())) { - builder.append(s.substring_view(0, s.length() - 1)); - } else { - builder.append(s); - } - } - - return builder.to_byte_string(); -} - -Vector ContainerBlock::render_lines_for_terminal(size_t view_width) const -{ - Vector lines; - - for (auto& block : m_blocks) { - for (auto& line : block->render_lines_for_terminal(view_width)) - lines.append(move(line)); - } - - return lines; -} - -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 -static bool try_parse_block(LineIterator& lines, Vector>& blocks, Heading* current_section) -{ - OwnPtr block = CodeBlock::parse(lines, current_section); - if (!block) - return false; - blocks.append(block.release_nonnull()); - return true; -} - -template -static bool try_parse_block(LineIterator& lines, Vector>& blocks) -{ - OwnPtr block = BlockType::parse(lines); - if (!block) - return false; - blocks.append(block.release_nonnull()); - return true; -} - -OwnPtr ContainerBlock::parse(LineIterator& lines) -{ - Vector> blocks; - - StringBuilder paragraph_text; - Heading* current_section = nullptr; - - auto flush_paragraph = [&] { - if (paragraph_text.is_empty()) - return; - auto paragraph = make(Text::parse(paragraph_text.to_byte_string())); - blocks.append(move(paragraph)); - paragraph_text.clear(); - }; - - bool has_blank_lines = false; - bool has_trailing_blank_lines = false; - - while (true) { - if (lines.is_end()) - break; - - if ((*lines).is_whitespace()) { - has_trailing_blank_lines = true; - ++lines; - - flush_paragraph(); - continue; - } else { - has_blank_lines = has_blank_lines || has_trailing_blank_lines; - } - - bool heading = false; - if ((heading = try_parse_block(lines, blocks))) - current_section = dynamic_cast(blocks.last().ptr()); - - bool any = heading - || try_parse_block(lines, blocks) - || try_parse_block(lines, blocks) - || try_parse_block(lines, blocks) - // CodeBlock needs to know the current section's name for proper indentation - || try_parse_block(lines, blocks, current_section) - || try_parse_block(lines, blocks) - || try_parse_block
(lines, blocks); - - if (any) { - if (!paragraph_text.is_empty()) { - auto last_block = blocks.take_last(); - flush_paragraph(); - blocks.append(move(last_block)); - } - continue; - } - - if (!paragraph_text.is_empty()) - paragraph_text.append('\n'); - paragraph_text.append(*lines++); - } - - flush_paragraph(); - - return make(move(blocks), has_blank_lines, has_trailing_blank_lines); -} - -} diff --git a/Userland/Libraries/LibMarkdown/ContainerBlock.h b/Userland/Libraries/LibMarkdown/ContainerBlock.h deleted file mode 100644 index aaed1c0a50..0000000000 --- a/Userland/Libraries/LibMarkdown/ContainerBlock.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Markdown { - -class ContainerBlock final : public Block { -public: - ContainerBlock(Vector> blocks, bool has_blank_lines, bool has_trailing_blank_lines) - : m_blocks(move(blocks)) - , m_has_blank_lines(has_blank_lines) - , m_has_trailing_blank_lines(has_trailing_blank_lines) - { - } - - virtual ~ContainerBlock() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - - static OwnPtr parse(LineIterator& lines); - - bool has_blank_lines() const { return m_has_blank_lines; } - bool has_trailing_blank_lines() const { return m_has_trailing_blank_lines; } - - Vector> const& blocks() const { return m_blocks; } - -private: - Vector> m_blocks; - bool m_has_blank_lines; - bool m_has_trailing_blank_lines; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Document.cpp b/Userland/Libraries/LibMarkdown/Document.cpp deleted file mode 100644 index 3dfb21461a..0000000000 --- a/Userland/Libraries/LibMarkdown/Document.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Markdown { - -ByteString Document::render_to_html(StringView extra_head_contents) const -{ - StringBuilder builder; - builder.append(R"~~~( - - - -)~~~"sv); - if (!extra_head_contents.is_empty()) - builder.append(extra_head_contents); - builder.append(R"~~~( - - -)~~~"sv); - - builder.append(render_to_inline_html()); - - builder.append(R"~~~( - -)~~~"sv); - - return builder.to_byte_string(); -} - -ByteString Document::render_to_inline_html() const -{ - return m_container->render_to_html(); -} - -ErrorOr Document::render_for_terminal(size_t view_width) const -{ - StringBuilder builder; - for (auto& line : m_container->render_lines_for_terminal(view_width)) { - TRY(builder.try_append(line)); - TRY(builder.try_append("\n"sv)); - } - - return builder.to_string(); -} - -RecursionDecision Document::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - return m_container->walk(visitor); -} - -OwnPtr Document::parse(StringView str) -{ - Vector const lines_vec = str.lines(); - LineIterator lines(lines_vec.begin()); - return make(ContainerBlock::parse(lines)); -} - -} diff --git a/Userland/Libraries/LibMarkdown/Document.h b/Userland/Libraries/LibMarkdown/Document.h deleted file mode 100644 index bdb211dfc5..0000000000 --- a/Userland/Libraries/LibMarkdown/Document.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Markdown { - -class Document final { -public: - Document(OwnPtr container) - : m_container(move(container)) - { - } - ByteString render_to_html(StringView extra_head_contents = ""sv) const; - ByteString render_to_inline_html() const; - ErrorOr 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 parse(StringView); - -private: - OwnPtr m_container; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Forward.h b/Userland/Libraries/LibMarkdown/Forward.h deleted file mode 100644 index 5261a41d95..0000000000 --- a/Userland/Libraries/LibMarkdown/Forward.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, Ben Wiederhake - * - * 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 HorizontalRule; -class List; -class Paragraph; -class Table; - -class Visitor; - -} diff --git a/Userland/Libraries/LibMarkdown/Heading.cpp b/Userland/Libraries/LibMarkdown/Heading.cpp deleted file mode 100644 index 5d08e7d39e..0000000000 --- a/Userland/Libraries/LibMarkdown/Heading.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Markdown { - -ByteString Heading::render_to_html(bool) const -{ - auto input = Unicode::normalize(m_text.render_for_raw_print(), Unicode::NormalizationForm::NFD); - auto slugified = MUST(AK::slugify(input)); - return ByteString::formatted("# {}\n", m_level, slugified, slugified, m_text.render_to_html(), m_level); -} - -Vector Heading::render_lines_for_terminal(size_t) const -{ - StringBuilder builder; - - builder.append("\n\033[0;31;1m"sv); - switch (m_level) { - case 1: - case 2: - builder.append(m_text.render_for_terminal().to_uppercase()); - builder.append("\033[0m"sv); - break; - default: - builder.append(m_text.render_for_terminal()); - builder.append("\033[0m"sv); - break; - } - - return Vector { builder.to_byte_string() }; -} - -RecursionDecision Heading::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - return m_text.walk(visitor); -} - -OwnPtr Heading::parse(LineIterator& lines) -{ - if (lines.is_end()) - return {}; - - StringView line = *lines; - size_t indent = 0; - - // Allow for up to 3 spaces of indentation. - // https://spec.commonmark.org/0.30/#example-68 - for (size_t i = 0; i < 3; ++i) { - if (line[i] != ' ') - break; - - ++indent; - } - - size_t level; - - for (level = 0; indent + level < line.length(); level++) { - if (line[indent + level] != '#') - break; - } - - if (!level || indent + level >= line.length() || line[indent + level] != ' ' || level > 6) - return {}; - - StringView title_view = line.substring_view(indent + level + 1); - auto text = Text::parse(title_view); - auto heading = make(move(text), level); - - ++lines; - return heading; -} - -} diff --git a/Userland/Libraries/LibMarkdown/Heading.h b/Userland/Libraries/LibMarkdown/Heading.h deleted file mode 100644 index 04247a9774..0000000000 --- a/Userland/Libraries/LibMarkdown/Heading.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace Markdown { - -class Heading final : public Block { -public: - Heading(Text&& text, size_t level) - : m_text(move(text)) - , m_level(level) - { - VERIFY(m_level > 0); - } - virtual ~Heading() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - static OwnPtr parse(LineIterator& lines); - -private: - Text m_text; - size_t m_level { 0 }; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/HorizontalRule.cpp b/Userland/Libraries/LibMarkdown/HorizontalRule.cpp deleted file mode 100644 index 416b2e28ff..0000000000 --- a/Userland/Libraries/LibMarkdown/HorizontalRule.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Markdown { - -ByteString HorizontalRule::render_to_html(bool) const -{ - return "
\n"; -} - -Vector HorizontalRule::render_lines_for_terminal(size_t view_width) const -{ - StringBuilder builder(view_width + 1); - for (size_t i = 0; i < view_width; ++i) - builder.append('-'); - builder.append("\n\n"sv); - return Vector { builder.to_byte_string() }; -} - -RecursionDecision HorizontalRule::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - // Normalize return value. - return RecursionDecision::Continue; -} - -static Regex thematic_break_re("^ {0,3}([\\*\\-_])\\s*(\\1\\s*){2,}$"); - -OwnPtr HorizontalRule::parse(LineIterator& lines) -{ - if (lines.is_end()) - return {}; - - StringView line = *lines; - - auto match = thematic_break_re.match(line); - if (!match.success) - return {}; - - ++lines; - return make(); -} - -} diff --git a/Userland/Libraries/LibMarkdown/HorizontalRule.h b/Userland/Libraries/LibMarkdown/HorizontalRule.h deleted file mode 100644 index bdbc206d57..0000000000 --- a/Userland/Libraries/LibMarkdown/HorizontalRule.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2021, Andreas Kling - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace Markdown { - -class HorizontalRule final : public Block { -public: - HorizontalRule() = default; - virtual ~HorizontalRule() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - static OwnPtr parse(LineIterator& lines); -}; - -} diff --git a/Userland/Libraries/LibMarkdown/LineIterator.cpp b/Userland/Libraries/LibMarkdown/LineIterator.cpp deleted file mode 100644 index a471007474..0000000000 --- a/Userland/Libraries/LibMarkdown/LineIterator.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Markdown { - -void LineIterator::reset_ignore_prefix() -{ - for (auto& context : m_context_stack) { - context.ignore_prefix = false; - } -} - -Optional LineIterator::match_context(StringView line) const -{ - bool is_ws = line.is_whitespace(); - size_t offset = 0; - for (auto& context : m_context_stack) { - switch (context.type) { - case Context::Type::ListItem: - if (is_ws) - break; - - if (offset + context.indent > line.length()) - return {}; - - if (!context.ignore_prefix && !line.substring_view(offset, context.indent).is_whitespace()) - return {}; - - offset += context.indent; - - break; - case Context::Type::BlockQuote: - for (; offset < line.length() && line[offset] == ' '; ++offset) { } - if (offset >= line.length() || line[offset] != '>') { - return {}; - } - ++offset; - break; - } - - if (offset > line.length()) - return {}; - } - return line.substring_view(offset); -} - -bool LineIterator::is_end() const -{ - return m_iterator.is_end() || !match_context(*m_iterator).has_value(); -} - -StringView LineIterator::operator*() const -{ - auto line = match_context(*m_iterator); - VERIFY(line.has_value()); - return line.value(); -} - -} diff --git a/Userland/Libraries/LibMarkdown/LineIterator.h b/Userland/Libraries/LibMarkdown/LineIterator.h deleted file mode 100644 index cc76ef04b7..0000000000 --- a/Userland/Libraries/LibMarkdown/LineIterator.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Markdown { - -template -class FakePtr { -public: - FakePtr(T item) - : m_item(move(item)) - { - } - - T const* operator->() const { return &m_item; } - T* operator->() { return &m_item; } - -private: - T m_item; -}; - -class LineIterator { -public: - struct Context { - enum class Type { - ListItem, - BlockQuote, - }; - - Type type; - size_t indent; - bool ignore_prefix; - - static Context list_item(size_t indent) { return { Type::ListItem, indent, true }; } - static Context block_quote() { return { Type::BlockQuote, 0, false }; } - }; - - LineIterator(Vector::ConstIterator const& lines) - : m_iterator(lines) - { - } - - bool is_end() const; - StringView operator*() const; - - LineIterator operator++() - { - reset_ignore_prefix(); - ++m_iterator; - return *this; - } - - LineIterator operator++(int) - { - LineIterator tmp = *this; - reset_ignore_prefix(); - ++m_iterator; - return tmp; - } - - LineIterator operator+(ptrdiff_t delta) const - { - LineIterator copy = *this; - copy.reset_ignore_prefix(); - copy.m_iterator = copy.m_iterator + delta; - return copy; - } - - LineIterator operator-(ptrdiff_t delta) const - { - LineIterator copy = *this; - copy.reset_ignore_prefix(); - copy.m_iterator = copy.m_iterator - delta; - return copy; - } - - ptrdiff_t operator-(LineIterator const& other) const { return m_iterator - other.m_iterator; } - - FakePtr operator->() const { return FakePtr(operator*()); } - - void push_context(Context context) { m_context_stack.append(move(context)); } - void pop_context() { m_context_stack.take_last(); } - -private: - void reset_ignore_prefix(); - Optional match_context(StringView line) const; - - Vector::ConstIterator m_iterator; - Vector m_context_stack; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/List.cpp b/Userland/Libraries/LibMarkdown/List.cpp deleted file mode 100644 index c953177892..0000000000 --- a/Userland/Libraries/LibMarkdown/List.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Markdown { - -ByteString List::render_to_html(bool) const -{ - StringBuilder builder; - - char const* tag = m_is_ordered ? "ol" : "ul"; - builder.appendff("<{}", tag); - - if (m_start_number != 1) - builder.appendff(" start=\"{}\"", m_start_number); - - builder.append(">\n"sv); - - for (auto& item : m_items) { - builder.append("
  • "sv); - if (!m_is_tight || (item->blocks().size() != 0 && !dynamic_cast(item->blocks()[0].ptr()))) - builder.append('\n'); - builder.append(item->render_to_html(m_is_tight)); - builder.append("
  • \n"sv); - } - - builder.appendff("\n", tag); - - return builder.to_byte_string(); -} - -Vector List::render_lines_for_terminal(size_t view_width) const -{ - Vector lines; - - int i = 0; - for (auto& item : m_items) { - auto item_lines = item->render_lines_for_terminal(view_width); - auto first_line = item_lines.take_first(); - - StringBuilder builder; - builder.append(" "sv); - if (m_is_ordered) - builder.appendff("{}.", ++i); - else - builder.append('*'); - auto item_indentation = builder.length(); - - builder.append(first_line); - - lines.append(builder.to_byte_string()); - - for (auto& line : item_lines) { - builder.clear(); - builder.append(ByteString::repeated(' ', item_indentation)); - builder.append(line); - lines.append(builder.to_byte_string()); - } - } - - return lines; -} - -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::parse(LineIterator& lines) -{ - Vector> items; - - bool first = true; - bool is_ordered = false; - - bool is_tight = true; - bool has_trailing_blank_lines = false; - size_t start_number = 1; - - while (!lines.is_end()) { - - size_t offset = 0; - - StringView line = *lines; - - bool appears_unordered = false; - - while (offset < line.length() && line[offset] == ' ') - ++offset; - - if (offset + 2 <= line.length()) { - if (line[offset + 1] == ' ' && (line[offset] == '*' || line[offset] == '-' || line[offset] == '+')) { - appears_unordered = true; - offset++; - } - } - - bool appears_ordered = false; - for (size_t i = offset; i < 10 && i < line.length(); i++) { - char ch = line[i]; - if ('0' <= ch && ch <= '9') - continue; - if (ch == '.' || ch == ')') - if (i + 1 < line.length() && line[i + 1] == ' ') { - auto maybe_start_number = line.substring_view(offset, i - offset).to_number(); - if (!maybe_start_number.has_value()) - break; - if (first) - start_number = maybe_start_number.value(); - appears_ordered = true; - offset = i + 1; - } - break; - } - - VERIFY(!(appears_unordered && appears_ordered)); - if (!appears_unordered && !appears_ordered) { - if (first) - return {}; - - break; - } - - while (offset < line.length() && line[offset] == ' ') - offset++; - - if (first) { - is_ordered = appears_ordered; - } else if (appears_ordered != is_ordered) { - break; - } - - is_tight = is_tight && !has_trailing_blank_lines; - - lines.push_context(LineIterator::Context::list_item(offset)); - - auto list_item = ContainerBlock::parse(lines); - is_tight = is_tight && !list_item->has_blank_lines(); - has_trailing_blank_lines = has_trailing_blank_lines || list_item->has_trailing_blank_lines(); - items.append(move(list_item)); - - lines.pop_context(); - - first = false; - } - - return make(move(items), is_ordered, is_tight, start_number); -} - -} diff --git a/Userland/Libraries/LibMarkdown/List.h b/Userland/Libraries/LibMarkdown/List.h deleted file mode 100644 index 1ac888c584..0000000000 --- a/Userland/Libraries/LibMarkdown/List.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Markdown { - -class List final : public Block { -public: - List(Vector> items, bool is_ordered, bool is_tight, size_t start_number) - : m_items(move(items)) - , m_is_ordered(is_ordered) - , m_is_tight(is_tight) - , m_start_number(start_number) - { - } - virtual ~List() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - - static OwnPtr parse(LineIterator& lines); - -private: - Vector> m_items; - bool m_is_ordered { false }; - bool m_is_tight { false }; - size_t m_start_number { 1 }; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Paragraph.cpp b/Userland/Libraries/LibMarkdown/Paragraph.cpp deleted file mode 100644 index 4110d26c59..0000000000 --- a/Userland/Libraries/LibMarkdown/Paragraph.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Markdown { - -ByteString Paragraph::render_to_html(bool tight) const -{ - StringBuilder builder; - - if (!tight) - builder.append("

    "sv); - - builder.append(m_text.render_to_html()); - - if (!tight) - builder.append("

    "sv); - - builder.append('\n'); - - return builder.to_byte_string(); -} - -Vector Paragraph::render_lines_for_terminal(size_t) const -{ - return Vector { ByteString::formatted(" {}", m_text.render_for_terminal()), "" }; -} - -RecursionDecision Paragraph::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - return m_text.walk(visitor); -} - -} diff --git a/Userland/Libraries/LibMarkdown/Paragraph.h b/Userland/Libraries/LibMarkdown/Paragraph.h deleted file mode 100644 index ed2599b5bc..0000000000 --- a/Userland/Libraries/LibMarkdown/Paragraph.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include - -namespace Markdown { - -class Paragraph final : public Block { -public: - Paragraph(Text text) - : m_text(move(text)) - { - } - - virtual ~Paragraph() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - -private: - Text m_text; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp deleted file mode 100644 index 16380e8ba0..0000000000 --- a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2023, Maciej - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include - -namespace Markdown { - -Syntax::Language SyntaxHighlighter::language() const -{ - return Syntax::Language::Markdown; -} - -Optional SyntaxHighlighter::comment_prefix() const -{ - return {}; -} - -Optional SyntaxHighlighter::comment_suffix() const -{ - return {}; -} - -enum class Token { - Default, - Header, - Code -}; - -void SyntaxHighlighter::rehighlight(Palette const& palette) -{ - auto text = m_client->get_text(); - - Vector spans; - - auto append_header = [&](Syntax::TextRange const& range) { - Gfx::TextAttributes attributes; - attributes.color = palette.base_text(); - attributes.bold = true; - Syntax::TextDocumentSpan span { - .range = range, - .attributes = attributes, - .data = static_cast(Token::Header), - .is_skippable = false - }; - spans.append(span); - }; - - auto append_code_block = [&](Syntax::TextRange const& range) { - Gfx::TextAttributes attributes; - attributes.color = palette.syntax_string(); - Syntax::TextDocumentSpan span { - .range = range, - .attributes = attributes, - .data = static_cast(Token::Code), - .is_skippable = false - }; - spans.append(span); - }; - - // Headers, code blocks - { - size_t line_index = 0; - Optional code_block_start; - for (auto const& line : StringView(text).lines()) { - if (line.starts_with("```"sv)) { - if (code_block_start.has_value()) { - append_code_block({ { *code_block_start, 0 }, { line_index, line.length() } }); - code_block_start = {}; - } else { - code_block_start = line_index; - } - } - - if (!code_block_start.has_value()) { - auto trimmed = line.trim_whitespace(TrimMode::Left); - size_t indent = line.length() - trimmed.length(); - if (indent < 4 && trimmed.starts_with("#"sv)) { - append_header({ { line_index, 0 }, { line_index, line.length() } }); - } - } - line_index++; - } - } - - // TODO: Highlight text nodes (em, strong, link, image) - - m_client->do_set_spans(spans); -} - -Vector SyntaxHighlighter::matching_token_pairs_impl() const -{ - return {}; -} - -bool SyntaxHighlighter::token_types_equal(u64 lhs, u64 rhs) const -{ - return static_cast(lhs) == static_cast(rhs); -} - -} diff --git a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h b/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h deleted file mode 100644 index 0ea40226d9..0000000000 --- a/Userland/Libraries/LibMarkdown/SyntaxHighlighter.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023, Maciej - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Markdown { - -class SyntaxHighlighter : public Syntax::Highlighter { - - virtual Syntax::Language language() const override; - virtual Optional comment_prefix() const override; - virtual Optional comment_suffix() const override; - virtual void rehighlight(Palette const&) override; - virtual Vector matching_token_pairs_impl() const override; - virtual bool token_types_equal(u64, u64) const override; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Table.cpp b/Userland/Libraries/LibMarkdown/Table.cpp deleted file mode 100644 index d65dfdf69e..0000000000 --- a/Userland/Libraries/LibMarkdown/Table.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2020, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include - -namespace Markdown { - -Vector Table::render_lines_for_terminal(size_t view_width) const -{ - auto unit_width_length = view_width == 0 ? 4 : ((float)(view_width - m_columns.size()) / (float)m_total_width); - StringBuilder builder; - Vector lines; - - auto write_aligned = [&](auto const& text, auto width, auto alignment) { - size_t original_length = text.terminal_length(); - auto string = text.render_for_terminal(); - if (alignment == Alignment::Center) { - auto padding_length = (width - original_length) / 2; - // FIXME: We're using a StringView literal to bypass the compile-time AK::Format checking here, since it can't handle the "}}" - builder.appendff("{:{1}}"sv, "", (int)padding_length); - builder.append(string); - builder.appendff("{:{1}}"sv, "", (int)padding_length); - if ((width - original_length) % 2) - builder.append(' '); - } else { - // FIXME: We're using StringView literals to bypass the compile-time AK::Format checking here, since it can't handle the "}}" - builder.appendff(alignment == Alignment::Left ? "{:<{1}}"sv : "{:>{1}}"sv, string, (int)(width + (string.length() - original_length))); - } - }; - - bool first = true; - for (auto& col : m_columns) { - if (!first) - builder.append('|'); - first = false; - size_t width = col.relative_width * unit_width_length; - write_aligned(col.header, width, col.alignment); - } - - lines.append(builder.to_byte_string()); - builder.clear(); - - for (size_t i = 0; i < view_width; ++i) - builder.append('-'); - lines.append(builder.to_byte_string()); - builder.clear(); - - for (size_t i = 0; i < m_row_count; ++i) { - bool first = true; - for (auto& col : m_columns) { - VERIFY(i < col.rows.size()); - auto& cell = col.rows[i]; - - if (!first) - builder.append('|'); - first = false; - - size_t width = col.relative_width * unit_width_length; - write_aligned(cell, width, col.alignment); - } - lines.append(builder.to_byte_string()); - builder.clear(); - } - - lines.append(""); - - return lines; -} - -ByteString Table::render_to_html(bool) const -{ - auto alignment_string = [](Alignment alignment) { - switch (alignment) { - case Alignment::Center: - return "center"sv; - case Alignment::Left: - return "left"sv; - case Alignment::Right: - return "right"sv; - } - VERIFY_NOT_REACHED(); - }; - - StringBuilder builder; - - builder.append("
    "sv); - builder.append(""sv); - builder.append(""sv); - for (auto& column : m_columns) { - builder.appendff(""sv); - } - builder.append(""sv); - builder.append(""sv); - builder.append(""sv); - for (size_t i = 0; i < m_row_count; ++i) { - builder.append(""sv); - for (auto& column : m_columns) { - VERIFY(i < column.rows.size()); - builder.appendff(""sv); - } - builder.append(""sv); - } - builder.append(""sv); - builder.append("
    ", alignment_string(column.alignment)); - builder.append(column.header.render_to_html()); - builder.append("
    ", alignment_string(column.alignment)); - builder.append(column.rows[i].render_to_html()); - builder.append("
    "sv); - - return builder.to_byte_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::parse(LineIterator& lines) -{ - auto peek_it = lines; - auto first_line = *peek_it; - if (!first_line.starts_with('|')) - return {}; - - ++peek_it; - - if (peek_it.is_end()) - return {}; - - auto header_segments = first_line.split_view('|', SplitBehavior::KeepEmpty); - auto header_delimiters = peek_it->split_view('|', SplitBehavior::KeepEmpty); - - if (!header_segments.is_empty()) - header_segments.take_first(); - if (!header_segments.is_empty() && header_segments.last().is_empty()) - header_segments.take_last(); - - if (!header_delimiters.is_empty()) - header_delimiters.take_first(); - if (!header_delimiters.is_empty() && header_delimiters.last().is_empty()) - header_delimiters.take_last(); - - ++peek_it; - - if (header_delimiters.size() != header_segments.size()) - return {}; - - if (header_delimiters.is_empty()) - return {}; - - size_t total_width = 0; - - auto table = make
    (); - table->m_columns.resize(header_delimiters.size()); - - for (size_t i = 0; i < header_segments.size(); ++i) { - auto text = Text::parse(header_segments[i]); - - auto& column = table->m_columns[i]; - - column.header = move(text); - - auto delimiter = header_delimiters[i].trim_whitespace(); - - auto align_left = delimiter.starts_with(':'); - auto align_right = delimiter != ":" && delimiter.ends_with(':'); - - if (align_left) - delimiter = delimiter.substring_view(1, delimiter.length() - 1); - if (align_right) - delimiter = delimiter.substring_view(0, delimiter.length() - 1); - - if (align_left && align_right) - column.alignment = Alignment::Center; - else if (align_right) - column.alignment = Alignment::Right; - else - column.alignment = Alignment::Left; - - size_t relative_width = delimiter.length(); - for (auto ch : delimiter) { - if (ch != '-') { - dbgln_if(MARKDOWN_DEBUG, "Invalid character _{}_ in table heading delimiter (ignored)", ch); - --relative_width; - } - } - - column.relative_width = relative_width; - total_width += relative_width; - } - - table->m_total_width = total_width; - - for (off_t i = 0; i < peek_it - lines; ++i) - ++lines; - - size_t row_count = 0; - ++lines; - while (!lines.is_end()) { - auto line = *lines; - if (!line.starts_with('|')) - break; - - ++lines; - - auto segments = line.split_view('|', SplitBehavior::KeepEmpty); - segments.take_first(); - if (!segments.is_empty() && segments.last().is_empty()) - segments.take_last(); - ++row_count; - - for (size_t i = 0; i < header_segments.size(); ++i) { - if (i >= segments.size()) { - // Ran out of segments, but still have headers. - // Just make an empty cell. - table->m_columns[i].rows.append(Text::parse(""sv)); - } else { - auto text = Text::parse(segments[i]); - table->m_columns[i].rows.append(move(text)); - } - } - } - - table->m_row_count = row_count; - - 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; -} - -} diff --git a/Userland/Libraries/LibMarkdown/Table.h b/Userland/Libraries/LibMarkdown/Table.h deleted file mode 100644 index a147ad015d..0000000000 --- a/Userland/Libraries/LibMarkdown/Table.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Markdown { - -class Table final : public Block { -public: - enum class Alignment { - Center, - Left, - Right, - }; - - struct Column { - Text header; - Vector rows; - Alignment alignment { Alignment::Left }; - size_t relative_width { 0 }; - - RecursionDecision walk(Visitor&) const; - }; - - Table() = default; - virtual ~Table() override = default; - - virtual ByteString render_to_html(bool tight = false) const override; - virtual Vector render_lines_for_terminal(size_t view_width = 0) const override; - virtual RecursionDecision walk(Visitor&) const override; - static OwnPtr
    parse(LineIterator& lines); - - Vector const& columns() const { return m_columns; } - -private: - Vector m_columns; - size_t m_total_width { 1 }; - size_t m_row_count { 0 }; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Text.cpp b/Userland/Libraries/LibMarkdown/Text.cpp deleted file mode 100644 index 4d9cc6c3a9..0000000000 --- a/Userland/Libraries/LibMarkdown/Text.cpp +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Copyright (c) 2019-2020, Sergey Bugaev - * Copyright (c) 2021, Peter Elliott - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include - -namespace Markdown { - -void Text::EmphasisNode::render_to_html(StringBuilder& builder) const -{ - builder.append((strong) ? ""sv : ""sv); - child->render_to_html(builder); - builder.append((strong) ? ""sv : ""sv); -} - -void Text::EmphasisNode::render_for_terminal(StringBuilder& builder) const -{ - if (strong) { - builder.append("\e[1m"sv); - child->render_for_terminal(builder); - builder.append("\e[22m"sv); - } else { - builder.append("\e[3m"sv); - child->render_for_terminal(builder); - builder.append("\e[23m"sv); - } -} - -void Text::EmphasisNode::render_for_raw_print(StringBuilder& builder) const -{ - child->render_for_raw_print(builder); -} - -size_t Text::EmphasisNode::terminal_length() const -{ - 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 -{ - builder.append(""sv); - code->render_to_html(builder); - builder.append(""sv); -} - -void Text::CodeNode::render_for_terminal(StringBuilder& builder) const -{ - builder.append("\e[1m"sv); - code->render_for_terminal(builder); - builder.append("\e[22m"sv); -} - -void Text::CodeNode::render_for_raw_print(StringBuilder& builder) const -{ - code->render_for_raw_print(builder); -} - -size_t Text::CodeNode::terminal_length() const -{ - 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 -{ - builder.append("
    "sv); -} - -void Text::BreakNode::render_for_terminal(StringBuilder&) const -{ -} - -void Text::BreakNode::render_for_raw_print(StringBuilder&) const -{ -} - -size_t Text::BreakNode::terminal_length() const -{ - 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 -{ - builder.append(escape_html_entities(text)); -} - -void Text::TextNode::render_for_raw_print(StringBuilder& builder) const -{ - builder.append(text); -} - -void Text::TextNode::render_for_terminal(StringBuilder& builder) const -{ - if (collapsible && (text == "\n" || text.is_whitespace())) { - builder.append(' '); - } else { - builder.append(text); - } -} - -size_t Text::TextNode::terminal_length() const -{ - if (collapsible && text.is_whitespace()) { - return 1; - } - - 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 -{ - if (is_image) { - builder.append("\""sv);render_to_html(builder); - builder.append("\" >"sv); - } else { - builder.append(""sv); - text->render_to_html(builder); - builder.append(""sv); - } -} - -void Text::LinkNode::render_for_raw_print(StringBuilder& builder) const -{ - text->render_for_raw_print(builder); -} - -void Text::LinkNode::render_for_terminal(StringBuilder& builder) const -{ - bool is_linked = href.contains("://"sv); - if (is_linked) { - builder.append("\033[0;34m\e]8;;"sv); - builder.append(href); - builder.append("\e\\"sv); - } - - text->render_for_terminal(builder); - - if (is_linked) { - builder.appendff(" <{}>", href); - builder.append("\033]8;;\033\\\033[0m"sv); - } -} - -size_t Text::LinkNode::terminal_length() const -{ - 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 -{ - for (auto& child : children) { - child->render_to_html(builder); - } -} - -void Text::MultiNode::render_for_raw_print(StringBuilder& builder) const -{ - for (auto& child : children) { - child->render_for_raw_print(builder); - } -} - -void Text::MultiNode::render_for_terminal(StringBuilder& builder) const -{ - for (auto& child : children) { - child->render_for_terminal(builder); - } -} - -size_t Text::MultiNode::terminal_length() const -{ - size_t length = 0; - for (auto& child : children) { - length += child->terminal_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; -} - -void Text::StrikeThroughNode::render_to_html(StringBuilder& builder) const -{ - builder.append(""sv); - striked_text->render_to_html(builder); - builder.append(""sv); -} - -void Text::StrikeThroughNode::render_for_raw_print(StringBuilder& builder) const -{ - striked_text->render_for_raw_print(builder); -} - -void Text::StrikeThroughNode::render_for_terminal(StringBuilder& builder) const -{ - builder.append("\e[9m"sv); - striked_text->render_for_terminal(builder); - builder.append("\e[29m"sv); -} - -size_t Text::StrikeThroughNode::terminal_length() const -{ - return striked_text->terminal_length(); -} - -RecursionDecision Text::StrikeThroughNode::walk(Visitor& visitor) const -{ - RecursionDecision rd = visitor.visit(*this); - if (rd != RecursionDecision::Recurse) - return rd; - - return striked_text->walk(visitor); -} - -size_t Text::terminal_length() const -{ - return m_node->terminal_length(); -} - -ByteString Text::render_to_html() const -{ - StringBuilder builder; - m_node->render_to_html(builder); - return builder.to_byte_string().trim(" \n\t"sv); -} - -ByteString Text::render_for_raw_print() const -{ - StringBuilder builder; - m_node->render_for_raw_print(builder); - return builder.to_byte_string().trim(" \n\t"sv); -} - -ByteString Text::render_for_terminal() const -{ - StringBuilder builder; - m_node->render_for_terminal(builder); - return builder.to_byte_string().trim(" \n\t"sv); -} - -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 str) -{ - Text text; - auto const tokens = tokenize(str); - auto iterator = tokens.begin(); - text.m_node = parse_sequence(iterator, false); - return text; -} - -static bool flanking(StringView str, size_t start, size_t end, int dir) -{ - ssize_t next = ((dir > 0) ? end : start) + dir; - if (next < 0 || next >= (ssize_t)str.length()) - return false; - - if (isspace(str[next])) - return false; - - if (!ispunct(str[next])) - return true; - - ssize_t prev = ((dir > 0) ? start : end) - dir; - if (prev < 0 || prev >= (ssize_t)str.length()) - return true; - - return isspace(str[prev]) || ispunct(str[prev]); -} - -Vector Text::tokenize(StringView str) -{ - Vector tokens; - StringBuilder current_token; - - auto flush_run = [&](bool left_flanking, bool right_flanking, bool punct_before, bool punct_after, bool is_run) { - if (current_token.is_empty()) - return; - - tokens.append({ - current_token.to_byte_string(), - left_flanking, - right_flanking, - punct_before, - punct_after, - is_run, - }); - current_token.clear(); - }; - - auto flush_token = [&]() { - flush_run(false, false, false, false, false); - }; - - bool in_space = false; - - for (size_t offset = 0; offset < str.length(); ++offset) { - auto has = [&](StringView seq) { - if (offset + seq.length() > str.length()) - return false; - - return str.substring_view(offset, seq.length()) == seq; - }; - - auto expect = [&](StringView seq) { - VERIFY(has(seq)); - flush_token(); - current_token.append(seq); - flush_token(); - offset += seq.length() - 1; - }; - - char ch = str[offset]; - if (ch != ' ' && in_space) { - flush_token(); - in_space = false; - } - - if (ch == '\\' && offset + 1 < str.length() && ispunct(str[offset + 1])) { - current_token.append(str[offset + 1]); - ++offset; - } else if (ch == '*' || ch == '_' || ch == '`' || ch == '~') { - flush_token(); - - char delim = ch; - size_t run_offset; - for (run_offset = offset; run_offset < str.length() && str[run_offset] == delim; ++run_offset) { - current_token.append(str[run_offset]); - } - - flush_run(flanking(str, offset, run_offset - 1, +1), - flanking(str, offset, run_offset - 1, -1), - offset > 0 && ispunct(str[offset - 1]), - run_offset < str.length() && ispunct(str[run_offset]), - true); - offset = run_offset - 1; - - } else if (ch == ' ') { - if (!in_space) { - flush_token(); - in_space = true; - } - current_token.append(ch); - } else if (has("\n"sv)) { - expect("\n"sv); - } else if (has("["sv)) { - expect("["sv); - } else if (has("!["sv)) { - expect("!["sv); - } else if (has("]("sv)) { - expect("]("sv); - } else if (has(")"sv)) { - expect(")"sv); - } else { - current_token.append(ch); - } - } - flush_token(); - return tokens; -} - -NonnullOwnPtr Text::parse_sequence(Vector::ConstIterator& tokens, bool in_link) -{ - auto node = make(); - - for (; !tokens.is_end(); ++tokens) { - if (tokens->is_space()) { - node->children.append(parse_break(tokens)); - } else if (*tokens == "\n"sv) { - node->children.append(parse_newline(tokens)); - } else if (tokens->is_run) { - switch (tokens->run_char()) { - case '*': - case '_': - node->children.append(parse_emph(tokens, in_link)); - break; - case '`': - node->children.append(parse_code(tokens)); - break; - case '~': - node->children.append(parse_strike_through(tokens)); - break; - } - } else if (*tokens == "["sv || *tokens == "!["sv) { - node->children.append(parse_link(tokens)); - } else if (in_link && *tokens == "]("sv) { - return node; - } else { - node->children.append(make(tokens->data)); - } - - if (in_link && !tokens.is_end() && *tokens == "]("sv) - return node; - - if (tokens.is_end()) - break; - } - return node; -} - -NonnullOwnPtr Text::parse_break(Vector::ConstIterator& tokens) -{ - auto next_tok = tokens + 1; - if (next_tok.is_end() || *next_tok != "\n"sv) - return make(tokens->data); - - if (tokens->data.length() >= 2) - return make(); - - return make(); -} - -NonnullOwnPtr Text::parse_newline(Vector::ConstIterator& tokens) -{ - auto node = make(tokens->data); - auto next_tok = tokens + 1; - if (!next_tok.is_end() && next_tok->is_space()) - // Skip whitespace after newline. - ++tokens; - - return node; -} - -bool Text::can_open(Token const& opening) -{ - return (opening.run_char() == '~' && opening.left_flanking) || (opening.run_char() == '*' && opening.left_flanking) || (opening.run_char() == '_' && opening.left_flanking && (!opening.right_flanking || opening.punct_before)); -} - -bool Text::can_close_for(Token const& opening, Text::Token const& closing) -{ - if (opening.run_char() != closing.run_char()) - return false; - - if (opening.run_length() != closing.run_length()) - return false; - - return (opening.run_char() == '~' && closing.right_flanking) || (opening.run_char() == '*' && closing.right_flanking) || (opening.run_char() == '_' && closing.right_flanking && (!closing.left_flanking || closing.punct_after)); -} - -NonnullOwnPtr Text::parse_emph(Vector::ConstIterator& tokens, bool in_link) -{ - auto opening = *tokens; - - // Check that the opening delimiter run is properly flanking. - if (!can_open(opening)) - return make(opening.data); - - auto child = make(); - for (++tokens; !tokens.is_end(); ++tokens) { - if (tokens->is_space()) { - child->children.append(parse_break(tokens)); - } else if (*tokens == "\n"sv) { - child->children.append(parse_newline(tokens)); - } else if (tokens->is_run) { - if (can_close_for(opening, *tokens)) { - return make(opening.run_length() >= 2, move(child)); - } - - switch (tokens->run_char()) { - case '*': - case '_': - child->children.append(parse_emph(tokens, in_link)); - break; - case '`': - child->children.append(parse_code(tokens)); - break; - case '~': - child->children.append(parse_strike_through(tokens)); - break; - } - } else if (*tokens == "["sv || *tokens == "!["sv) { - child->children.append(parse_link(tokens)); - } else if (in_link && *tokens == "]("sv) { - child->children.prepend(make(opening.data)); - return child; - } else { - child->children.append(make(tokens->data)); - } - - if (in_link && !tokens.is_end() && *tokens == "]("sv) { - child->children.prepend(make(opening.data)); - return child; - } - - if (tokens.is_end()) - break; - } - child->children.prepend(make(opening.data)); - return child; -} - -NonnullOwnPtr Text::parse_code(Vector::ConstIterator& tokens) -{ - auto opening = *tokens; - - auto is_closing = [&](Token const& token) { - return token.is_run && token.run_char() == '`' && token.run_length() == opening.run_length(); - }; - - bool is_all_whitespace = true; - auto code = make(); - for (auto iterator = tokens + 1; !iterator.is_end(); ++iterator) { - if (is_closing(*iterator)) { - tokens = iterator; - - // Strip first and last space, when appropriate. - if (!is_all_whitespace) { - auto& first = dynamic_cast(*code->children.first()); - auto& last = dynamic_cast(*code->children.last()); - if (first.text.starts_with(' ') && last.text.ends_with(' ')) { - first.text = first.text.substring(1); - last.text = last.text.substring(0, last.text.length() - 1); - } - } - - return make(move(code)); - } - - is_all_whitespace = is_all_whitespace && iterator->data.is_whitespace(); - code->children.append(make((*iterator == "\n"sv) ? " " : iterator->data, false)); - } - - return make(opening.data); -} - -NonnullOwnPtr Text::parse_link(Vector::ConstIterator& tokens) -{ - auto opening = *tokens++; - bool is_image = opening == "!["sv; - - auto link_text = parse_sequence(tokens, true); - - if (tokens.is_end() || *tokens != "]("sv) { - link_text->children.prepend(make(opening.data)); - return link_text; - } - auto separator = *tokens; - VERIFY(separator == "]("sv); - - Optional image_width; - Optional image_height; - - auto parse_image_dimensions = [&](StringView dimensions) -> bool { - if (!dimensions.starts_with('=')) - return false; - - ArmedScopeGuard clear_image_dimensions = [&] { - image_width = {}; - image_height = {}; - }; - - auto dimension_seperator = dimensions.find('x', 1); - if (!dimension_seperator.has_value()) - return false; - - auto width_string = dimensions.substring_view(1, *dimension_seperator - 1); - if (!width_string.is_empty()) { - auto width = width_string.to_number(); - if (!width.has_value()) - return false; - image_width = width; - } - - auto height_start = *dimension_seperator + 1; - if (height_start < dimensions.length()) { - auto height_string = dimensions.substring_view(height_start); - auto height = height_string.to_number(); - if (!height.has_value()) - return false; - image_height = height; - } - - clear_image_dimensions.disarm(); - return true; - }; - - StringBuilder address; - for (auto iterator = tokens + 1; !iterator.is_end(); ++iterator) { - // FIXME: What to do if there's multiple dimension tokens? - if (is_image && !address.is_empty() && parse_image_dimensions(iterator->data)) - continue; - - if (*iterator == ")"sv) { - tokens = iterator; - - ByteString href = address.to_byte_string().trim_whitespace(); - - // Add file:// if the link is an absolute path otherwise it will be assumed relative. - if (AK::StringUtils::starts_with(href, "/"sv, CaseSensitivity::CaseSensitive)) - href = ByteString::formatted("file://{}", href); - - return make(is_image, move(link_text), move(href), image_width, image_height); - } - - address.append(iterator->data); - } - - link_text->children.prepend(make(opening.data)); - link_text->children.append(make(separator.data)); - return link_text; -} - -NonnullOwnPtr Text::parse_strike_through(Vector::ConstIterator& tokens) -{ - auto opening = *tokens; - - auto is_closing = [&](Token const& token) { - return token.is_run && token.run_char() == '~' && token.run_length() == opening.run_length(); - }; - - bool is_all_whitespace = true; - auto striked_text = make(); - for (auto iterator = tokens + 1; !iterator.is_end(); ++iterator) { - if (is_closing(*iterator)) { - tokens = iterator; - - if (!is_all_whitespace) { - auto& first = dynamic_cast(*striked_text->children.first()); - auto& last = dynamic_cast(*striked_text->children.last()); - if (first.text.starts_with(' ') && last.text.ends_with(' ')) { - first.text = first.text.substring(1); - last.text = last.text.substring(0, last.text.length() - 1); - } - } - - return make(move(striked_text)); - } - - is_all_whitespace = is_all_whitespace && iterator->data.is_whitespace(); - striked_text->children.append(make((*iterator == "\n"sv) ? " " : iterator->data, false)); - } - - return make(opening.data); -} - -} diff --git a/Userland/Libraries/LibMarkdown/Text.h b/Userland/Libraries/LibMarkdown/Text.h deleted file mode 100644 index eeb719b5a7..0000000000 --- a/Userland/Libraries/LibMarkdown/Text.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * 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 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 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 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 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 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 text; - ByteString href; - Optional image_width; - Optional image_height; - - LinkNode(bool is_image, NonnullOwnPtr text, ByteString 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 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> 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 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 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 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; -}; - -} diff --git a/Userland/Libraries/LibMarkdown/Visitor.h b/Userland/Libraries/LibMarkdown/Visitor.h deleted file mode 100644 index 14a33da9f4..0000000000 --- a/Userland/Libraries/LibMarkdown/Visitor.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2021, Ben Wiederhake - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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(CommentBlock 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::StrikeThroughNode const&) { return RecursionDecision::Recurse; } - virtual RecursionDecision visit(Text::TextNode const&) { return RecursionDecision::Recurse; } - - virtual RecursionDecision visit(ByteString const&) { return RecursionDecision::Recurse; } -}; - -} diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 0456826811..b832ff6d03 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -750,7 +750,7 @@ set(GENERATED_SOURCES serenity_lib(LibWeb web) -target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibMarkdown LibHTTP LibGemini LibGfx LibIPC LibLocale LibRegex LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL LibURL LibTLS) +target_link_libraries(LibWeb PRIVATE LibCore LibCrypto LibJS LibHTTP LibGemini LibGfx LibIPC LibLocale LibRegex LibSyntax LibTextCodec LibUnicode LibAudio LibVideo LibWasm LibXML LibIDL LibURL LibTLS) if (HAS_ACCELERATED_GRAPHICS) target_link_libraries(LibWeb PRIVATE ${ACCEL_GFX_LIBS}) diff --git a/Userland/Libraries/LibWeb/DOM/DocumentLoading.cpp b/Userland/Libraries/LibWeb/DOM/DocumentLoading.cpp index d0523bc036..a797cdf9b9 100644 --- a/Userland/Libraries/LibWeb/DOM/DocumentLoading.cpp +++ b/Userland/Libraries/LibWeb/DOM/DocumentLoading.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -37,74 +36,6 @@ static void convert_to_xml_error_document(DOM::Document& document, String error_ MUST(document.append_child(html_element)); } -static WebIDL::ExceptionOr> load_markdown_document(HTML::NavigationParams const& navigation_params) -{ - auto extra_head_contents = R"~~~( - - -)~~~"sv; - - return create_document_for_inline_content(navigation_params.navigable.ptr(), navigation_params.id, [&](DOM::Document& document) { - auto& realm = document.realm(); - auto process_body = JS::create_heap_function(realm.heap(), [&document, url = navigation_params.response->url().value(), extra_head_contents](ByteBuffer data) { - auto markdown_document = Markdown::Document::parse(data); - if (!markdown_document) - return; - - auto parser = HTML::HTMLParser::create(document, markdown_document->render_to_html(extra_head_contents), "utf-8"sv); - parser->run(url); - }); - - auto process_body_error = JS::create_heap_function(realm.heap(), [](JS::Value) { - dbgln("FIXME: Load html page with an error if read of body failed."); - }); - - navigation_params.response->body()->fully_read( - realm, - process_body, - process_body_error, - JS::NonnullGCPtr { realm.global_object() }); - }); -} - bool build_xml_document(DOM::Document& document, ByteBuffer const& data, Optional content_encoding) { Optional decoder; @@ -533,8 +464,6 @@ JS::GCPtr load_document(HTML::NavigationParams const& navigation_ // native rendering of the content or an error message because the specified type is not supported, then // return the result of creating a document for inline content that doesn't have a DOM given navigationParams's // navigable, navigationParams's id, and navigationParams's navigation timing type. - if (type.essence() == "text/markdown"sv) - return load_markdown_document(navigation_params).release_value_but_fixme_should_propagate_errors(); // FIXME: 4. Otherwise, the document's type is such that the resource will not affect navigationParams's navigable, // e.g., because the resource is to be handed to an external application or because it is an unknown type