MDCodeBlock.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #include <AK/StringBuilder.h>
  2. #include <LibMarkdown/MDCodeBlock.h>
  3. MDText::Style MDCodeBlock::style() const
  4. {
  5. if (m_style_spec.spans().is_empty())
  6. return {};
  7. return m_style_spec.spans()[0].style;
  8. }
  9. String MDCodeBlock::style_language() const
  10. {
  11. if (m_style_spec.spans().is_empty())
  12. return {};
  13. return m_style_spec.spans()[0].text;
  14. }
  15. String MDCodeBlock::render_to_html() const
  16. {
  17. StringBuilder builder;
  18. String style_language = this->style_language();
  19. MDText::Style style = this->style();
  20. if (style.strong)
  21. builder.append("<b>");
  22. if (style.emph)
  23. builder.append("<i>");
  24. if (style_language.is_null())
  25. builder.append("<code style=\"white-space: pre;\">");
  26. else
  27. builder.appendf("<code style=\"white-space: pre;\" class=\"%s\">", style_language.characters());
  28. // TODO: This should also be done in other places.
  29. for (int i = 0; i < m_code.length(); i++)
  30. if (m_code[i] == '<')
  31. builder.append("&lt;");
  32. else if (m_code[i] == '>')
  33. builder.append("&gt;");
  34. else if (m_code[i] == '&')
  35. builder.append("&amp;");
  36. else
  37. builder.append(m_code[i]);
  38. builder.append("</code>");
  39. if (style.emph)
  40. builder.append("</i>");
  41. if (style.strong)
  42. builder.append("</b>");
  43. builder.append('\n');
  44. return builder.build();
  45. }
  46. String MDCodeBlock::render_for_terminal() const
  47. {
  48. StringBuilder builder;
  49. MDText::Style style = this->style();
  50. bool needs_styling = style.strong || style.emph;
  51. if (needs_styling) {
  52. builder.append("\033[");
  53. bool first = true;
  54. if (style.strong) {
  55. builder.append('1');
  56. first = false;
  57. }
  58. if (style.emph) {
  59. if (!first)
  60. builder.append(';');
  61. builder.append('4');
  62. }
  63. builder.append('m');
  64. }
  65. builder.append(m_code);
  66. if (needs_styling)
  67. builder.append("\033[0m");
  68. builder.append("\n\n");
  69. return builder.build();
  70. }
  71. bool MDCodeBlock::parse(Vector<StringView>::ConstIterator& lines)
  72. {
  73. if (lines.is_end())
  74. return false;
  75. constexpr auto tick_tick_tick = "```";
  76. StringView line = *lines;
  77. if (!line.starts_with(tick_tick_tick))
  78. return false;
  79. // Our Markdown extension: we allow
  80. // specifying a style and a language
  81. // for a code block, like so:
  82. //
  83. // ```**sh**
  84. // $ echo hello friends!
  85. // ````
  86. //
  87. // The code block will be made bold,
  88. // and if possible syntax-highlighted
  89. // as appropriate for a shell script.
  90. StringView style_spec = line.substring_view(3, line.length() - 3);
  91. bool success = m_style_spec.parse(style_spec);
  92. ASSERT(success);
  93. ++lines;
  94. bool first = true;
  95. StringBuilder builder;
  96. while (true) {
  97. if (lines.is_end())
  98. break;
  99. line = *lines;
  100. ++lines;
  101. if (line == tick_tick_tick)
  102. break;
  103. if (!first)
  104. builder.append('\n');
  105. builder.append(line);
  106. first = false;
  107. }
  108. m_code = builder.build();
  109. return true;
  110. }