Serenity Operating System
at portability 161 lines 4.6 kB view raw
1/* 2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/StringBuilder.h> 28#include <LibMarkdown/MDCodeBlock.h> 29 30MDText::Style MDCodeBlock::style() const 31{ 32 if (m_style_spec.spans().is_empty()) 33 return {}; 34 return m_style_spec.spans()[0].style; 35} 36 37String MDCodeBlock::style_language() const 38{ 39 if (m_style_spec.spans().is_empty()) 40 return {}; 41 return m_style_spec.spans()[0].text; 42} 43 44String MDCodeBlock::render_to_html() const 45{ 46 StringBuilder builder; 47 48 String style_language = this->style_language(); 49 MDText::Style style = this->style(); 50 51 if (style.strong) 52 builder.append("<b>"); 53 if (style.emph) 54 builder.append("<i>"); 55 56 if (style_language.is_null()) 57 builder.append("<code style=\"white-space: pre;\">"); 58 else 59 builder.appendf("<code style=\"white-space: pre;\" class=\"%s\">", style_language.characters()); 60 61 // TODO: This should also be done in other places. 62 for (size_t i = 0; i < m_code.length(); i++) 63 if (m_code[i] == '<') 64 builder.append("&lt;"); 65 else if (m_code[i] == '>') 66 builder.append("&gt;"); 67 else if (m_code[i] == '&') 68 builder.append("&amp;"); 69 else 70 builder.append(m_code[i]); 71 72 builder.append("</code>"); 73 74 if (style.emph) 75 builder.append("</i>"); 76 if (style.strong) 77 builder.append("</b>"); 78 79 builder.append('\n'); 80 81 return builder.build(); 82} 83 84String MDCodeBlock::render_for_terminal() const 85{ 86 StringBuilder builder; 87 88 MDText::Style style = this->style(); 89 bool needs_styling = style.strong || style.emph; 90 if (needs_styling) { 91 builder.append("\033["); 92 bool first = true; 93 if (style.strong) { 94 builder.append('1'); 95 first = false; 96 } 97 if (style.emph) { 98 if (!first) 99 builder.append(';'); 100 builder.append('4'); 101 } 102 builder.append('m'); 103 } 104 105 builder.append(m_code); 106 107 if (needs_styling) 108 builder.append("\033[0m"); 109 110 builder.append("\n\n"); 111 112 return builder.build(); 113} 114 115bool MDCodeBlock::parse(Vector<StringView>::ConstIterator& lines) 116{ 117 if (lines.is_end()) 118 return false; 119 120 constexpr auto tick_tick_tick = "```"; 121 122 StringView line = *lines; 123 if (!line.starts_with(tick_tick_tick)) 124 return false; 125 126 // Our Markdown extension: we allow 127 // specifying a style and a language 128 // for a code block, like so: 129 // 130 // ```**sh** 131 // $ echo hello friends! 132 // ```` 133 // 134 // The code block will be made bold, 135 // and if possible syntax-highlighted 136 // as appropriate for a shell script. 137 StringView style_spec = line.substring_view(3, line.length() - 3); 138 bool success = m_style_spec.parse(style_spec); 139 ASSERT(success); 140 141 ++lines; 142 143 bool first = true; 144 StringBuilder builder; 145 146 while (true) { 147 if (lines.is_end()) 148 break; 149 line = *lines; 150 ++lines; 151 if (line == tick_tick_tick) 152 break; 153 if (!first) 154 builder.append('\n'); 155 builder.append(line); 156 first = false; 157 } 158 159 m_code = builder.build(); 160 return true; 161}