1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 22:57:44 +00:00

LibMarkdown: Render lines to terminal instead of a single string

With this patch, the blocks in a markdown document render a vector of
lines. These lines get concatenated in Document::render_to_terminal, so
this does not change any external APIs of LibMarkdown.

This change makes it possible to indent individual lines in the rendered
markdown. So, rendering blockquotes in a similar way to code blocks :^)
This commit is contained in:
Arda Cinar 2022-12-23 12:25:00 +03:00 committed by Andreas Kling
parent 7a4b912ece
commit 5cc984d74c
20 changed files with 85 additions and 55 deletions

View file

@ -19,7 +19,7 @@ public:
virtual ~Block() = default; virtual ~Block() = default;
virtual DeprecatedString render_to_html(bool tight = false) const = 0; virtual DeprecatedString render_to_html(bool tight = false) const = 0;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const = 0; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const = 0;
virtual RecursionDecision walk(Visitor&) const = 0; virtual RecursionDecision walk(Visitor&) const = 0;
}; };

View file

@ -5,6 +5,7 @@
*/ */
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibMarkdown/BlockQuote.h> #include <LibMarkdown/BlockQuote.h>
#include <LibMarkdown/Visitor.h> #include <LibMarkdown/Visitor.h>
@ -19,10 +20,10 @@ DeprecatedString BlockQuote::render_to_html(bool) const
return builder.build(); return builder.build();
} }
DeprecatedString BlockQuote::render_for_terminal(size_t view_width) const Vector<DeprecatedString> BlockQuote::render_lines_for_terminal(size_t view_width) const
{ {
// FIXME: Rewrite the whole terminal renderer to make blockquote rendering possible // FIXME: Indent lines inside the blockquote
return m_contents->render_for_terminal(view_width); return m_contents->render_lines_for_terminal(view_width);
} }
RecursionDecision BlockQuote::walk(Visitor& visitor) const RecursionDecision BlockQuote::walk(Visitor& visitor) const

View file

@ -22,7 +22,7 @@ public:
virtual ~BlockQuote() override = default; virtual ~BlockQuote() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<BlockQuote> parse(LineIterator& lines); static OwnPtr<BlockQuote> parse(LineIterator& lines);

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Forward.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibJS/MarkupGenerator.h> #include <LibJS/MarkupGenerator.h>
#include <LibMarkdown/CodeBlock.h> #include <LibMarkdown/CodeBlock.h>
@ -53,22 +54,23 @@ DeprecatedString CodeBlock::render_to_html(bool) const
return builder.build(); return builder.build();
} }
DeprecatedString CodeBlock::render_for_terminal(size_t) const Vector<DeprecatedString> CodeBlock::render_lines_for_terminal(size_t) const
{ {
StringBuilder builder; Vector<DeprecatedString> lines;
for (auto const& line : m_code.split('\n')) { // Do not indent too much if we are in the synopsis
// Do not indent too much if we are in the synopsis auto indentation = " "sv;
if (!(m_current_section && m_current_section->render_for_terminal().contains("SYNOPSIS"sv))) if (m_current_section != nullptr) {
builder.append(" "sv); auto current_section_name = m_current_section->render_lines_for_terminal()[0];
if (current_section_name.contains("SYNOPSIS"sv))
builder.append(" "sv); indentation = " "sv;
builder.append(line);
builder.append("\n"sv);
} }
builder.append("\n"sv);
return builder.build(); 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 CodeBlock::walk(Visitor& visitor) const

View file

@ -27,7 +27,7 @@ public:
virtual ~CodeBlock() override = default; virtual ~CodeBlock() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<CodeBlock> parse(LineIterator& lines, Heading* current_section); static OwnPtr<CodeBlock> parse(LineIterator& lines, Heading* current_section);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Forward.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibMarkdown/CommentBlock.h> #include <LibMarkdown/CommentBlock.h>
#include <LibMarkdown/Visitor.h> #include <LibMarkdown/Visitor.h>
@ -22,9 +23,9 @@ DeprecatedString CommentBlock::render_to_html(bool) const
return builder.build(); return builder.build();
} }
DeprecatedString CommentBlock::render_for_terminal(size_t) const Vector<DeprecatedString> CommentBlock::render_lines_for_terminal(size_t) const
{ {
return ""; return Vector<DeprecatedString> {};
} }
RecursionDecision CommentBlock::walk(Visitor& visitor) const RecursionDecision CommentBlock::walk(Visitor& visitor) const

View file

