cmark

My personal build of CMark ✏️

iterator.c (3518B)

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