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
9static 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
15cmark_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
30void cmark_iter_free(cmark_iter *iter) { iter->mem->free(iter); }
31
32static bool S_is_leaf(cmark_node *node) {
33 return ((1 << node->type) & S_leaf_mask) != 0;
34}
35
36cmark_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
75void 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
82cmark_node *cmark_iter_get_node(cmark_iter *iter) { return iter->cur.node; }
83
84cmark_event_type cmark_iter_get_event_type(cmark_iter *iter) {
85 return iter->cur.ev_type;
86}
87
88cmark_node *cmark_iter_get_root(cmark_iter *iter) { return iter->root; }
89
90void 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}
123