@ -23,7 +23,7 @@ public:
virtual ~CommentBlock() override = default; virtual ~CommentBlock() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<CommentBlock> parse(LineIterator& lines); static OwnPtr<CommentBlock> parse(LineIterator& lines);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Forward.h>
#include <LibMarkdown/BlockQuote.h> #include <LibMarkdown/BlockQuote.h>
#include <LibMarkdown/CodeBlock.h> #include <LibMarkdown/CodeBlock.h>
#include <LibMarkdown/ContainerBlock.h> #include <LibMarkdown/ContainerBlock.h>
@ -39,16 +40,16 @@ DeprecatedString ContainerBlock::render_to_html(bool tight) const
return builder.build(); return builder.build();
} }
DeprecatedString ContainerBlock::render_for_terminal(size_t view_width) const Vector<DeprecatedString> ContainerBlock::render_lines_for_terminal(size_t view_width) const
{ {
StringBuilder builder; Vector<DeprecatedString> lines;
for (auto& block : m_blocks) { for (auto& block : m_blocks) {
auto s = block.render_for_terminal(view_width); for (auto& line : block.render_lines_for_terminal(view_width))
builder.append(s); lines.append(move(line));
} }
return builder.build(); return lines;
} }
RecursionDecision ContainerBlock::walk(Visitor& visitor) const RecursionDecision ContainerBlock::walk(Visitor& visitor) const

View file

@ -27,7 +27,7 @@ public:
virtual ~ContainerBlock() override = default; virtual ~ContainerBlock() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<ContainerBlock> parse(LineIterator& lines); static OwnPtr<ContainerBlock> parse(LineIterator& lines);

View file

@ -45,7 +45,13 @@ DeprecatedString Document::render_to_inline_html() const
DeprecatedString Document::render_for_terminal(size_t view_width) const DeprecatedString Document::render_for_terminal(size_t view_width) const
{ {
return m_container->render_for_terminal(view_width); StringBuilder builder;
for (auto& line : m_container->render_lines_for_terminal(view_width)) {
builder.append(line);
builder.append("\n"sv);
}
return builder.build();
} }
RecursionDecision Document::walk(Visitor& visitor) const RecursionDecision Document::walk(Visitor& visitor) const

View file

@ -15,7 +15,7 @@ DeprecatedString Heading::render_to_html(bool) const
return DeprecatedString::formatted("<h{}>{}</h{}>\n", m_level, m_text.render_to_html(), m_level); return DeprecatedString::formatted("<h{}>{}</h{}>\n", m_level, m_text.render_to_html(), m_level);
} }
DeprecatedString Heading::render_for_terminal(size_t) const Vector<DeprecatedString> Heading::render_lines_for_terminal(size_t) const
{ {
StringBuilder builder; StringBuilder builder;
@ -24,15 +24,15 @@ DeprecatedString Heading::render_for_terminal(size_t) const
case 1: case 1:
case 2: case 2:
builder.append(m_text.render_for_terminal().to_uppercase()); builder.append(m_text.render_for_terminal().to_uppercase());
builder.append("\033[0m\n"sv); builder.append("\033[0m"sv);
break; break;
default: default:
builder.append(m_text.render_for_terminal()); builder.append(m_text.render_for_terminal());
builder.append("\033[0m\n"sv); builder.append("\033[0m"sv);
break; break;
} }
return builder.build(); return Vector<DeprecatedString> { builder.build() };
} }
RecursionDecision Heading::walk(Visitor& visitor) const RecursionDecision Heading::walk(Visitor& visitor) const

View file

@ -27,7 +27,7 @@ public:
virtual ~Heading() override = default; virtual ~Heading() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<Heading> parse(LineIterator& lines); static OwnPtr<Heading> parse(LineIterator& lines);

View file

@ -17,13 +17,13 @@ DeprecatedString HorizontalRule::render_to_html(bool) const
return "<hr />\n"; return "<hr />\n";
} }
DeprecatedString HorizontalRule::render_for_terminal(size_t view_width) const Vector<DeprecatedString> HorizontalRule::render_lines_for_terminal(size_t view_width) const
{ {
StringBuilder builder(view_width + 1); StringBuilder builder(view_width + 1);
for (size_t i = 0; i < view_width; ++i) for (size_t i = 0; i < view_width; ++i)
builder.append('-'); builder.append('-');
builder.append("\n\n"sv); builder.append("\n\n"sv);
return builder.to_deprecated_string(); return Vector<DeprecatedString> { builder.to_deprecated_string() };
} }
RecursionDecision HorizontalRule::walk(Visitor& visitor) const RecursionDecision HorizontalRule::walk(Visitor& visitor) const

View file

