summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api_test/harness.c2
-rw-r--r--api_test/main.c241
-rw-r--r--src/cmark.h52
-rw-r--r--src/node.c185
4 files changed, 470 insertions, 10 deletions
diff --git a/api_test/harness.c b/api_test/harness.c
index 6b38d41..e8f320c 100644
--- 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
index 1ff606b..f74dee2 100644
--- 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
index 522e77e..a74fe93 100644
--- 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
index c5ce642..624baad 100644
--- 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