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 | } |
123 | |