CodeBlock.cpp 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/StringBuilder.h>
  7. #include <LibJS/MarkupGenerator.h>
  8. #include <LibMarkdown/CodeBlock.h>
  9. namespace Markdown {
  10. Text::Style CodeBlock::style() const
  11. {
  12. if (m_style_spec.spans().is_empty())
  13. return {};
  14. return m_style_spec.spans()[0].style;
  15. }
  16. String CodeBlock::style_language() const
  17. {
  18. if (m_style_spec.spans().is_empty())
  19. return {};
  20. return m_style_spec.spans()[0].text;
  21. }
  22. String CodeBlock::render_to_html() const
  23. {
  24. StringBuilder builder;
  25. String style_language = this->style_language();
  26. Text::Style style = this->style();
  27. builder.append("<pre>");
  28. if (style.strong)
  29. builder.append("<b>");
  30. if (style.emph)
  31. builder.append("<i>");
  32. if (style_language.is_empty())
  33. builder.append("<code>");
  34. else
  35. builder.appendff("<code class=\"{}\">", escape_html_entities(style_language));
  36. if (style_language == "js")
  37. builder.append(JS::MarkupGenerator::html_from_source(m_code));
  38. else
  39. builder.append(escape_html_entities(m_code));
  40. builder.append("</code>");
  41. if (style.emph)
  42. builder.append("</i>");
  43. if (style.strong)
  44. builder.append("</b>");
  45. builder.append("</pre>\n");
  46. return builder.build();
  47. }
  48. String CodeBlock::render_for_terminal(size_t) const
  49. {
  50. StringBuilder builder;
  51. Text::Style style = this->style();
  52. bool needs_styling = style.strong || style.emph;
  53. if (needs_styling) {
  54. builder.append("\033[");
  55. bool first = true;
  56. if (style.strong) {
  57. builder.append('1');
  58. first = false;
  59. }
  60. if (style.emph) {
  61. if (!first)
  62. builder.append(';');
  63. builder.append('4');
  64. }
  65. builder.append('m');
  66. }
  67. builder.append(m_code);
  68. if (needs_styling)
  69. builder.append("\033[0m");
  70. builder.append("\n\n");
  71. return builder.build();
  72. }
  73. OwnPtr<CodeBlock> CodeBlock::parse(Vector<StringView>::ConstIterator& lines)
  74. {
  75. if (lines.is_end())
  76. return {};
  77. constexpr auto tick_tick_tick = "```";
  78. StringView line = *lines;
  79. if (!line.starts_with(tick_tick_tick))
  80. return {};
  81. // Our Markdown extension: we allow
  82. // specifying a style and a language
  83. // for a code block, like so:
  84. //
  85. // ```**sh**
  86. // $ echo hello friends!
  87. // ````
  88. //
  89. // The code block will be made bold,
  90. // and if possible syntax-highlighted
  91. // as appropriate for a shell script.
  92. StringView style_spec = line.substring_view(3, line.length() - 3);
  93. auto spec = Text::parse(style_spec);
  94. if (!spec.has_value())
  95. return {};
  96. ++lines;
  97. bool first = true;
  98. StringBuilder builder;
  99. while (true) {
  100. if (lines.is_end())
  101. break;
  102. line = *lines;
  103. ++lines;
  104. if (line == tick_tick_tick)
  105. break;
  106. if (!first)
  107. builder.append('\n');
  108. builder.append(line);
  109. first = false;
  110. }
  111. return make<CodeBlock>(move(spec.value()), builder.build());
  112. }
  113. }