const char *cmark_node_get_fence_info(cmark_node *node) { if (node == NULL) { return NULL; } if (node->type == CMARK_NODE_CODE_BLOCK) { return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.info); } else { return NULL; } }
const char *cmark_node_get_literal(cmark_node *node) { if (node == NULL) { return NULL; } switch (node->type) { case CMARK_NODE_HTML_BLOCK: case CMARK_NODE_TEXT: case CMARK_NODE_HTML_INLINE: case CMARK_NODE_CODE: return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal); case CMARK_NODE_CODE_BLOCK: return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.code.literal); default: break; } return NULL; }
const char *cmark_node_get_on_exit(cmark_node *node) { if (node == NULL) { return NULL; } switch (node->type) { case CMARK_NODE_CUSTOM_INLINE: case CMARK_NODE_CUSTOM_BLOCK: return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.custom.on_exit); default: break; } return NULL; }
const char *cmark_node_get_title(cmark_node *node) { if (node == NULL) { return NULL; } switch (node->type) { case CMARK_NODE_LINK: case CMARK_NODE_IMAGE: return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.link.title); default: break; } return NULL; }
const char* 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; } return NULL; }
static int S_render_node(cmark_renderer *renderer, cmark_node *node, cmark_event_type ev_type, int options) { cmark_node *tmp; int list_number; cmark_delim_type list_delim; int numticks; int i; bool entering = (ev_type == CMARK_EVENT_ENTER); const char *info, *code, *title; size_t info_len, code_len; char listmarker[LISTMARKER_SIZE]; char *emph_delim; bool first_in_list_item; bufsize_t marker_width; bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) && !(CMARK_OPT_HARDBREAKS & options); // Don't adjust tight list status til we've started the list. // Otherwise we loose the blank line between a paragraph and // a following list. if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL && entering)) { tmp = get_containing_block(node); renderer->in_tight_list_item = tmp && // tmp might be NULL if there is no containing block ((tmp->type == CMARK_NODE_ITEM && cmark_node_get_list_tight(tmp->parent)) || (tmp && tmp->parent && tmp->parent->type == CMARK_NODE_ITEM && cmark_node_get_list_tight(tmp->parent->parent))); } if (node->extension && node->extension->commonmark_render_func) { node->extension->commonmark_render_func(node->extension, renderer, node, ev_type, options); return 1; } switch (node->type) { case CMARK_NODE_DOCUMENT: break; case CMARK_NODE_BLOCK_QUOTE: if (entering) { LIT("> "); renderer->begin_content = true; cmark_strbuf_puts(renderer->prefix, "> "); } else { cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2); BLANKLINE(); } break; case CMARK_NODE_LIST: if (!entering && node->next && (node->next->type == CMARK_NODE_CODE_BLOCK || node->next->type == CMARK_NODE_LIST)) { // this ensures that a following indented code block or list will be // inteprereted correctly. CR(); LIT("<!-- end list -->"); BLANKLINE(); } break; case CMARK_NODE_ITEM: if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { marker_width = 4; } else { list_number = cmark_node_get_list_start(node->parent); list_delim = cmark_node_get_list_delim(node->parent); tmp = node; while (tmp->prev) { tmp = tmp->prev; list_number += 1; } // we ensure a width of at least 4 so // we get nice transition from single digits // to double snprintf(listmarker, LISTMARKER_SIZE, "%d%s%s", list_number, list_delim == CMARK_PAREN_DELIM ? ")" : ".", list_number < 10 ? " " : " "); marker_width = (bufsize_t)strlen(listmarker); } if (entering) { if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { LIT(" - "); renderer->begin_content = true; } else { LIT(listmarker); renderer->begin_content = true; } for (i = marker_width; i--;) { cmark_strbuf_putc(renderer->prefix, ' '); } } else { cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - marker_width); CR(); } break; case CMARK_NODE_HEADING: if (entering) { for (i = cmark_node_get_heading_level(node); i > 0; i--) { LIT("#"); } LIT(" "); renderer->begin_content = true; renderer->no_linebreaks = true; } else { renderer->no_linebreaks = false; BLANKLINE(); } break; case CMARK_NODE_CODE_BLOCK: first_in_list_item = node->prev == NULL && node->parent && node->parent->type == CMARK_NODE_ITEM; if (!first_in_list_item) { BLANKLINE(); } info = cmark_node_get_fence_info(node); info_len = strlen(info); code = cmark_node_get_literal(node); code_len = strlen(code); // use indented form if no info, and code doesn't // begin or end with a blank line, and code isn't // first thing in a list item if (info_len == 0 && (code_len > 2 && !cmark_isspace(code[0]) && !(cmark_isspace(code[code_len - 1]) && cmark_isspace(code[code_len - 2]))) && !first_in_list_item) { LIT(" "); cmark_strbuf_puts(renderer->prefix, " "); OUT(cmark_node_get_literal(node), false, LITERAL); cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4); } else { numticks = longest_backtick_sequence(code) + 1; if (numticks < 3) { numticks = 3; } for (i = 0; i < numticks; i++) { LIT("`"); } LIT(" "); OUT(info, false, LITERAL); CR(); OUT(cmark_node_get_literal(node), false, LITERAL); CR(); for (i = 0; i < numticks; i++) { LIT("`"); } } BLANKLINE(); break; case CMARK_NODE_HTML_BLOCK: BLANKLINE(); OUT(cmark_node_get_literal(node), false, LITERAL); BLANKLINE(); break; case CMARK_NODE_CUSTOM_BLOCK: BLANKLINE(); OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), false, LITERAL); BLANKLINE(); break; case CMARK_NODE_THEMATIC_BREAK: BLANKLINE(); LIT("-----"); BLANKLINE(); break; case CMARK_NODE_PARAGRAPH: if (!entering) { BLANKLINE(); } break; case CMARK_NODE_TEXT: OUT(cmark_node_get_literal(node), allow_wrap, NORMAL); break; case CMARK_NODE_LINEBREAK: if (!(CMARK_OPT_HARDBREAKS & options)) { LIT(" "); } CR(); break; case CMARK_NODE_SOFTBREAK: if (CMARK_OPT_HARDBREAKS & options) { LIT(" "); CR(); } else if (!renderer->no_linebreaks && renderer->width == 0 && !(CMARK_OPT_HARDBREAKS & options) && !(CMARK_OPT_NOBREAKS & options)) { CR(); } else { OUT(" ", allow_wrap, LITERAL); } break; case CMARK_NODE_CODE: code = cmark_node_get_literal(node); code_len = strlen(code); numticks = shortest_unused_backtick_sequence(code); for (i = 0; i < numticks; i++) { LIT("`"); } if (code_len == 0 || code[0] == '`') { LIT(" "); } OUT(cmark_node_get_literal(node), allow_wrap, LITERAL); if (code_len == 0 || code[code_len - 1] == '`') { LIT(" "); } for (i = 0; i < numticks; i++) { LIT("`"); } break; case CMARK_NODE_HTML_INLINE: OUT(cmark_node_get_literal(node), false, LITERAL); break; case CMARK_NODE_CUSTOM_INLINE: OUT(entering ? cmark_node_get_on_enter(node) : cmark_node_get_on_exit(node), false, LITERAL); break; case CMARK_NODE_STRONG: if (entering) { LIT("**"); } else { LIT("**"); } break; case CMARK_NODE_EMPH: // If we have EMPH(EMPH(x)), we need to use *_x_* // because **x** is STRONG(x): if (node->parent && node->parent->type == CMARK_NODE_EMPH && node->next == NULL && node->prev == NULL) { emph_delim = "_"; } else { emph_delim = "*"; } if (entering) { LIT(emph_delim); } else { LIT(emph_delim); } break; case CMARK_NODE_LINK: if (is_autolink(node)) { if (entering) { LIT("<"); if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) { LIT((const char *)cmark_node_get_url(node) + 7); } else { LIT((const char *)cmark_node_get_url(node)); } LIT(">"); // return signal to skip contents of node... return 0; } } else { if (entering) { LIT("["); } else { LIT("]("); OUT(cmark_node_get_url(node), false, URL); title = cmark_node_get_title(node); if (strlen(title) > 0) { LIT(" \""); OUT(title, false, TITLE); LIT("\""); } LIT(")"); } } break; case CMARK_NODE_IMAGE: if (entering) { LIT("!["); } else { LIT("]("); OUT(cmark_node_get_url(node), false, URL); title = cmark_node_get_title(node); if (strlen(title) > 0) { OUT(" \"", allow_wrap, LITERAL); OUT(title, false, TITLE); LIT("\""); } LIT(")"); } break; case CMARK_NODE_FOOTNOTE_REFERENCE: if (entering) { LIT("[^"); OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL); LIT("]"); } break; case CMARK_NODE_FOOTNOTE_DEFINITION: if (entering) { renderer->footnote_ix += 1; LIT("[^"); char n[32]; snprintf(n, sizeof(n), "%d", renderer->footnote_ix); OUT(n, false, LITERAL); LIT("]:\n"); cmark_strbuf_puts(renderer->prefix, " "); } else { cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4); } break; default: assert(false); break; } return 1; }
static void postprocess_text(cmark_parser *parser, cmark_node *text, int offset) { size_t link_end; uint8_t *data = text->as.literal.data, *at; size_t size = text->as.literal.len; int rewind, max_rewind, nb = 0, np = 0, ns = 0; if (offset < 0 || (size_t)offset >= size) return; data += offset; size -= offset; at = (uint8_t *)memchr(data, '@', size); if (!at) return; max_rewind = (int)(at - data); data += max_rewind; size -= max_rewind; for (rewind = 0; rewind < max_rewind; ++rewind) { uint8_t c = data[-rewind - 1]; if (cmark_isalnum(c)) continue; if (strchr(".+-_", c) != NULL) continue; if (c == '/') ns++; break; } if (rewind == 0 || ns > 0) { postprocess_text(parser, text, max_rewind + 1 + offset); return; } for (link_end = 0; link_end < size; ++link_end) { uint8_t c = data[link_end]; if (cmark_isalnum(c)) continue; if (c == '@') nb++; else if (c == '.' && link_end < size - 1 && cmark_isalnum(data[link_end + 1])) np++; else if (c != '-' && c != '_') break; } if (link_end < 2 || nb != 1 || np == 0 || (!cmark_isalpha(data[link_end - 1]) && data[link_end - 1] != '.')) { postprocess_text(parser, text, max_rewind + 1 + offset); return; } link_end = autolink_delim(data, link_end); if (link_end == 0) { postprocess_text(parser, text, max_rewind + 1 + offset); return; } cmark_chunk_to_cstr(parser->mem, &text->as.literal); cmark_node *link_node = cmark_node_new_with_mem(CMARK_NODE_LINK, parser->mem); cmark_strbuf buf; cmark_strbuf_init(parser->mem, &buf, 10); cmark_strbuf_puts(&buf, "mailto:"); cmark_strbuf_put(&buf, data - rewind, (bufsize_t)(link_end + rewind)); link_node->as.link.url = cmark_chunk_buf_detach(&buf); cmark_node *link_text = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem); cmark_chunk email = cmark_chunk_dup( &text->as.literal, offset + max_rewind - rewind, (bufsize_t)(link_end + rewind)); cmark_chunk_to_cstr(parser->mem, &email); link_text->as.literal = email; cmark_node_append_child(link_node, link_text); cmark_node_insert_after(text, link_node); cmark_node *post = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem); post->as.literal = cmark_chunk_dup(&text->as.literal, (bufsize_t)(offset + max_rewind + link_end), (bufsize_t)(size - link_end)); cmark_chunk_to_cstr(parser->mem, &post->as.literal); cmark_node_insert_after(link_node, post); text->as.literal.len = offset + max_rewind - rewind; text->as.literal.data[text->as.literal.len] = 0; postprocess_text(parser, post, 0); }