aboutsummaryrefslogtreecommitdiff
path: root/src/iterator.c
blob: 7f90cc7910d41752dddf56ded943cca3e9980d49 (plain)
  1. #include <assert.h>
  2. #include <stdlib.h>
  3. #include "config.h"
  4. #include "node.h"
  5. #include "cmark.h"
  6. #include "iterator.h"
  7. static const int S_leaf_mask =
  8. (1 << CMARK_NODE_HTML) |
  9. (1 << CMARK_NODE_HRULE) |
  10. (1 << CMARK_NODE_CODE_BLOCK) |
  11. (1 << CMARK_NODE_TEXT) |
  12. (1 << CMARK_NODE_SOFTBREAK) |
  13. (1 << CMARK_NODE_LINEBREAK) |
  14. (1 << CMARK_NODE_CODE) |
  15. (1 << CMARK_NODE_INLINE_HTML);
  16. cmark_iter*
  17. cmark_iter_new(cmark_node *root)
  18. {
  19. if (root == NULL) {
  20. return NULL;
  21. }
  22. cmark_iter *iter = (cmark_iter*)malloc(sizeof(cmark_iter));
  23. if (iter == NULL) {
  24. return NULL;
  25. }
  26. iter->root = root;
  27. iter->cur.ev_type = CMARK_EVENT_NONE;
  28. iter->cur.node = NULL;
  29. iter->next.ev_type = CMARK_EVENT_ENTER;
  30. iter->next.node = root;
  31. return iter;
  32. }
  33. void
  34. cmark_iter_free(cmark_iter *iter)
  35. {
  36. free(iter);
  37. }
  38. static bool
  39. S_is_leaf(cmark_node *node)
  40. {
  41. return (1 << node->type) & S_leaf_mask;
  42. }
  43. cmark_event_type
  44. cmark_iter_next(cmark_iter *iter)
  45. {
  46. cmark_event_type ev_type = iter->next.ev_type;
  47. cmark_node *node = iter->next.node;
  48. iter->cur.ev_type = ev_type;
  49. iter->cur.node = node;
  50. if (ev_type == CMARK_EVENT_DONE) {
  51. return ev_type;
  52. }
  53. /* roll forward to next item, setting both fields */
  54. if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) {
  55. if (node->first_child == NULL) {
  56. /* stay on this node but exit */
  57. iter->next.ev_type = CMARK_EVENT_EXIT;
  58. } else {
  59. iter->next.ev_type = CMARK_EVENT_ENTER;
  60. iter->next.node = node->first_child;
  61. }
  62. } else if (node == iter->root) {
  63. /* don't move past root */
  64. iter->next.ev_type = CMARK_EVENT_DONE;
  65. iter->next.node = NULL;
  66. } else if (node->next) {
  67. iter->next.ev_type = CMARK_EVENT_ENTER;
  68. iter->next.node = node->next;
  69. } else if (node->parent) {
  70. iter->next.ev_type = CMARK_EVENT_EXIT;
  71. iter->next.node = node->parent;
  72. } else {
  73. assert(false);
  74. iter->next.ev_type = CMARK_EVENT_DONE;
  75. iter->next.node = NULL;
  76. }
  77. return ev_type;
  78. }
  79. void
  80. cmark_iter_reset(cmark_iter *iter, cmark_node *current,
  81. cmark_event_type event_type)
  82. {
  83. iter->next.ev_type = event_type;
  84. iter->next.node = current;
  85. cmark_iter_next(iter);
  86. }
  87. cmark_node*
  88. cmark_iter_get_node(cmark_iter *iter)
  89. {
  90. return iter->cur.node;
  91. }
  92. cmark_event_type
  93. cmark_iter_get_event_type(cmark_iter *iter)
  94. {
  95. return iter->cur.ev_type;
  96. }
  97. void cmark_consolidate_text_nodes(cmark_node *root)
  98. {
  99. cmark_iter *iter = cmark_iter_new(root);
  100. cmark_strbuf buf = GH_BUF_INIT;
  101. cmark_event_type ev_type;
  102. cmark_node *cur, *tmp, *next;
  103. while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
  104. cur = cmark_iter_get_node(iter);
  105. if (ev_type == CMARK_EVENT_ENTER &&
  106. cur->type == CMARK_NODE_TEXT &&
  107. cur->next &&
  108. cur->next->type == CMARK_NODE_TEXT) {
  109. cmark_strbuf_clear(&buf);
  110. cmark_strbuf_puts(&buf, cmark_node_get_literal(cur));
  111. tmp = cur->next;
  112. while (tmp && tmp->type == CMARK_NODE_TEXT) {
  113. cmark_iter_get_node(iter); // advance pointer
  114. cmark_strbuf_puts(&buf, cmark_node_get_literal(tmp));
  115. next = tmp->next;
  116. cmark_node_free(tmp);
  117. tmp = next;
  118. }
  119. cmark_node_set_literal(cur, (char *)cmark_strbuf_detach(&buf));
  120. }
  121. }
  122. cmark_iter_free(iter);
  123. }