summaryrefslogtreecommitdiff
path: root/src/commonmark.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/commonmark.c')
-rw-r--r--src/commonmark.c234
1 files changed, 38 insertions, 196 deletions
diff --git a/src/commonmark.c b/src/commonmark.c
index 9ad9137..c6a13e8 100644
--- a/src/commonmark.c
+++ b/src/commonmark.c
@@ -10,182 +10,51 @@
#include "buffer.h"
#include "utf8.h"
#include "scanners.h"
+#include "render.h"
// Functions to convert cmark_nodes to commonmark strings.
-struct render_state {
- int options;
- cmark_strbuf* buffer;
- cmark_strbuf* prefix;
- int column;
- int width;
- int need_cr;
- bufsize_t last_breakable;
- bool begin_line;
- bool no_wrap;
- bool in_tight_list_item;
-};
-
-static inline void cr(struct render_state *state)
+static inline void outc(cmark_render_state *state,
+ cmark_escaping escape,
+ int32_t c,
+ unsigned char nextc)
{
- if (state->need_cr < 1) {
- state->need_cr = 1;
- }
-}
-
-static inline void blankline(struct render_state *state)
-{
- if (state->need_cr < 2) {
- state->need_cr = 2;
- }
-}
-
-typedef enum {
- LITERAL,
- NORMAL,
- TITLE,
- URL
-} escaping;
-
-static inline bool
-needs_escaping(escaping escape,
- int32_t c,
- unsigned char next_c,
- struct render_state *state)
-{
- if (escape == NORMAL) {
- return (c == '*' || c == '_' || c == '[' || c == ']' ||
- c == '<' || c == '>' || c == '\\' || c == '`' ||
- (c == '&' && isalpha(next_c)) ||
- (c == '!' && next_c == '[') ||
- (state->begin_line &&
- (c == '-' || c == '+' || c == '#' || c == '=')) ||
- (c == '#' && (isspace(next_c) || next_c == '\0')) ||
- ((c == '.' || c == ')') &&
- isdigit(state->buffer->ptr[state->buffer->size - 1])));
- } else if (escape == TITLE) {
- return (c == '`' || c == '<' || c == '>' || c == '"' ||
- c == '\\');
- } else if (escape == URL) {
- return (c == '`' || c == '<' || c == '>' || isspace(c) ||
- c == '\\' || c == ')' || c == '(');
- } else {
- return false;
- }
-}
-
-static inline void out(struct render_state *state,
- cmark_chunk str,
- bool wrap,
- escaping escape)
-{
- unsigned char* source = str.data;
- int length = str.len;
- unsigned char nextc;
- int32_t c;
- int i = 0;
- int len;
- cmark_chunk remainder = cmark_chunk_literal("");
- int k = state->buffer->size - 1;
-
- wrap = wrap && !state->no_wrap;
-
- if (state->in_tight_list_item && state->need_cr > 1) {
- state->need_cr = 1;
- }
- while (state->need_cr) {
- if (k < 0 || state->buffer->ptr[k] == '\n') {
- k -= 1;
- } else {
- cmark_strbuf_putc(state->buffer, '\n');
- if (state->need_cr > 1) {
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
- state->prefix->size);
- }
- }
- state->column = 0;
- state->begin_line = true;
- state->need_cr -= 1;
- }
-
- while (i < length) {
- if (state->begin_line) {
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
- state->prefix->size);
- // note: this assumes prefix is ascii:
- state->column = state->prefix->size;
- }
-
- len = utf8proc_iterate(source + i, length - i, &c);
- if (len == -1) { // error condition
- return; // return without rendering rest of string
- }
- nextc = source[i + len];
- if (c == 32 && wrap) {
- if (!state->begin_line) {
- cmark_strbuf_putc(state->buffer, ' ');
- state->column += 1;
- state->begin_line = false;
- state->last_breakable = state->buffer->size -
- 1;
- // skip following spaces
- while (source[i + 1] == ' ') {
- i++;
- }
- }
-
- } else if (c == 10) {
- cmark_strbuf_putc(state->buffer, '\n');
- state->column = 0;
- state->begin_line = true;
- state->last_breakable = 0;
- } else if (needs_escaping(escape, c, nextc, state)) {
- if (isspace(c)) {
- // use percent encoding for spaces
- cmark_strbuf_printf(state->buffer, "%%%2x", c);
- state->column += 3;
- } else {
- cmark_strbuf_putc(state->buffer, '\\');
- utf8proc_encode_char(c, state->buffer);
- state->column += 2;
- }
- state->begin_line = false;
+ bool needs_escaping = false;
+ needs_escaping =
+ escape != LITERAL &&
+ ((escape == NORMAL &&
+ (c == '*' || c == '_' || c == '[' || c == ']' ||
+ c == '<' || c == '>' || c == '\\' || c == '`' ||
+ (c == '&' && isalpha(nextc)) ||
+ (c == '!' && nextc == '[') ||
+ (state->begin_line &&
+ (c == '-' || c == '+' || c == '#' || c == '=')) ||
+ ((c == '.' || c == ')') &&
+ isdigit(state->buffer->ptr[state->buffer->size - 1])))) ||
+ (escape == URL &&
+ (c == '`' || c == '<' || c == '>' || isspace(c) ||
+ c == '\\' || c == ')' || c == '(')) ||
+ (escape == TITLE &&
+ (c == '`' || c == '<' || c == '>' || c == '"' ||
+ c == '\\')));
+
+ if (needs_escaping) {
+ if (isspace(c)) {
+ // use percent encoding for spaces
+ cmark_strbuf_printf(state->buffer, "%%%2x", c);
+ state->column += 3;
} else {
+ cmark_strbuf_putc(state->buffer, '\\');
utf8proc_encode_char(c, state->buffer);
- state->column += 1;
- state->begin_line = false;
+ state->column += 2;
}
-
- // If adding the character went beyond width, look for an
- // earlier place where the line could be broken:
- if (state->width > 0 &&
- state->column > state->width &&
- !state->begin_line &&
- state->last_breakable > 0) {
-
- // copy from last_breakable to remainder
- cmark_chunk_set_cstr(&remainder, (char *) state->buffer->ptr + state->last_breakable + 1);
- // truncate at last_breakable
- cmark_strbuf_truncate(state->buffer, state->last_breakable);
- // add newline, prefix, and remainder
- cmark_strbuf_putc(state->buffer, '\n');
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
- state->prefix->size);
- cmark_strbuf_put(state->buffer, remainder.data, remainder.len);
- state->column = state->prefix->size + remainder.len;
- cmark_chunk_free(&remainder);
- state->last_breakable = 0;
- state->begin_line = false;
- }
-
- i += len;
+ state->begin_line = false;
+ } else {
+ utf8proc_encode_char(c, state->buffer);
+ state->column += 1;
+ state->begin_line = false;
}
-}
-static void lit(struct render_state *state, char *s, bool wrap)
-{
- cmark_chunk str = cmark_chunk_literal(s);
- out(state, str, wrap, LITERAL);
}
static int
@@ -287,7 +156,7 @@ get_containing_block(cmark_node *node)
static int
S_render_node(cmark_node *node, cmark_event_type ev_type,
- struct render_state *state)
+ cmark_render_state *state)
{
cmark_node *tmp;
cmark_chunk *code;
@@ -586,32 +455,5 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
char *cmark_render_commonmark(cmark_node *root, int options, int width)
{
- char *result;
- cmark_strbuf commonmark = GH_BUF_INIT;
- cmark_strbuf prefix = GH_BUF_INIT;
- if (CMARK_OPT_HARDBREAKS & options) {
- width = 0;
- }
- struct render_state state = {
- options, &commonmark, &prefix, 0, width,
- 0, 0, true, false, false
- };
- cmark_node *cur;
- cmark_event_type ev_type;
- cmark_iter *iter = cmark_iter_new(root);
-
- while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
- cur = cmark_iter_get_node(iter);
- if (!S_render_node(cur, ev_type, &state)) {
- // 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);
- }
- }
- result = (char *)cmark_strbuf_detach(&commonmark);
-
- cmark_strbuf_free(&prefix);
- cmark_iter_free(iter);
- return result;
+ return cmark_render(root, options, width, outc, S_render_node);
}