From 46330867a8d82bf45f2ea575b93c5f0d4496ca0c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 11 Jul 2015 23:15:31 -0700 Subject: Restructured common renderer code. * Added functions for cr, blankline, out to renderer object. * Removed lit (we'll handle this with a macro). * Changed type of out so it takes a regular string instead of a chunk. * Use macros LIT, OUT, BLANKLINE, CR in renderers to simplify code. (Not sure about this, but `renderer->out(renderer, ...)` sure is verbose.) --- src/commonmark.c | 186 ++++++++++++++++++++++++++++--------------------------- src/latex.c | 166 +++++++++++++++++++++++++------------------------ src/render.c | 29 ++++----- src/render.h | 17 ++--- 4 files changed, 198 insertions(+), 200 deletions(-) diff --git a/src/commonmark.c b/src/commonmark.c index 5bbc8d4..274d4bc 100644 --- a/src/commonmark.c +++ b/src/commonmark.c @@ -12,6 +12,12 @@ #include "scanners.h" #include "render.h" +#define safe_strlen(s) cmark_strbuf_safe_strlen(s) +#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) +#define LIT(s) renderer->out(renderer, s, false, LITERAL) +#define CR() renderer->cr(renderer) +#define BLANKLINE() renderer->blankline(renderer) + // Functions to convert cmark_nodes to commonmark strings. static inline void outc(cmark_renderer *renderer, @@ -58,13 +64,14 @@ static inline void outc(cmark_renderer *renderer, } static int -longest_backtick_sequence(cmark_chunk *code) +longest_backtick_sequence(const char *code) { int longest = 0; int current = 0; - int i = 0; - while (i <= code->len) { - if (code->data[i] == '`') { + size_t i = 0; + size_t code_len = safe_strlen(code); + while (i <= code_len) { + if (code[i] == '`') { current++; } else { if (current > longest) { @@ -78,13 +85,14 @@ longest_backtick_sequence(cmark_chunk *code) } static int -shortest_unused_backtick_sequence(cmark_chunk *code) +shortest_unused_backtick_sequence(const char *code) { int32_t used = 1; int current = 0; - int i = 0; - while (i <= code->len) { - if (code->data[i] == '`') { + size_t i = 0; + size_t code_len = safe_strlen(code); + while (i <= code_len) { + if (code[i] == '`') { current++; } else { if (current) { @@ -159,14 +167,13 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, cmark_renderer *renderer) { cmark_node *tmp; - cmark_chunk *code; int list_number; cmark_delim_type list_delim; int numticks; int i; bool entering = (ev_type == CMARK_EVENT_ENTER); - cmark_chunk *info; - cmark_chunk *title; + size_t info_len; + size_t code_len; cmark_strbuf listmarker = GH_BUF_INIT; char *emph_delim; bufsize_t marker_width; @@ -195,12 +202,12 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, case CMARK_NODE_BLOCK_QUOTE: if (entering) { - lit(renderer, "> ", false); + LIT("> "); cmark_strbuf_puts(renderer->prefix, "> "); } else { cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 2); - blankline(renderer); + BLANKLINE(); } break; @@ -210,7 +217,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, node->next->type == CMARK_NODE_LIST)) { // this ensures 2 blank lines after list, // if before code block or list: - lit(renderer, "\n", false); + LIT("\n"); renderer->need_cr = 0; } break; @@ -240,10 +247,10 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, if (entering) { if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) { - lit(renderer, "* ", false); + LIT("* "); cmark_strbuf_puts(renderer->prefix, " "); } else { - lit(renderer, (char *)listmarker.ptr, false); + LIT((char *)listmarker.ptr); for (i = marker_width; i--;) { cmark_strbuf_putc(renderer->prefix, ' '); } @@ -252,7 +259,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - marker_width); - cr(renderer); + CR(); } cmark_strbuf_free(&listmarker); break; @@ -260,33 +267,35 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, case CMARK_NODE_HEADER: if (entering) { for (int i = cmark_node_get_header_level(node); i > 0; i--) { - lit(renderer, "#", false); + LIT("#"); } - lit(renderer, " ", false); + LIT(" "); renderer->no_wrap = true; } else { renderer->no_wrap = false; - blankline(renderer); + BLANKLINE(); } break; case CMARK_NODE_CODE_BLOCK: - blankline(renderer); - info = &node->as.code.info; - code = &node->as.code.literal; + BLANKLINE(); + const char* info = cmark_node_get_fence_info(node); + info_len = safe_strlen(info); + const char* code = cmark_node_get_literal(node); + code_len = safe_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 && - !isspace(code->data[0]) && - !(isspace(code->data[code->len - 1]) && - isspace(code->data[code->len - 2]))) && + if (info_len == 0 && + (code_len > 2 && + !isspace(code[0]) && + !(isspace(code[code_len - 1]) && + isspace(code[code_len - 2]))) && !(node->prev == NULL && node->parent && node->parent->type == CMARK_NODE_ITEM)) { - lit(renderer, " ", false); + LIT(" "); cmark_strbuf_puts(renderer->prefix, " "); - out(renderer, node->as.code.literal, false, LITERAL); + OUT(cmark_node_get_literal(node), false, LITERAL); cmark_strbuf_truncate(renderer->prefix, renderer->prefix->size - 4); } else { @@ -295,84 +304,85 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, numticks = 3; } for (i = 0; i < numticks; i++) { - lit(renderer, "`", false); + LIT("`"); } - lit(renderer, " ", false); - out(renderer, *info, false, LITERAL); - cr(renderer); - out(renderer, node->as.code.literal, false, LITERAL); - cr(renderer); + LIT(" "); + OUT(info, false, LITERAL); + CR(); + OUT(cmark_node_get_literal(node), false, LITERAL); + CR(); for (i = 0; i < numticks; i++) { - lit(renderer, "`", false); + LIT("`"); } } - blankline(renderer); + BLANKLINE(); break; case CMARK_NODE_HTML: - blankline(renderer); - out(renderer, node->as.literal, false, LITERAL); - blankline(renderer); + BLANKLINE(); + OUT(cmark_node_get_literal(node), false, LITERAL); + BLANKLINE(); break; case CMARK_NODE_HRULE: - blankline(renderer); - lit(renderer, "-----", false); - blankline(renderer); + BLANKLINE(); + LIT("-----"); + BLANKLINE(); break; case CMARK_NODE_PARAGRAPH: if (!entering) { - blankline(renderer); + BLANKLINE(); } break; case CMARK_NODE_TEXT: - out(renderer, node->as.literal, true, NORMAL); + OUT(cmark_node_get_literal(node), true, NORMAL); break; case CMARK_NODE_LINEBREAK: if (!(CMARK_OPT_HARDBREAKS & renderer->options)) { - lit(renderer, "\\", false); + LIT("\\"); } - cr(renderer); + CR(); break; case CMARK_NODE_SOFTBREAK: if (renderer->width == 0) { - cr(renderer); + CR(); } else { - lit(renderer, " ", true); + OUT(" ", true, LITERAL); } break; case CMARK_NODE_CODE: - code = &node->as.literal; + code = cmark_node_get_literal(node); + code_len = safe_strlen(code); numticks = shortest_unused_backtick_sequence(code); for (i = 0; i < numticks; i++) { - lit(renderer, "`", false); + LIT("`"); } - if (code->len == 0 || code->data[0] == '`') { - lit(renderer, " ", false); + if (code_len == 0 || code[0] == '`') { + LIT(" "); } - out(renderer, node->as.literal, true, LITERAL); - if (code->len == 0 || code->data[code->len - 1] == '`') { - lit(renderer, " ", false); + OUT(cmark_node_get_literal(node), true, LITERAL); + if (code_len == 0 || code[code_len - 1] == '`') { + LIT(" "); } for (i = 0; i < numticks; i++) { - lit(renderer, "`", false); + LIT("`"); } break; case CMARK_NODE_INLINE_HTML: - out(renderer, node->as.literal, false, LITERAL); + OUT(cmark_node_get_literal(node), false, LITERAL); break; case CMARK_NODE_STRONG: if (entering) { - lit(renderer, "**", false); + LIT("**"); } else { - lit(renderer, "**", false); + LIT("**"); } break; @@ -386,62 +396,56 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, emph_delim = "*"; } if (entering) { - lit(renderer, emph_delim, false); + LIT(emph_delim); } else { - lit(renderer, emph_delim, false); + LIT(emph_delim); } break; case CMARK_NODE_LINK: if (is_autolink(node)) { if (entering) { - lit(renderer, "<", false); + LIT("<"); if (strncmp(cmark_node_get_url(node), "mailto:", 7) == 0) { - lit(renderer, - (char *)cmark_node_get_url(node) + 7, - false); + LIT((char *)cmark_node_get_url(node) + 7); } else { - lit(renderer, - (char *)cmark_node_get_url(node), - false); + LIT((char *)cmark_node_get_url(node)); } - lit(renderer, ">", false); + LIT(">"); // return signal to skip contents of node... return 0; } } else { if (entering) { - lit(renderer, "[", false); + LIT("["); } else { - lit(renderer, "](", false); - out(renderer, - cmark_chunk_literal(cmark_node_get_url(node)), - false, URL); - title = &node->as.link.title; - if (title->len > 0) { - lit(renderer, " \"", true); - out(renderer, *title, false, TITLE); - lit(renderer, "\"", false); + LIT("]("); + OUT(cmark_node_get_url(node), false, URL); + const char* title = cmark_node_get_title(node); + if (safe_strlen(title) > 0) { + LIT(" \""); + OUT(title, false, TITLE); + LIT("\""); } - lit(renderer, ")", false); + LIT(")"); } } break; case CMARK_NODE_IMAGE: if (entering) { - lit(renderer, "![", false); + LIT("!["); } else { - lit(renderer, "](", false); - out(renderer, cmark_chunk_literal(cmark_node_get_url(node)), false, URL); - title = &node->as.link.title; - if (title->len > 0) { - lit(renderer, " \"", true); - out(renderer, *title, false, TITLE); - lit(renderer, "\"", false); + LIT("]("); + OUT(cmark_node_get_url(node), false, URL); + const char* title = cmark_node_get_title(node); + if (safe_strlen(title) > 0) { + OUT(" \"", true, LITERAL); + OUT(title, false, TITLE); + LIT("\""); } - lit(renderer, ")", false); + LIT(")"); } break; diff --git a/src/latex.c b/src/latex.c index 6cca96e..058f212 100644 --- a/src/latex.c +++ b/src/latex.c @@ -12,6 +12,12 @@ #include "scanners.h" #include "render.h" +#define safe_strlen(s) cmark_strbuf_safe_strlen(s) +#define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping) +#define LIT(s) renderer->out(renderer, s, false, LITERAL) +#define CR() renderer->cr(renderer) +#define BLANKLINE() renderer->blankline(renderer) + static inline void outc(cmark_renderer *renderer, cmark_escaping escape, int32_t c, @@ -188,8 +194,7 @@ typedef enum { static link_type get_link_type(cmark_node *node) { - cmark_chunk *title; - cmark_chunk *url; + size_t title_len, url_len; cmark_node *link_text; char *realurl; int realurllen; @@ -199,21 +204,25 @@ get_link_type(cmark_node *node) return NO_LINK; } - url = &node->as.link.url; - if (url->len == 0 || scan_scheme(url, 0) == 0) { + const char* url = cmark_node_get_url(node); + cmark_chunk url_chunk = cmark_chunk_literal(url); + + url_len = safe_strlen(url); + if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) { return NO_LINK; } - title = &node->as.link.title; + const char* title = cmark_node_get_title(node); + title_len = safe_strlen(title); // if it has a title, we can't treat it as an autolink: - if (title->len > 0) { + if (title_len > 0) { return NORMAL_LINK; } link_text = node->first_child; cmark_consolidate_text_nodes(link_text); - realurl = (char*)url->data; - realurllen = url->len; + realurl = (char*)url; + realurllen = url_len; if (strncmp(realurl, "mailto:", 7) == 0) { realurl += 7; realurllen -= 7; @@ -255,7 +264,6 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, char list_number_string[20]; bool entering = (ev_type == CMARK_EVENT_ENTER); cmark_list_type list_type; - cmark_chunk url; const char* roman_numerals[] = { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x" }; @@ -284,11 +292,11 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, case CMARK_NODE_BLOCK_QUOTE: if (entering) { - lit(renderer, "\\begin{quote}", false); - cr(renderer); + LIT("\\begin{quote}"); + CR(); } else { - lit(renderer, "\\end{quote}", false); - blankline(renderer); + LIT("\\end{quote}"); + BLANKLINE(); } break; @@ -298,44 +306,39 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, if (list_type == CMARK_ORDERED_LIST) { renderer->enumlevel++; } - lit(renderer, "\\begin{", false); - lit(renderer, - list_type == CMARK_ORDERED_LIST ? - "enumerate" : "itemize", false); - lit(renderer, "}", false); - cr(renderer); + LIT("\\begin{"); + LIT(list_type == CMARK_ORDERED_LIST ? + "enumerate" : "itemize"); + LIT("}"); + CR(); list_number = cmark_node_get_list_start(node); if (list_number > 1) { sprintf(list_number_string, "%d", list_number); - lit(renderer, "\\setcounter{enum", false); - lit(renderer, (char *)roman_numerals[renderer->enumlevel], - false); - lit(renderer, "}{", false); - out(renderer, - cmark_chunk_literal(list_number_string), - false, NORMAL); - lit(renderer, "}", false); - cr(renderer); + LIT("\\setcounter{enum"); + LIT((char *)roman_numerals[renderer->enumlevel]); + LIT("}{"); + OUT(list_number_string, false, NORMAL); + LIT("}"); + CR(); } } else { if (list_type == CMARK_ORDERED_LIST) { renderer->enumlevel--; } - lit(renderer, "\\end{", false); - lit(renderer, - list_type == CMARK_ORDERED_LIST ? - "enumerate" : "itemize", false); - lit(renderer, "}", false); - blankline(renderer); + LIT("\\end{"); + LIT(list_type == CMARK_ORDERED_LIST ? + "enumerate" : "itemize"); + LIT("}"); + BLANKLINE(); } break; case CMARK_NODE_ITEM: if (entering) { - lit(renderer, "\\item ", false); + LIT("\\item "); } else { - cr(renderer); + CR(); } break; @@ -343,74 +346,74 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, if (entering) { switch (cmark_node_get_header_level(node)) { case 1: - lit(renderer, "\\section", false); + LIT("\\section"); break; case 2: - lit(renderer, "\\subsection", false); + LIT("\\subsection"); break; case 3: - lit(renderer, "\\subsubsection", false); + LIT("\\subsubsection"); break; case 4: - lit(renderer, "\\paragraph", false); + LIT("\\paragraph"); break; case 5: - lit(renderer, "\\subparagraph", false); + LIT("\\subparagraph"); break; } - lit(renderer, "{", false); + LIT("{"); } else { - lit(renderer, "}", false); - blankline(renderer); + LIT("}"); + BLANKLINE(); } break; case CMARK_NODE_CODE_BLOCK: - cr(renderer); - lit(renderer, "\\begin{verbatim}", false); - cr(renderer); - out(renderer, node->as.code.literal, false, LITERAL); - cr(renderer); - lit(renderer, "\\end{verbatim}", false); - blankline(renderer); + CR(); + LIT("\\begin{verbatim}"); + CR(); + OUT(cmark_node_get_literal(node), false, LITERAL); + CR(); + LIT("\\end{verbatim}"); + BLANKLINE(); break; case CMARK_NODE_HTML: break; case CMARK_NODE_HRULE: - blankline(renderer); - lit(renderer, "\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}", false); - blankline(renderer); + BLANKLINE(); + LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}"); + BLANKLINE(); break; case CMARK_NODE_PARAGRAPH: if (!entering) { - blankline(renderer); + BLANKLINE(); } break; case CMARK_NODE_TEXT: - out(renderer, node->as.literal, true, NORMAL); + OUT(cmark_node_get_literal(node), true, NORMAL); break; case CMARK_NODE_LINEBREAK: - lit(renderer, "\\\\", false); - cr(renderer); + LIT("\\\\"); + CR(); break; case CMARK_NODE_SOFTBREAK: if (renderer->width == 0) { - cr(renderer); + CR(); } else { - lit(renderer, " ", true); + OUT(" ", true, NORMAL); } break; case CMARK_NODE_CODE: - lit(renderer, "\\texttt{", false); - out(renderer, node->as.literal, false, NORMAL); - lit(renderer, "}", false); + LIT("\\texttt{"); + OUT(cmark_node_get_literal(node), false, NORMAL); + LIT("}"); break; case CMARK_NODE_INLINE_HTML: @@ -418,55 +421,54 @@ S_render_node(cmark_node *node, cmark_event_type ev_type, case CMARK_NODE_STRONG: if (entering) { - lit(renderer, "\\textbf{", false); + LIT("\\textbf{"); } else { - lit(renderer, "}", false); + LIT("}"); } break; case CMARK_NODE_EMPH: if (entering) { - lit(renderer, "\\emph{", false); + LIT("\\emph{"); } else { - lit(renderer, "}", false); + LIT("}"); } break; case CMARK_NODE_LINK: if (entering) { - url = cmark_chunk_literal(cmark_node_get_url(node)); + const char* url = cmark_node_get_url(node); // requires \usepackage{hyperref} switch(get_link_type(node)) { case URL_AUTOLINK: - lit(renderer, "\\url{", false); - out(renderer, url, false, URL); + LIT("\\url{"); + OUT(url, false, URL); break; case EMAIL_AUTOLINK: - lit(renderer, "\\href{", false); - out(renderer, url, false, URL); - lit(renderer, "}\\nolinkurl{", false); + LIT("\\href{"); + OUT(url, false, URL); + LIT("}\\nolinkurl{"); break; case NORMAL_LINK: - lit(renderer, "\\href{", false); - out(renderer, url, false, URL); - lit(renderer, "}{", false); + LIT("\\href{"); + OUT(url, false, URL); + LIT("}{"); break; case NO_LINK: - lit(renderer, "{", false); // error? + LIT("{"); // error? } } else { - lit(renderer, "}", false); + LIT("}"); } break; case CMARK_NODE_IMAGE: if (entering) { - url = cmark_chunk_literal(cmark_node_get_url(node)); - lit(renderer, "\\protect\\includegraphics{", false); + LIT("\\protect\\includegraphics{"); // requires \include{graphicx} - out(renderer, url, false, URL); - lit(renderer, "}", false); + OUT(cmark_node_get_url(node), false, URL); + LIT("}"); return 0; } break; diff --git a/src/render.c b/src/render.c index 442a7fc..16caadc 100644 --- a/src/render.c +++ b/src/render.c @@ -5,27 +5,29 @@ #include "utf8.h" #include "render.h" -void cr(cmark_renderer *renderer) +static inline +void S_cr(cmark_renderer *renderer) { if (renderer->need_cr < 1) { renderer->need_cr = 1; } } -void blankline(cmark_renderer *renderer) +static inline +void S_blankline(cmark_renderer *renderer) { if (renderer->need_cr < 2) { renderer->need_cr = 2; } } -void out(cmark_renderer *renderer, - cmark_chunk str, - bool wrap, - cmark_escaping escape) +static +void S_out(cmark_renderer *renderer, + const char *source, + bool wrap, + cmark_escaping escape) { - unsigned char* source = str.data; - int length = str.len; + int length = cmark_strbuf_safe_strlen(source); unsigned char nextc; int32_t c; int i = 0; @@ -61,7 +63,7 @@ void out(cmark_renderer *renderer, renderer->column = renderer->prefix->size; } - len = utf8proc_iterate(source + i, length - i, &c); + len = utf8proc_iterate((const uint8_t *)source + i, length - i, &c); if (len == -1) { // error condition return; // return without rendering rest of string } @@ -114,12 +116,6 @@ void out(cmark_renderer *renderer, } } -void lit(cmark_renderer *renderer, char *s, bool wrap) -{ - cmark_chunk str = cmark_chunk_literal(s); - out(renderer, str, wrap, LITERAL); -} - char* cmark_render(cmark_node *root, int options, @@ -145,7 +141,8 @@ cmark_render(cmark_node *root, } cmark_renderer renderer = { options, &buf, &pref, 0, width, - 0, 0, 0, true, false, false, outc }; + 0, 0, 0, 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); diff --git a/src/render.h b/src/render.h index 718050e..cf5b078 100644 --- a/src/render.h +++ b/src/render.h @@ -32,21 +32,16 @@ struct cmark_renderer { cmark_escaping, int32_t, unsigned char); + void (*cr)(struct cmark_renderer*); + void (*blankline)(struct cmark_renderer*); + void (*out)(struct cmark_renderer*, + const char *, + bool, + cmark_escaping); }; typedef struct cmark_renderer cmark_renderer; -void cr(cmark_renderer *renderer); - -void blankline(cmark_renderer *renderer); - -void out(cmark_renderer *renderer, - cmark_chunk str, - bool wrap, - cmark_escaping escape); - -void lit(cmark_renderer *renderer, char *s, bool wrap); - char* cmark_render(cmark_node *root, int options, -- cgit v1.2.3