@ -21,7 +21,7 @@ public:
virtual ~HorizontalRule() override = default; virtual ~HorizontalRule() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<HorizontalRule> parse(LineIterator& lines); static OwnPtr<HorizontalRule> parse(LineIterator& lines);
}; };

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Forward.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibMarkdown/List.h> #include <LibMarkdown/List.h>
#include <LibMarkdown/Paragraph.h> #include <LibMarkdown/Paragraph.h>
@ -37,21 +38,36 @@ DeprecatedString List::render_to_html(bool) const
return builder.build(); return builder.build();
} }
DeprecatedString List::render_for_terminal(size_t) const Vector<DeprecatedString> List::render_lines_for_terminal(size_t view_width) const
{ {
StringBuilder builder; Vector<DeprecatedString> lines;
int i = 0; int i = 0;
for (auto& item : m_items) { 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); builder.append(" "sv);
if (m_is_ordered) if (m_is_ordered)
builder.appendff("{}.", ++i); builder.appendff("{}.", ++i);
else else
builder.append('*'); builder.append('*');
builder.append(item->render_for_terminal()); auto item_indentation = builder.length();
builder.append(first_line);
lines.append(builder.build());
for (auto& line : item_lines) {
builder.clear();
builder.append(DeprecatedString::repeated(' ', item_indentation));
builder.append(line);
lines.append(builder.build());
}
} }
return builder.build(); return lines;
} }
RecursionDecision List::walk(Visitor& visitor) const RecursionDecision List::walk(Visitor& visitor) const

View file

@ -26,7 +26,7 @@ public:
virtual ~List() override = default; virtual ~List() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<List> parse(LineIterator& lines); static OwnPtr<List> parse(LineIterator& lines);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Forward.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <LibMarkdown/Paragraph.h> #include <LibMarkdown/Paragraph.h>
#include <LibMarkdown/Visitor.h> #include <LibMarkdown/Visitor.h>
@ -27,13 +28,9 @@ DeprecatedString Paragraph::render_to_html(bool tight) const
return builder.build(); return builder.build();
} }
DeprecatedString Paragraph::render_for_terminal(size_t) const Vector<DeprecatedString> Paragraph::render_lines_for_terminal(size_t) const
{ {
StringBuilder builder; return Vector<DeprecatedString> { DeprecatedString::formatted(" {}", m_text.render_for_terminal()), "" };
builder.append(" "sv);
builder.append(m_text.render_for_terminal());
builder.append("\n\n"sv);
return builder.build();
} }
RecursionDecision Paragraph::walk(Visitor& visitor) const RecursionDecision Paragraph::walk(Visitor& visitor) const

View file

@ -24,7 +24,7 @@ public:
virtual ~Paragraph() override = default; virtual ~Paragraph() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
private: private:

View file

@ -6,15 +6,17 @@
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibMarkdown/Table.h> #include <LibMarkdown/Table.h>
#include <LibMarkdown/Visitor.h> #include <LibMarkdown/Visitor.h>
namespace Markdown { namespace Markdown {
DeprecatedString Table::render_for_terminal(size_t view_width) const Vector<DeprecatedString> 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); auto unit_width_length = view_width == 0 ? 4 : ((float)(view_width - m_columns.size()) / (float)m_total_width);
StringBuilder builder; StringBuilder builder;
Vector<DeprecatedString> lines;
auto write_aligned = [&](auto const& text, auto width, auto alignment) { auto write_aligned = [&](auto const& text, auto width, auto alignment) {
size_t original_length = text.terminal_length(); size_t original_length = text.terminal_length();
@ -42,10 +44,13 @@ DeprecatedString Table::render_for_terminal(size_t view_width) const
write_aligned(col.header, width, col.alignment); write_aligned(col.header, width, col.alignment);
} }
builder.append('\n'); lines.append(builder.build());
builder.clear();
for (size_t i = 0; i < view_width; ++i) for (size_t i = 0; i < view_width; ++i)
builder.append('-'); builder.append('-');
builder.append('\n'); lines.append(builder.build());
builder.clear();
for (size_t i = 0; i < m_row_count; ++i) { for (size_t i = 0; i < m_row_count; ++i) {
bool first = true; bool first = true;
@ -60,12 +65,13 @@ DeprecatedString Table::render_for_terminal(size_t view_width) const
size_t width = col.relative_width * unit_width_length; size_t width = col.relative_width * unit_width_length;
write_aligned(cell, width, col.alignment); write_aligned(cell, width, col.alignment);
} }
builder.append('\n'); lines.append(builder.build());
builder.clear();
} }
builder.append('\n'); lines.append("");
return builder.to_deprecated_string(); return lines;
} }
DeprecatedString Table::render_to_html(bool) const DeprecatedString Table::render_to_html(bool) const

View file

@ -35,7 +35,7 @@ public:
virtual ~Table() override = default; virtual ~Table() override = default;
virtual DeprecatedString render_to_html(bool tight = false) const override; virtual DeprecatedString render_to_html(bool tight = false) const override;
virtual DeprecatedString render_for_terminal(size_t view_width = 0) const override; virtual Vector<DeprecatedString> render_lines_for_terminal(size_t view_width = 0) const override;
virtual RecursionDecision walk(Visitor&) const override; virtual RecursionDecision walk(Visitor&) const override;
static OwnPtr<Table> parse(LineIterator& lines); static OwnPtr<Table> parse(LineIterator& lines);