aboutsummaryrefslogtreecommitdiff
path: root/src/iterator.c
blob: b0ac9d2697691ee27a49f806b6d83fd0d11e0868 (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. cmark_node*
  46. cmark_iter_get_node(cmark_iter *iter)
  47. {
  48. /* we'll return current */
  49. cmark_node *cur = iter->current;
  50. if (cur == NULL || iter->event_type == CMARK_EVENT_DONE) {
  51. return NULL;
  52. }
  53. /* roll forward to next item, setting both fields */
  54. if (iter->event_type == CMARK_EVENT_ENTER && !S_is_leaf(cur)) {
  55. if (cur->first_child == NULL) {
  56. /* stay on this node but exit */
  57. iter->event_type = CMARK_EVENT_EXIT;
  58. } else {
  59. iter->current = cur->first_child;
  60. iter->event_type = CMARK_EVENT_ENTER;
  61. }
  62. } else if (cur == iter->root) {
  63. /* don't move past root */
  64. iter->event_type = CMARK_EVENT_DONE;
  65. iter->current = NULL;
  66. } else if (cur->next) {
  67. iter->event_type = CMARK_EVENT_ENTER;
  68. iter->current = cur->next;
  69. } else if (cur->parent) {
  70. iter->event_type = CMARK_EVENT_EXIT;
  71. iter->current = cur->parent;
  72. } else {
  73. iter->event_type = CMARK_EVENT_DONE;
  74. iter->current = NULL;
  75. }
  76. return cur;
  77. }
  78. void cmark_consolidate_text_nodes(cmark_node *root)
  79. {
  80. cmark_iter *iter = cmark_iter_new(root);
  81. cmark_strbuf buf = GH_BUF_INIT;
  82. cmark_event_type ev_type;
  83. cmark_node *cur, *tmp, *next;
  84. while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
  85. cur = cmark_iter_get_node(iter);
  86. if (ev_type == CMARK_EVENT_ENTER &&
  87. cur->type == CMARK_NODE_TEXT &&
  88. cur->next &&
  89. cur->next->type == CMARK_NODE_TEXT) {
  90. cmark_strbuf_clear(&buf);
  91. cmark_strbuf_puts(&buf, cmark_node_get_literal(cur));
  92. tmp = cur->next;
  93. while (tmp && tmp->type == CMARK_NODE_TEXT) {
  94. cmark_iter_get_node(iter); // advance pointer
  95. cmark_strbuf_puts(&buf, cmark_node_get_literal(tmp));
  96. next = tmp->next;
  97. cmark_node_free(tmp);
  98. tmp = next;
  99. }
  100. cmark_node_set_literal(cur, (char *)cmark_strbuf_detach(&buf));
  101. }
  102. }
  103. cmark_iter_free(iter);
  104. }