diff options
author | Sergey Bugaev <bugaevc@gmail.com> | 2019-09-21 00:46:18 +0300 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-09-28 18:29:42 +0200 |
commit | 2e80b2b32f2448502db3acc99b6a403ecb7dc835 (patch) | |
tree | 96de88dd3abeb30700270e3d003a73f1edbaa5e5 /Libraries/LibMarkdown/MDText.cpp | |
parent | dd5541fefc2763368627d8e6e39e93eeead36000 (diff) | |
download | serenity-2e80b2b32f2448502db3acc99b6a403ecb7dc835.zip |
Libraries: Add LibMarkdown
Diffstat (limited to 'Libraries/LibMarkdown/MDText.cpp')
-rw-r--r-- | Libraries/LibMarkdown/MDText.cpp | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/Libraries/LibMarkdown/MDText.cpp b/Libraries/LibMarkdown/MDText.cpp new file mode 100644 index 0000000000..9be080df9b --- /dev/null +++ b/Libraries/LibMarkdown/MDText.cpp @@ -0,0 +1,138 @@ +#include <AK/StringBuilder.h> +#include <LibMarkdown/MDText.h> + +String MDText::render_to_html() const +{ + StringBuilder builder; + + Vector<String> open_tags; + Style current_style; + + for (auto& span : m_spans) { + struct TagAndFlag { + String tag; + bool Style::*flag; + }; + TagAndFlag tags_and_flags[] = { + { "i", &Style::emph }, + { "b", &Style::strong }, + { "code", &Style::code } + }; + auto it = open_tags.find([&](const String& open_tag) { + for (auto& tag_and_flag : tags_and_flags) { + if (open_tag == tag_and_flag.tag && !(span.style.*tag_and_flag.flag)) + return true; + } + return false; + }); + + if (!it.is_end()) { + // We found an open tag that should + // not be open for the new span. Close + // it and all the open tags that follow + // it. + for (auto it2 = --open_tags.end(); it2 >= it; --it2) { + const String& tag = *it2; + builder.appendf("</%s>", tag.characters()); + for (auto& tag_and_flag : tags_and_flags) + if (tag == tag_and_flag.tag) + current_style.*tag_and_flag.flag = false; + } + open_tags.shrink(it.index()); + } + for (auto& tag_and_flag : tags_and_flags) { + if (current_style.*tag_and_flag.flag != span.style.*tag_and_flag.flag) { + open_tags.append(tag_and_flag.tag); + builder.appendf("<%s>", tag_and_flag.tag.characters()); + } + } + + current_style = span.style; + builder.append(span.text); + } + + for (auto it = --open_tags.end(); it >= open_tags.begin(); --it) { + const String& tag = *it; + builder.appendf("</%s>", tag.characters()); + } + + return builder.build(); +} + +String MDText::render_for_terminal() const +{ + StringBuilder builder; + + for (auto& span : m_spans) { + bool needs_styling = span.style.strong || span.style.emph || span.style.code; + if (needs_styling) { + builder.append("\033["); + bool first = true; + if (span.style.strong || span.style.code) { + builder.append('1'); + first = false; + } + if (span.style.emph) { + if (!first) + builder.append(';'); + builder.append('4'); + } + builder.append('m'); + } + + builder.append(span.text.characters()); + + if (needs_styling) + builder.append("\033[0m"); + } + + return builder.build(); +} + +bool MDText::parse(const StringView& str) +{ + Style current_style; + int current_span_start = 0; + + for (int offset = 0; offset < str.length(); offset++) { + char ch = str[offset]; + + bool is_special_character = false; + is_special_character |= ch == '`'; + if (!current_style.code) + is_special_character |= ch == '*' || ch == '_'; + if (!is_special_character) + continue; + + if (current_span_start != offset) { + Span span { + str.substring_view(current_span_start, offset - current_span_start), + current_style + }; + m_spans.append(move(span)); + } + + if (ch == '`') { + current_style.code = !current_style.code; + } else { + if (offset + 1 < str.length() && str[offset + 1] == ch) { + offset++; + current_style.strong = !current_style.strong; + } else { + current_style.emph = !current_style.emph; + } + } + + current_span_start = offset + 1; + } + + if (current_span_start < str.length()) { + Span span { + str.substring_view(current_span_start, str.length() - current_span_start), + current_style + }; + m_spans.append(move(span)); + } + + return true; +} |