summaryrefslogtreecommitdiff
path: root/src/iterator.c
blob: 7f90cc7910d41752dddf56ded943cca3e9980d49 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#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;
}

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


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_puts(&buf, cmark_node_get_literal(cur));
			tmp = cur->next;
			while (tmp && tmp->type == CMARK_NODE_TEXT) {
				cmark_iter_get_node(iter); // advance pointer
				cmark_strbuf_puts(&buf, cmark_node_get_literal(tmp));
				next = tmp->next;
				cmark_node_free(tmp);
				tmp = next;
			}
			cmark_node_set_literal(cur, (char *)cmark_strbuf_detach(&buf));
		}
	}

	cmark_iter_free(iter);
}