
This patch changes the HTML formatting (where to put newlines, etc...) to better match commonmark's test cases. This has minimal effect of the correctness of our markdown implementation, but makes it easier to test. Changes: - Use <em> instead of <i>. - Newline before end of code block. - <hr /> instead of <hr>. - Newline before first list item. - Newline between lines of a paragraph. - Trim whitespace on lines of paragraphs. Tests passed: 33/652 -> 87/652
142 lines
3.1 KiB
C++
142 lines
3.1 KiB
C++
/*
|
|
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/StringBuilder.h>
|
|
#include <LibJS/MarkupGenerator.h>
|
|
#include <LibMarkdown/CodeBlock.h>
|
|
|
|
namespace Markdown {
|
|
|
|
Text::Style CodeBlock::style() const
|
|
{
|
|
if (m_style_spec.spans().is_empty())
|
|
return {};
|
|
return m_style_spec.spans()[0].style;
|
|
}
|
|
|
|
String CodeBlock::style_language() const
|
|
{
|
|
if (m_style_spec.spans().is_empty())
|
|
return {};
|
|
return m_style_spec.spans()[0].text;
|
|
}
|
|
|
|
String CodeBlock::render_to_html() const
|
|
{
|
|
StringBuilder builder;
|
|
|
|
String style_language = this->style_language();
|
|
Text::Style style = this->style();
|
|
|
|
builder.append("<pre>");
|
|
|
|
if (style.strong)
|
|
builder.append("<b>");
|
|
if (style.emph)
|
|
builder.append("<em>");
|
|
|
|
if (style_language.is_empty())
|
|
builder.append("<code>");
|
|
else
|
|
builder.appendff("<code class=\"{}\">", escape_html_entities(style_language));
|
|
|
|
if (style_language == "js")
|
|
builder.append(JS::MarkupGenerator::html_from_source(m_code));
|
|
else
|
|
builder.append(escape_html_entities(m_code));
|
|
|
|
builder.append("\n</code>");
|
|
|
|
if (style.emph)
|
|
builder.append("</em>");
|
|
if (style.strong)
|
|
builder.append("</b>");
|
|
|
|
builder.append("</pre>\n");
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
String CodeBlock::render_for_terminal(size_t) const
|
|
{
|
|
StringBuilder builder;
|
|
|
|
Text::Style style = this->style();
|
|
bool needs_styling = style.strong || style.emph;
|
|
if (needs_styling) {
|
|
builder.append("\033[");
|
|
bool first = true;
|
|
if (style.strong) {
|
|
builder.append('1');
|
|
first = false;
|
|
}
|
|
if (style.emph) {
|
|
if (!first)
|
|
builder.append(';');
|
|
builder.append('4');
|
|
}
|
|
builder.append('m');
|
|
}
|
|
|
|
builder.append(m_code);
|
|
|
|
if (needs_styling)
|
|
builder.append("\033[0m");
|
|
|
|
builder.append("\n\n");
|
|
|
|
return builder.build();
|
|
}
|
|
|
|
OwnPtr<CodeBlock> CodeBlock::parse(Vector<StringView>::ConstIterator& lines)
|
|
{
|
|
if (lines.is_end())
|
|
return {};
|
|
|
|
constexpr auto tick_tick_tick = "```";
|
|
|
|
StringView line = *lines;
|
|
if (!line.starts_with(tick_tick_tick))
|
|
return {};
|
|
|
|
// 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.
|
|
StringView style_spec = line.substring_view(3, line.length() - 3);
|
|
auto spec = Text::parse(style_spec);
|
|
if (!spec.has_value())
|
|
return {};
|
|
|
|
++lines;
|
|
|
|
bool first = true;
|
|
StringBuilder builder;
|
|
|
|
while (true) {
|
|
if (lines.is_end())
|
|
break;
|
|
line = *lines;
|
|
++lines;
|
|
if (line == tick_tick_tick)
|
|
break;
|
|
if (!first)
|
|
builder.append('\n');
|
|
builder.append(line);
|
|
first = false;
|
|
}
|
|
|
|
return make<CodeBlock>(move(spec.value()), builder.build());
|
|
}
|
|
|
|
}
|