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