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
Notes: sideshowbarker 2024-07-17 02:24:10 +09:00
20 changed files with 85 additions and 55 deletions

View file

@ -19,7 +19,7 @@ public:
virtual ~Block() = default;
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;
};

View file

@ -5,6 +5,7 @@
*/
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibMarkdown/BlockQuote.h>
#include <LibMarkdown/Visitor.h>
@ -19,10 +20,10 @@ DeprecatedString BlockQuote::render_to_html(bool) const
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
return m_contents->render_for_terminal(view_width);
// FIXME: Indent lines inside the blockquote
return m_contents->render_lines_for_terminal(view_width);
}
RecursionDecision BlockQuote::walk(Visitor& visitor) const

View file

@ -22,7 +22,7 @@ public:
virtual ~BlockQuote() override = default;
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;
static OwnPtr<BlockQuote> parse(LineIterator& lines);

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Forward.h>
#include <AK/StringBuilder.h>
#include <LibJS/MarkupGenerator.h>
#include <LibMarkdown/CodeBlock.h>
@ -53,22 +54,23 @@ DeprecatedString CodeBlock::render_to_html(bool) const
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
if (!(m_current_section && m_current_section->render_for_terminal().contains("SYNOPSIS"sv)))
builder.append(" "sv);
builder.append(" "sv);
builder.append(line);
builder.append("\n"sv);
// 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;
}
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

View file

@ -27,7 +27,7 @@ public:
virtual ~CodeBlock() override = default;
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;
static OwnPtr<CodeBlock> parse(LineIterator& lines, Heading* current_section);

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Forward.h>
#include <AK/StringBuilder.h>
#include <LibMarkdown/CommentBlock.h>
#include <LibMarkdown/Visitor.h>
@ -22,9 +23,9 @@ DeprecatedString CommentBlock::render_to_html(bool) const
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

View file

@ -23,7 +23,7 @@ public:
virtual ~CommentBlock() override = default;
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;
static OwnPtr<CommentBlock> parse(LineIterator& lines);

View file

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

View file

@ -27,7 +27,7 @@ public:
virtual ~ContainerBlock() override = default;
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;
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
{
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

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

View file

@ -27,7 +27,7 @@ public:
virtual ~Heading() override = default;
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;
static OwnPtr<Heading> parse(LineIterator& lines);

View file

@ -17,13 +17,13 @@ DeprecatedString HorizontalRule::render_to_html(bool) const
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);
for (size_t i = 0; i < view_width; ++i)
builder.append('-');
builder.append("\n\n"sv);
return builder.to_deprecated_string();
return Vector<DeprecatedString> { builder.to_deprecated_string() };
}
RecursionDecision HorizontalRule::walk(Visitor& visitor) const

View file

@ -21,7 +21,7 @@ public:
virtual ~HorizontalRule() override = default;
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;
static OwnPtr<HorizontalRule> parse(LineIterator& lines);
};

View file

@ -5,6 +5,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Forward.h>
#include <AK/StringBuilder.h>
#include <LibMarkdown/List.h>
#include <LibMarkdown/Paragraph.h>
@ -37,21 +38,36 @@ DeprecatedString List::render_to_html(bool) const
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;
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('*');
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

View file

@ -26,7 +26,7 @@ public:
virtual ~List() override = default;
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;
static OwnPtr<List> parse(LineIterator& lines);

View file

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

View file

@ -24,7 +24,7 @@ public:
virtual ~Paragraph() override = default;
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;
private:

View file

@ -6,15 +6,17 @@
#include <AK/Debug.h>
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibMarkdown/Table.h>
#include <LibMarkdown/Visitor.h>
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);
StringBuilder builder;
Vector<DeprecatedString> lines;
auto write_aligned = [&](auto const& text, auto width, auto alignment) {
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);
}
builder.append('\n');
lines.append(builder.build());
builder.clear();
for (size_t i = 0; i < view_width; ++i)
builder.append('-');
builder.append('\n');
lines.append(builder.build());
builder.clear();
for (size_t i = 0; i < m_row_count; ++i) {
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;
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

View file

@ -35,7 +35,7 @@ public:
virtual ~Table() override = default;
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;
static OwnPtr<Table> parse(LineIterator& lines);