static void iterator(test_batch_runner *runner) { cmark_node *doc = cmark_parse_document("> a *b*\n\nc", 10, CMARK_OPT_DEFAULT); int parnodes = 0; cmark_event_type ev_type; cmark_iter *iter = cmark_iter_new(doc); cmark_node *cur; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); if (cur->type == CMARK_NODE_PARAGRAPH && ev_type == CMARK_EVENT_ENTER) { parnodes += 1; } } INT_EQ(runner, parnodes, 2, "iterate correctly counts paragraphs"); cmark_iter_free(iter); cmark_node_free(doc); }
char *cmark_render(cmark_node *root, int options, int width, void (*outc)(cmark_renderer *, cmark_escaping, int32_t, unsigned char), int (*render_node)(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options)) { cmark_strbuf pref = GH_BUF_INIT; cmark_strbuf buf = GH_BUF_INIT; cmark_node *cur; cmark_event_type ev_type; char *result; cmark_iter *iter = cmark_iter_new(root); cmark_renderer renderer = {&buf, &pref, 0, width, 0, 0, true, true, false, false, outc, S_cr, S_blankline, S_out }; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); if (!render_node(&renderer, cur, ev_type, options)) { // a false value causes us to skip processing // the node's contents. this is used for // autolinks. cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT); } } // ensure final newline if (renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') { cmark_strbuf_putc(renderer.buffer, '\n'); } result = (char *)cmark_strbuf_detach(renderer.buffer); cmark_iter_free(iter); cmark_strbuf_free(renderer.prefix); cmark_strbuf_free(renderer.buffer); return result; }
char *cmark_render_man(cmark_node *root, long options) { char *result; cmark_strbuf man = GH_BUF_INIT; struct render_state state = { &man, NULL }; cmark_node *cur; cmark_event_type ev_type; cmark_iter *iter = cmark_iter_new(root); if (options == 0) options = 0; // avoid warning about unused parameters while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); S_render_node(cur, ev_type, &state); } result = (char *)cmark_strbuf_detach(&man); cmark_iter_free(iter); cmark_strbuf_free(&man); return result; }
int md_process(cmark_node *const node) { assert(node); cmark_iter *iter = cmark_iter_new(node); assert(iter); // Due to the way cmark_iter_reset works, we're missing // the first event each time. But that's fine. cmark_iter_reset(iter, node, CMARK_EVENT_ENTER); md_escape(iter); cmark_iter_reset(iter, node, CMARK_EVENT_ENTER); md_escape_inline(iter); cmark_iter_reset(iter, node, CMARK_EVENT_ENTER); md_autolink(iter); cmark_iter_reset(iter, node, CMARK_EVENT_ENTER); md_block_external_images(iter); cmark_iter_reset(iter, node, CMARK_EVENT_ENTER); md_convert_hashes(iter); cmark_iter_free(iter); iter = NULL; return 0; }
char *cmark_render_xml(cmark_node *root, int options) { char *result; cmark_strbuf xml = GH_BUF_INIT; cmark_event_type ev_type; cmark_node *cur; struct render_state state = {&xml, 0}; cmark_iter *iter = cmark_iter_new(root); cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); cmark_strbuf_puts(state.xml, "<!DOCTYPE CommonMark SYSTEM \"CommonMark.dtd\">\n"); while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cur = cmark_iter_get_node(iter); S_render_node(cur, ev_type, &state, options); } result = (char *)cmark_strbuf_detach(&xml); cmark_iter_free(iter); return result; }
static void iterator_delete(test_batch_runner *runner) { static const char md[] = "a *b* c\n" "\n" "* item1\n" "* item2\n" "\n" "a `b` c\n" "\n" "* item1\n" "* item2\n"; cmark_node *doc = cmark_parse_document(md, sizeof(md) - 1, CMARK_OPT_DEFAULT); cmark_iter *iter = cmark_iter_new(doc); cmark_event_type ev_type; while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { cmark_node *node = cmark_iter_get_node(iter); // Delete list, emph, and code nodes. if ((ev_type == CMARK_EVENT_EXIT && node->type == CMARK_NODE_LIST) || (ev_type == CMARK_EVENT_EXIT && node->type == CMARK_NODE_EMPH) || (ev_type == CMARK_EVENT_ENTER && node->type == CMARK_NODE_CODE)) { cmark_node_free(node); } } char *html = cmark_render_html(doc, CMARK_OPT_DEFAULT); static const char expected[] = "<p>a c</p>\n" "<p>a c</p>\n"; STR_EQ(runner, html, expected, "iterate and delete nodes"); free(html); cmark_iter_free(iter); cmark_node_free(doc); }
// Convert a single node. static char* S_node_to_pod(cmark_node *node, CFCClass *klass, int header_level) { char *result = CFCUtil_strdup(""); if (node == NULL) { return result; } int found_matching_code_block = false; cmark_iter *iter = cmark_iter_new(node); cmark_event_type ev_type; while (CMARK_EVENT_DONE != (ev_type = cmark_iter_next(iter))) { cmark_node *node = cmark_iter_get_node(iter); cmark_node_type type = cmark_node_get_type(node); switch (type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_PARAGRAPH: if (ev_type == CMARK_EVENT_EXIT) { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_BLOCK_QUOTE: case CMARK_NODE_LIST: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=over\n\n", NULL); } else { result = CFCUtil_cat(result, "=back\n\n", NULL); } break; case CMARK_NODE_ITEM: // TODO: Ordered lists. if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "=item *\n\n", NULL); } break; case CMARK_NODE_HEADER: if (ev_type == CMARK_EVENT_ENTER) { int extra_level = cmark_node_get_header_level(node) - 1; char *header = CFCUtil_sprintf("=head%d ", header_level + extra_level); result = CFCUtil_cat(result, header, NULL); FREEMEM(header); } else { result = CFCUtil_cat(result, "\n\n", NULL); } break; case CMARK_NODE_CODE_BLOCK: { int is_host = CFCMarkdown_code_block_is_host(node, "perl"); if (is_host) { found_matching_code_block = true; const char *content = cmark_node_get_literal(node); char *copy = CFCUtil_strdup(content); // Chomp trailing newline. size_t len = strlen(copy); if (len > 0 && copy[len-1] == '\n') { copy[len-1] = '\0'; } char *indented = CFCUtil_global_replace(copy, "\n", "\n "); result = CFCUtil_cat(result, " ", indented, "\n\n", NULL); FREEMEM(indented); FREEMEM(copy); } if (CFCMarkdown_code_block_is_last(node)) { if (!found_matching_code_block) { result = CFCUtil_cat(result, " Code example for Perl is missing\n\n"); } else { // Reset. found_matching_code_block = false; } } break; } case CMARK_NODE_HTML: { const char *html = cmark_node_get_literal(node); result = CFCUtil_cat(result, "=begin html\n\n", html, "\n=end\n\n", NULL); break; } case CMARK_NODE_HRULE: break; case CMARK_NODE_TEXT: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, escaped, NULL); FREEMEM(escaped); break; } case CMARK_NODE_LINEBREAK: // POD doesn't support line breaks. Start a new paragraph. result = CFCUtil_cat(result, "\n\n", NULL); break; case CMARK_NODE_SOFTBREAK: result = CFCUtil_cat(result, "\n", NULL); break; case CMARK_NODE_CODE: { const char *content = cmark_node_get_literal(node); char *escaped = S_pod_escape(content); result = CFCUtil_cat(result, "C<", escaped, ">", NULL); FREEMEM(escaped); break; } case CMARK_NODE_INLINE_HTML: { const char *html = cmark_node_get_literal(node); CFCUtil_warn("Inline HTML not supported in POD: %s", html); break; } case CMARK_NODE_LINK: if (ev_type == CMARK_EVENT_ENTER) { char *pod = S_convert_link(node, klass, header_level); result = CFCUtil_cat(result, pod, NULL); FREEMEM(pod); cmark_iter_reset(iter, node, CMARK_EVENT_EXIT); } break; case CMARK_NODE_IMAGE: CFCUtil_warn("Images not supported in POD"); break; case CMARK_NODE_STRONG: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "B<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; case CMARK_NODE_EMPH: if (ev_type == CMARK_EVENT_ENTER) { result = CFCUtil_cat(result, "I<", NULL); } else { result = CFCUtil_cat(result, ">", NULL); } break; default: CFCUtil_die("Invalid cmark node type: %d", (int)type); break; } } cmark_iter_free(iter); return result; }