cmark

My personal build of CMark ✏️

Commit
c6468f480a589a4520e667f46afb05cdad17f3d2
Parent
ae90bbe34fc24ffa5d8e2138bdfe46ddac2006be
Author
John MacFarlane <jgm@berkeley.edu>
Date

Merge pull request #208 from nwellnhof/more_accessors_and_tests

More accessors and tests

Diffstat

4 files changed, 470 insertions, 10 deletions

Status File Name N° Changes Insertions Deletions
Modified api_test/harness.c 2 1 1
Modified api_test/main.c 241 237 4
Modified src/cmark.h 52 49 3
Modified src/node.c 185 183 2
diff --git a/api_test/harness.c b/api_test/harness.c
@@ -90,7 +90,7 @@ test_print_summary(test_batch_runner *runner)
 	int num_failed  = runner->num_failed;
 
 	fprintf(stderr, "%d tests passed, %d failed, %d skipped\n",
-		num_passed, num_skipped, num_failed);
+		num_passed, num_failed, num_skipped);
 
 	if (test_ok(runner)) {
 		fprintf(stderr, "PASS\n");
diff --git a/api_test/main.c b/api_test/main.c
@@ -8,8 +8,215 @@
 #include "harness.h"
 
 static void
+accessors(test_batch_runner *runner)
+{
+	static const unsigned char markdown[] =
+		"## Header\n"
+		"\n"
+		"* Item 1\n"
+		"* Item 2\n"
+		"\n"
+		"2. Item 1\n"
+		"\n"
+		"3. Item 2\n"
+		"\n"
+		"\n"
+		"    code\n"
+		"\n"
+		"``` lang\n"
+		"fenced\n"
+		"```\n"
+		"\n"
+		"<div>html</div>\n"
+		"\n"
+		"[link](url 'title')\n";
+
+	cmark_node *doc = cmark_parse_document(markdown, sizeof(markdown) - 1);
+
+	// Getters
+
+	cmark_node *header = cmark_node_first_child(doc);
+	INT_EQ(runner, cmark_node_get_type(header), CMARK_NODE_ATX_HEADER,
+	       "get_type header");
+	INT_EQ(runner, cmark_node_get_header_level(header), 2,
+	       "get_header_level");
+
+	cmark_node *bullet_list = cmark_node_next(header);
+	INT_EQ(runner, cmark_node_get_type(bullet_list), CMARK_NODE_LIST,
+	       "get_type bullet list");
+	INT_EQ(runner, cmark_node_get_list_type(bullet_list),
+	       CMARK_BULLET_LIST, "get_list_type bullet");
+	INT_EQ(runner, cmark_node_get_list_tight(bullet_list), 1,
+	       "get_list_tight tight");
+
+	cmark_node *ordered_list = cmark_node_next(bullet_list);
+	INT_EQ(runner, cmark_node_get_type(ordered_list), CMARK_NODE_LIST,
+	       "get_type ordered list");
+	INT_EQ(runner, cmark_node_get_list_type(ordered_list),
+	       CMARK_ORDERED_LIST, "get_list_type ordered");
+	INT_EQ(runner, cmark_node_get_list_start(ordered_list), 2,
+	       "get_list_start");
+	INT_EQ(runner, cmark_node_get_list_tight(ordered_list), 0,
+	       "get_list_tight loose");
+
+	cmark_node *code = cmark_node_next(ordered_list);
+	INT_EQ(runner, cmark_node_get_type(code), CMARK_NODE_INDENTED_CODE,
+	       "get_type indented code");
+	STR_EQ(runner, cmark_node_get_string_content(code), "code\n",
+	       "get_string_content indented code");
+
+	cmark_node *fenced = cmark_node_next(code);
+	INT_EQ(runner, cmark_node_get_type(fenced), CMARK_NODE_FENCED_CODE,
+	       "get_type fenced code");
+	STR_EQ(runner, cmark_node_get_string_content(fenced), "fenced\n",
+	       "get_string_content fenced code");
+	STR_EQ(runner, cmark_node_get_fence_info(fenced), "lang",
+	       "get_fence_info");
+
+	cmark_node *html = cmark_node_next(fenced);
+	INT_EQ(runner, cmark_node_get_type(html), CMARK_NODE_HTML,
+	       "get_type html");
+	STR_EQ(runner, cmark_node_get_string_content(html),
+	       "<div>html</div>\n", "get_string_content html");
+
+	cmark_node *paragraph = cmark_node_next(html);
+	INT_EQ(runner, cmark_node_get_type(paragraph), CMARK_NODE_PARAGRAPH,
+	       "get_type paragraph");
+	INT_EQ(runner, cmark_node_get_start_line(paragraph), 19,
+	       "get_start_line");
+	INT_EQ(runner, cmark_node_get_start_column(paragraph), 1,
+	       "get_start_column");
+	INT_EQ(runner, cmark_node_get_end_line(paragraph), 19,
+	       "get_end_line");
+
+	cmark_node *link = cmark_node_first_child(paragraph);
+	INT_EQ(runner, cmark_node_get_type(link), CMARK_NODE_LINK,
+	       "get_type link");
+	STR_EQ(runner, cmark_node_get_url(link), "url",
+	       "get_url");
+	STR_EQ(runner, cmark_node_get_title(link), "title",
+	       "get_title");
+
+	cmark_node *string = cmark_node_first_child(link);
+	INT_EQ(runner, cmark_node_get_type(string), CMARK_NODE_STRING,
+	       "get_type string");
+	STR_EQ(runner, cmark_node_get_string_content(string), "link",
+	       "get_string_content string");
+
+	// Setters
+
+	OK(runner, cmark_node_set_header_level(header, 3),
+	   "set_header_level");
+
+	OK(runner, cmark_node_set_list_type(bullet_list, CMARK_ORDERED_LIST),
+	   "set_list_type ordered");
+	OK(runner, cmark_node_set_list_start(bullet_list, 3),
+	   "set_list_start");
+	OK(runner, cmark_node_set_list_tight(bullet_list, 0),
+	   "set_list_tight loose");
+
+	OK(runner, cmark_node_set_list_type(ordered_list, CMARK_BULLET_LIST),
+	   "set_list_type bullet");
+	OK(runner, cmark_node_set_list_tight(ordered_list, 1),
+	   "set_list_tight tight");
+
+	OK(runner, cmark_node_set_string_content(code, "CODE\n"),
+	   "set_string_content indented code");
+
+	OK(runner, cmark_node_set_string_content(fenced, "FENCED\n"),
+	   "set_string_content fenced code");
+	OK(runner, cmark_node_set_fence_info(fenced, "LANG"),
+	   "set_fence_info");
+
+	OK(runner, cmark_node_set_string_content(html, "<div>HTML</div>\n"),
+	   "set_string_content html");
+
+	OK(runner, cmark_node_set_url(link, "URL"),
+	   "set_url");
+	OK(runner, cmark_node_set_title(link, "TITLE"),
+	   "set_title");
+
+	OK(runner, cmark_node_set_string_content(string, "LINK"),
+	   "set_string_content string");
+
+	char *rendered_html = (char *)cmark_render_html(doc);
+	static const char expected_html[] =
+		"<h3>Header</h3>\n"
+		"<ol start=\"3\">\n"
+		"<li>\n"
+		"<p>Item 1</p>\n"
+		"</li>\n"
+		"<li>\n"
+		"<p>Item 2</p>\n"
+		"</li>\n"
+		"</ol>\n"
+		"<ul start=\"2\">\n"
+		"<li>Item 1</li>\n"
+		"<li>Item 2</li>\n"
+		"</ul>\n"
+		"<pre><code>CODE\n"
+		"</code></pre>\n"
+		"<pre><code class=\"language-LANG\">FENCED\n"
+		"</code></pre>\n"
+		"<div>HTML</div>\n"
+		"<p><a href=\"URL\" title=\"TITLE\">LINK</a></p>\n";
+	STR_EQ(runner, rendered_html, expected_html, "setters work");
+	free(rendered_html);
+
+	// Getter errors
+
+	INT_EQ(runner, cmark_node_get_header_level(bullet_list), 0,
+	       "get_header_level error");
+	INT_EQ(runner, cmark_node_get_list_type(header), CMARK_NO_LIST,
+	       "get_list_type error");
+	INT_EQ(runner, cmark_node_get_list_start(code), 0,
+	       "get_list_start error");
+	INT_EQ(runner, cmark_node_get_list_tight(fenced), 0,
+	       "get_list_tight error");
+	OK(runner, cmark_node_get_string_content(ordered_list) == NULL,
+	   "get_string_content error");
+	OK(runner, cmark_node_get_fence_info(paragraph) == NULL,
+	   "get_fence_info error");
+	OK(runner, cmark_node_get_url(html) == NULL,
+	   "get_url error");
+	OK(runner, cmark_node_get_title(header) == NULL,
+	   "get_title error");
+
+	// Setter errors
+
+	OK(runner, !cmark_node_set_header_level(bullet_list, 3),
+	   "set_header_level error");
+	OK(runner, !cmark_node_set_list_type(header, CMARK_ORDERED_LIST),
+	   "set_list_type error");
+	OK(runner, !cmark_node_set_list_start(code, 3),
+	   "set_list_start error");
+	OK(runner, !cmark_node_set_list_tight(fenced, 0),
+	   "set_list_tight error");
+	OK(runner, !cmark_node_set_string_content(ordered_list, "content\n"),
+	   "set_string_content error");
+	OK(runner, !cmark_node_set_fence_info(paragraph, "lang"),
+	   "set_fence_info error");
+	OK(runner, !cmark_node_set_url(html, "url"),
+	   "set_url error");
+	OK(runner, !cmark_node_set_title(header, "title"),
+	   "set_title error");
+
+	OK(runner, !cmark_node_set_header_level(header, 0),
+	   "set_header_level too small");
+	OK(runner, !cmark_node_set_header_level(header, 7),
+	   "set_header_level too large");
+	OK(runner, !cmark_node_set_list_type(bullet_list, CMARK_NO_LIST),
+	   "set_list_type invalid");
+	OK(runner, !cmark_node_set_list_start(bullet_list, -1),
+	   "set_list_start negative");
+
+	cmark_node_destroy(doc);
+}
+
+static void
 create_tree(test_batch_runner *runner)
 {
+	char *html;
 	cmark_node *doc = cmark_node_new(CMARK_NODE_DOCUMENT);
 
 	cmark_node *p = cmark_node_new(CMARK_NODE_PARAGRAPH);
@@ -21,25 +228,50 @@ create_tree(test_batch_runner *runner)
 	INT_EQ(runner, cmark_node_check(doc), 0, "prepend1 consistent");
 
 	cmark_node *str1 = cmark_node_new(CMARK_NODE_STRING);
-	cmark_node_set_content(str1, "Hello, ");
+	cmark_node_set_string_content(str1, "Hello, ");
 	OK(runner, cmark_node_prepend_child(p, str1), "prepend2");
 	INT_EQ(runner, cmark_node_check(doc), 0, "prepend2 consistent");
 
 	cmark_node *str3 = cmark_node_new(CMARK_NODE_STRING);
-	cmark_node_set_content(str3, "!");
+	cmark_node_set_string_content(str3, "!");
 	OK(runner, cmark_node_append_child(p, str3), "append2");
 	INT_EQ(runner, cmark_node_check(doc), 0, "append2 consistent");
 
 	cmark_node *str2 = cmark_node_new(CMARK_NODE_STRING);
-	cmark_node_set_content(str2, "world");
+	cmark_node_set_string_content(str2, "world");
 	OK(runner, cmark_node_append_child(emph, str2), "append3");
 	INT_EQ(runner, cmark_node_check(doc), 0, "append3 consistent");
 
-	char *html = (char *)cmark_render_html(doc);
+	html = (char *)cmark_render_html(doc);
 	STR_EQ(runner, html, "<p>Hello, <em>world</em>!</p>\n",
 	       "render_html");
 	free(html);
 
+	OK(runner, cmark_node_insert_before(str1, str3), "ins before1");
+	INT_EQ(runner, cmark_node_check(doc), 0, "ins before1 consistent");
+	// 31e
+	OK(runner, cmark_node_first_child(p) == str3, "ins before1 works");
+
+	OK(runner, cmark_node_insert_before(str1, emph), "ins before2");
+	INT_EQ(runner, cmark_node_check(doc), 0, "ins before2 consistent");
+	// 3e1
+	OK(runner, cmark_node_last_child(p) == str1, "ins before2 works");
+
+	OK(runner, cmark_node_insert_after(str1, str3), "ins after1");
+	INT_EQ(runner, cmark_node_check(doc), 0, "ins after1 consistent");
+	// e13
+	OK(runner, cmark_node_last_child(p) == str3, "ins after1 works");
+
+	OK(runner, cmark_node_insert_after(str1, emph), "ins after2");
+	INT_EQ(runner, cmark_node_check(doc), 0, "ins after2 consistent");
+	// 1e3
+	OK(runner, cmark_node_first_child(p) == str1, "ins after2 works");
+
+	html = (char *)cmark_render_html(doc);
+	STR_EQ(runner, html, "<p>Hello, <em>world</em>!</p>\n",
+	       "render_html after shuffling");
+	free(html);
+
 	cmark_node_destroy(doc);
 }
 
@@ -47,6 +279,7 @@ int main() {
 	int retval;
 	test_batch_runner *runner = test_batch_runner_new();
 
+	accessors(runner);
 	create_tree(runner);
 
 	test_print_summary(runner);
diff --git a/src/cmark.h b/src/cmark.h
@@ -44,6 +44,7 @@ typedef enum {
 } cmark_node_type;
 
 typedef enum {
+	CMARK_NO_LIST,
 	CMARK_BULLET_LIST,
 	CMARK_ORDERED_LIST
 }  cmark_list_type;
@@ -90,10 +91,40 @@ CMARK_EXPORT cmark_node_type
 cmark_node_get_type(cmark_node *node);
 
 CMARK_EXPORT const char*
-cmark_node_get_content(cmark_node *node);
+cmark_node_get_string_content(cmark_node *node);
 
 CMARK_EXPORT int
-cmark_node_set_content(cmark_node *node, const char *content);
+cmark_node_set_string_content(cmark_node *node, const char *content);
+
+CMARK_EXPORT int
+cmark_node_get_header_level(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_header_level(cmark_node *node, int level);
+
+CMARK_EXPORT cmark_list_type
+cmark_node_get_list_type(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_list_type(cmark_node *node, cmark_list_type type);
+
+CMARK_EXPORT int
+cmark_node_get_list_start(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_list_start(cmark_node *node, int start);
+
+CMARK_EXPORT int
+cmark_node_get_list_tight(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_list_tight(cmark_node *node, int tight);
+
+CMARK_EXPORT const char*
+cmark_node_get_fence_info(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_fence_info(cmark_node *node, const char *info);
 
 CMARK_EXPORT const char*
 cmark_node_get_url(cmark_node *node);
@@ -101,6 +132,21 @@ cmark_node_get_url(cmark_node *node);
 CMARK_EXPORT int
 cmark_node_set_url(cmark_node *node, const char *url);
 
+CMARK_EXPORT const char*
+cmark_node_get_title(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_set_title(cmark_node *node, const char *title);
+
+CMARK_EXPORT int
+cmark_node_get_start_line(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_get_start_column(cmark_node *node);
+
+CMARK_EXPORT int
+cmark_node_get_end_line(cmark_node *node);
+
 // Tree manipulation
 
 CMARK_EXPORT void
@@ -110,7 +156,7 @@ CMARK_EXPORT int
 cmark_node_insert_before(cmark_node *node, cmark_node *sibling);
 
 CMARK_EXPORT int
-cmark_node_insert_before(cmark_node *node, cmark_node *sibling);
+cmark_node_insert_after(cmark_node *node, cmark_node *sibling);
 
 CMARK_EXPORT int
 cmark_node_prepend_child(cmark_node *node, cmark_node *child);
diff --git a/src/node.c b/src/node.c
@@ -96,12 +96,18 @@ S_strdup(const char *str) {
 }
 
 const char*
-cmark_node_get_content(cmark_node *node) {
+cmark_node_get_string_content(cmark_node *node) {
 	switch (node->type) {
+	case NODE_INDENTED_CODE:
+	case NODE_FENCED_CODE:
+	case NODE_HTML:
+		return cmark_strbuf_cstr(&node->string_content);
+
 	case NODE_STRING:
 	case NODE_INLINE_HTML:
 	case NODE_INLINE_CODE:
 		return cmark_chunk_to_cstr(&node->as.literal);
+
 	default:
 		break;
 	}
@@ -110,13 +116,53 @@ cmark_node_get_content(cmark_node *node) {
 }
 
 int
-cmark_node_set_content(cmark_node *node, const char *content) {
+cmark_node_set_string_content(cmark_node *node, const char *content) {
 	switch (node->type) {
+	case NODE_INDENTED_CODE:
+	case NODE_FENCED_CODE:
+	case NODE_HTML:
+		cmark_strbuf_sets(&node->string_content, content);
+		return 1;
+
 	case NODE_STRING:
 	case NODE_INLINE_HTML:
 	case NODE_INLINE_CODE:
 		cmark_chunk_set_cstr(&node->as.literal, content);
 		return 1;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+int
+cmark_node_get_header_level(cmark_node *node) {
+	switch (node->type) {
+	case CMARK_NODE_ATX_HEADER:
+	case CMARK_NODE_SETEXT_HEADER:
+		return node->as.header.level;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+int
+cmark_node_set_header_level(cmark_node *node, int level) {
+	if (level < 1 || level > 6) {
+		return 0;
+	}
+
+	switch (node->type) {
+	case CMARK_NODE_ATX_HEADER:
+	case CMARK_NODE_SETEXT_HEADER:
+		node->as.header.level = level;
+		return 1;
+
 	default:
 		break;
 	}
@@ -124,6 +170,98 @@ cmark_node_set_content(cmark_node *node, const char *content) {
 	return 0;
 }
 
+cmark_list_type
+cmark_node_get_list_type(cmark_node *node) {
+	if (node->type == CMARK_NODE_LIST) {
+		return node->as.list.list_type;
+	}
+	else {
+		return CMARK_NO_LIST;
+	}
+}
+
+int
+cmark_node_set_list_type(cmark_node *node, cmark_list_type type) {
+	if (!(type == CMARK_BULLET_LIST || type == CMARK_ORDERED_LIST)) {
+		return 0;
+	}
+
+	if (node->type == CMARK_NODE_LIST) {
+		node->as.list.list_type = type;
+		return 1;
+	}
+	else {
+		return 0;
+	}
+}
+
+int
+cmark_node_get_list_start(cmark_node *node) {
+	if (node->type == CMARK_NODE_LIST) {
+		return node->as.list.start;
+	}
+	else {
+		return 0;
+	}
+}
+
+int
+cmark_node_set_list_start(cmark_node *node, int start) {
+	if (start < 0) {
+		return 0;
+	}
+
+	if (node->type == CMARK_NODE_LIST) {
+		node->as.list.start = start;
+		return 1;
+	}
+	else {
+		return 0;
+	}
+}
+
+int
+cmark_node_get_list_tight(cmark_node *node) {
+	if (node->type == CMARK_NODE_LIST) {
+		return node->as.list.tight;
+	}
+	else {
+		return 0;
+	}
+}
+
+int
+cmark_node_set_list_tight(cmark_node *node, int tight) {
+	if (node->type == CMARK_NODE_LIST) {
+		node->as.list.tight = tight;
+		return 1;
+	}
+	else {
+		return 0;
+	}
+}
+
+const char*
+cmark_node_get_fence_info(cmark_node *node) {
+	if (node->type == NODE_FENCED_CODE) {
+		return cmark_strbuf_cstr(&node->as.code.info);
+	}
+	else {
+		return NULL;
+	}
+}
+
+int
+cmark_node_set_fence_info(cmark_node *node, const char *info) {
+	if (node->type == NODE_FENCED_CODE) {
+		cmark_strbuf_sets(&node->as.code.info, info);
+		return 1;
+	}
+	else {
+		return 0;
+	}
+}
+
 const char*
 cmark_node_get_url(cmark_node *node) {
 	switch (node->type) {
@@ -152,6 +290,49 @@ cmark_node_set_url(cmark_node *node, const char *url) {
 	return 0;
 }
 
+const char*
+cmark_node_get_title(cmark_node *node) {
+	switch (node->type) {
+	case NODE_LINK:
+	case NODE_IMAGE:
+		return (char *)node->as.link.title;
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
+int
+cmark_node_set_title(cmark_node *node, const char *title) {
+	switch (node->type) {
+	case NODE_LINK:
+	case NODE_IMAGE:
+		free(node->as.link.title);
+		node->as.link.title = (unsigned char *)S_strdup(title);
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+int
+cmark_node_get_start_line(cmark_node *node) {
+	return node->start_line;
+}
+
+int
+cmark_node_get_start_column(cmark_node *node) {
+	return node->start_column;
+}
+
+int
+cmark_node_get_end_line(cmark_node *node) {
+	return node->end_line;
+}
+
 static inline bool
 S_is_block(cmark_node *node) {
 	return node->type >= CMARK_NODE_FIRST_BLOCK