| #include <assert.h> |
| #include <stdlib.h> |
| |
| #include "config.h" |
| #include "node.h" |
| #include "cmark.h" |
| #include "iterator.h" |
| |
| static const int S_leaf_mask = |
| (1 << CMARK_NODE_HTML) | (1 << CMARK_NODE_HRULE) | |
| (1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) | |
| (1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) | |
| (1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_INLINE_HTML); |
| |
| cmark_iter *cmark_iter_new(cmark_node *root) { |
| if (root == NULL) { |
| return NULL; |
| } |
| cmark_iter *iter = (cmark_iter *)malloc(sizeof(cmark_iter)); |
| if (iter == NULL) { |
| return NULL; |
| } |
| iter->root = root; |
| iter->cur.ev_type = CMARK_EVENT_NONE; |
| iter->cur.node = NULL; |
| iter->next.ev_type = CMARK_EVENT_ENTER; |
| iter->next.node = root; |
| return iter; |
| } |
| |
| void cmark_iter_free(cmark_iter *iter) { free(iter); } |
| |
| static bool S_is_leaf(cmark_node *node) { |
| return ((1 << node->type) & S_leaf_mask) != 0; |
| } |
| |
| cmark_event_type cmark_iter_next(cmark_iter *iter) { |
| cmark_event_type ev_type = iter->next.ev_type; |
| cmark_node *node = iter->next.node; |
| |
| iter->cur.ev_type = ev_type; |
| iter->cur.node = node; |
| |
| if (ev_type == CMARK_EVENT_DONE) { |
| return ev_type; |
| } |
| |
| /* roll forward to next item, setting both fields */ |
| if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) { |
| if (node->first_child == NULL) { |
| /* stay on this node but exit */ |
| iter->next.ev_type = CMARK_EVENT_EXIT; |
| } else { |
| iter->next.ev_type = CMARK_EVENT_ENTER; |
| iter->next.node = node->first_child; |
| } |
| } else if (node == iter->root) { |
| /* don't move past root */ |
| iter->next.ev_type = CMARK_EVENT_DONE; |
| iter->next.node = NULL; |
| } else if (node->next) { |
| iter->next.ev_type = CMARK_EVENT_ENTER; |
| iter->next.node = node->next; |
| } else if (node->parent) { |
| iter->next.ev_type = CMARK_EVENT_EXIT; |
| iter->next.node = node->parent; |
| } else { |
| assert(false); |
| iter->next.ev_type = CMARK_EVENT_DONE; |
| iter->next.node = NULL; |
| } |
| |
| return ev_type; |
| } |
| |
| void cmark_iter_reset(cmark_iter *iter, cmark_node *current, |
| cmark_event_type event_type) { |
| iter->next.ev_type = event_type; |
| iter->next.node = current; |
| cmark_iter_next(iter); |
| } |
| |
| cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; } |
| |
| cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) { |
| return iter->cur.ev_type; |
| } |
| |
| cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; } |
| |
| void cmark_consolidate_text_nodes(cmark_node *root) { |
| cmark_iter *iter = cmark_iter_new(root); |
| cmark_strbuf buf = GH_BUF_INIT; |
| cmark_event_type ev_type; |
| cmark_node *cur, *tmp, *next; |
| |
| while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { |
| cur = cmark_iter_get_node(iter); |
| if (ev_type == CMARK_EVENT_ENTER && cur->type == CMARK_NODE_TEXT && |
| cur->next && cur->next->type == CMARK_NODE_TEXT) { |
| cmark_strbuf_clear(&buf); |
| cmark_strbuf_put(&buf, cur->as.literal.data, cur->as.literal.len); |
| tmp = cur->next; |
| while (tmp && tmp->type == CMARK_NODE_TEXT) { |
| cmark_iter_next(iter); // advance pointer |
| cmark_strbuf_put(&buf, tmp->as.literal.data, tmp->as.literal.len); |
| next = tmp->next; |
| cmark_node_free(tmp); |
| tmp = next; |
| } |
| cmark_chunk_free(&cur->as.literal); |
| cur->as.literal = cmark_chunk_buf_detach(&buf); |
| } |
| } |
| |
| cmark_strbuf_free(&buf); |
| cmark_iter_free(iter); |
| } |