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