ContainerBlock.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /*
  2. * Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibMarkdown/BlockQuote.h>
  7. #include <LibMarkdown/CodeBlock.h>
  8. #include <LibMarkdown/ContainerBlock.h>
  9. #include <LibMarkdown/Heading.h>
  10. #include <LibMarkdown/HorizontalRule.h>
  11. #include <LibMarkdown/List.h>
  12. #include <LibMarkdown/Paragraph.h>
  13. #include <LibMarkdown/Table.h>
  14. #include <LibMarkdown/Visitor.h>
  15. namespace Markdown {
  16. String ContainerBlock::render_to_html(bool tight) const
  17. {
  18. StringBuilder builder;
  19. for (size_t i = 0; i + 1 < m_blocks.size(); ++i) {
  20. auto s = m_blocks[i].render_to_html(tight);
  21. builder.append(s);
  22. }
  23. // I don't like this edge case.
  24. if (m_blocks.size() != 0) {
  25. auto& block = m_blocks[m_blocks.size() - 1];
  26. auto s = block.render_to_html(tight);
  27. if (tight && dynamic_cast<Paragraph const*>(&block)) {
  28. builder.append(s.substring_view(0, s.length() - 1));
  29. } else {
  30. builder.append(s);
  31. }
  32. }
  33. return builder.build();
  34. }
  35. String ContainerBlock::render_for_terminal(size_t view_width) const
  36. {
  37. StringBuilder builder;
  38. for (auto& block : m_blocks) {
  39. auto s = block.render_for_terminal(view_width);
  40. builder.append(s);
  41. }
  42. return builder.build();
  43. }
  44. RecursionDecision ContainerBlock::walk(Visitor& visitor) const
  45. {
  46. RecursionDecision rd = visitor.visit(*this);
  47. if (rd != RecursionDecision::Recurse)
  48. return rd;
  49. for (auto const& block : m_blocks) {
  50. rd = block.walk(visitor);
  51. if (rd == RecursionDecision::Break)
  52. return rd;
  53. }
  54. return RecursionDecision::Continue;
  55. }
  56. template<typename BlockType>
  57. static bool try_parse_block(LineIterator& lines, NonnullOwnPtrVector<Block>& blocks)
  58. {
  59. OwnPtr<BlockType> block = BlockType::parse(lines);
  60. if (!block)
  61. return false;
  62. blocks.append(block.release_nonnull());
  63. return true;
  64. }
  65. OwnPtr<ContainerBlock> ContainerBlock::parse(LineIterator& lines)
  66. {
  67. NonnullOwnPtrVector<Block> blocks;
  68. StringBuilder paragraph_text;
  69. auto flush_paragraph = [&] {
  70. if (paragraph_text.is_empty())
  71. return;
  72. auto paragraph = make<Paragraph>(Text::parse(paragraph_text.build()));
  73. blocks.append(move(paragraph));
  74. paragraph_text.clear();
  75. };
  76. bool has_blank_lines = false;
  77. bool has_trailing_blank_lines = false;
  78. while (true) {
  79. if (lines.is_end())
  80. break;
  81. if ((*lines).is_empty()) {
  82. has_trailing_blank_lines = true;
  83. ++lines;
  84. flush_paragraph();
  85. continue;
  86. } else {
  87. has_blank_lines = has_blank_lines || has_trailing_blank_lines;
  88. }
  89. bool any = try_parse_block<Table>(lines, blocks)
  90. || try_parse_block<List>(lines, blocks)
  91. || try_parse_block<CodeBlock>(lines, blocks)
  92. || try_parse_block<CommentBlock>(lines, blocks)
  93. || try_parse_block<Heading>(lines, blocks)
  94. || try_parse_block<HorizontalRule>(lines, blocks)
  95. || try_parse_block<BlockQuote>(lines, blocks);
  96. if (any) {
  97. if (!paragraph_text.is_empty()) {
  98. auto last_block = blocks.take_last();
  99. flush_paragraph();
  100. blocks.append(move(last_block));
  101. }
  102. continue;
  103. }
  104. if (!paragraph_text.is_empty())
  105. paragraph_text.append("\n");
  106. paragraph_text.append(*lines++);
  107. }
  108. flush_paragraph();
  109. return make<ContainerBlock>(move(blocks), has_blank_lines, has_trailing_blank_lines);
  110. }
  111. }