cmark

My personal build of CMark ✏️

render.c (6491B)

  1 #include <stdlib.h>
  2 #include "buffer.h"
  3 #include "cmark.h"
  4 #include "utf8.h"
  5 #include "render.h"
  6 #include "node.h"
  7 #include "cmark_ctype.h"
  8 
  9 static CMARK_INLINE void S_cr(cmark_renderer *renderer) {
 10   if (renderer->need_cr < 1) {
 11     renderer->need_cr = 1;
 12   }
 13 }
 14 
 15 static CMARK_INLINE void S_blankline(cmark_renderer *renderer) {
 16   if (renderer->need_cr < 2) {
 17     renderer->need_cr = 2;
 18   }
 19 }
 20 
 21 static void S_out(cmark_renderer *renderer, const char *source, bool wrap,
 22                   cmark_escaping escape) {
 23   int length = strlen(source);
 24   unsigned char nextc;
 25   int32_t c;
 26   int i = 0;
 27   int last_nonspace;
 28   int len;
 29   int k = renderer->buffer->size - 1;
 30 
 31   wrap = wrap && !renderer->no_linebreaks;
 32 
 33   if (renderer->in_tight_list_item && renderer->need_cr > 1) {
 34     renderer->need_cr = 1;
 35   }
 36   while (renderer->need_cr) {
 37     if (k < 0 || renderer->buffer->ptr[k] == '\n') {
 38       k -= 1;
 39     } else {
 40       cmark_strbuf_putc(renderer->buffer, '\n');
 41       if (renderer->need_cr > 1) {
 42         cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
 43                          renderer->prefix->size);
 44       }
 45     }
 46     renderer->column = 0;
 47     renderer->last_breakable = 0;
 48     renderer->begin_line = true;
 49     renderer->begin_content = true;
 50     renderer->need_cr -= 1;
 51   }
 52 
 53   while (i < length) {
 54     if (renderer->begin_line) {
 55       cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
 56                        renderer->prefix->size);
 57       // note: this assumes prefix is ascii:
 58       renderer->column = renderer->prefix->size;
 59     }
 60 
 61     len = cmark_utf8proc_iterate((const uint8_t *)source + i, length - i, &c);
 62     if (len == -1) { // error condition
 63       return;        // return without rendering rest of string
 64     }
 65     nextc = source[i + len];
 66     if (c == 32 && wrap) {
 67       if (!renderer->begin_line) {
 68         last_nonspace = renderer->buffer->size;
 69         cmark_strbuf_putc(renderer->buffer, ' ');
 70         renderer->column += 1;
 71         renderer->begin_line = false;
 72         renderer->begin_content = false;
 73         // skip following spaces
 74         while (source[i + 1] == ' ') {
 75           i++;
 76         }
 77         // We don't allow breaks that make a digit the first character
 78         // because this causes problems with commonmark output.
 79         if (!cmark_isdigit(source[i + 1])) {
 80           renderer->last_breakable = last_nonspace;
 81         }
 82       }
 83 
 84     } else if (escape == LITERAL) {
 85       if (c == 10) {
 86         cmark_strbuf_putc(renderer->buffer, '\n');
 87         renderer->column = 0;
 88         renderer->begin_line = true;
 89         renderer->begin_content = true;
 90         renderer->last_breakable = 0;
 91       } else {
 92         cmark_render_code_point(renderer, c);
 93         renderer->begin_line = false;
 94         // we don't set 'begin_content' to false til we've
 95         // finished parsing a digit.  Reason:  in commonmark
 96         // we need to escape a potential list marker after
 97         // a digit:
 98         renderer->begin_content =
 99             renderer->begin_content && cmark_isdigit(c) == 1;
100       }
101     } else {
102       (renderer->outc)(renderer, escape, c, nextc);
103       renderer->begin_line = false;
104       renderer->begin_content =
105           renderer->begin_content && cmark_isdigit(c) == 1;
106     }
107 
108     // If adding the character went beyond width, look for an
109     // earlier place where the line could be broken:
110     if (renderer->width > 0 && renderer->column > renderer->width &&
111         !renderer->begin_line && renderer->last_breakable > 0) {
112 
113       // copy from last_breakable to remainder
114       unsigned char *src = renderer->buffer->ptr +
115                            renderer->last_breakable + 1;
116       bufsize_t remainder_len = renderer->buffer->size -
117                                 renderer->last_breakable - 1;
118       unsigned char *remainder =
119           (unsigned char *)renderer->mem->realloc(NULL, remainder_len);
120       memcpy(remainder, src, remainder_len);
121       // truncate at last_breakable
122       cmark_strbuf_truncate(renderer->buffer, renderer->last_breakable);
123       // add newline, prefix, and remainder
124       cmark_strbuf_putc(renderer->buffer, '\n');
125       cmark_strbuf_put(renderer->buffer, renderer->prefix->ptr,
126                        renderer->prefix->size);
127       cmark_strbuf_put(renderer->buffer, remainder, remainder_len);
128       renderer->column = renderer->prefix->size + remainder_len;
129       renderer->mem->free(remainder);
130       renderer->last_breakable = 0;
131       renderer->begin_line = false;
132       renderer->begin_content = false;
133     }
134 
135     i += len;
136   }
137 }
138 
139 // Assumes no newlines, assumes ascii content:
140 void cmark_render_ascii(cmark_renderer *renderer, const char *s) {
141   int origsize = renderer->buffer->size;
142   cmark_strbuf_puts(renderer->buffer, s);
143   renderer->column += renderer->buffer->size - origsize;
144 }
145 
146 void cmark_render_code_point(cmark_renderer *renderer, uint32_t c) {
147   cmark_utf8proc_encode_char(c, renderer->buffer);
148   renderer->column += 1;
149 }
150 
151 char *cmark_render(cmark_node *root, int options, int width,
152                    void (*outc)(cmark_renderer *, cmark_escaping, int32_t,
153                                 unsigned char),
154                    int (*render_node)(cmark_renderer *renderer,
155                                       cmark_node *node,
156                                       cmark_event_type ev_type, int options)) {
157   cmark_mem *mem = root->mem;
158   cmark_strbuf pref = CMARK_BUF_INIT(mem);
159   cmark_strbuf buf = CMARK_BUF_INIT(mem);
160   cmark_node *cur;
161   cmark_event_type ev_type;
162   char *result;
163   cmark_iter *iter = cmark_iter_new(root);
164 
165   cmark_renderer renderer = {options,
166 	                     mem,   &buf, &pref, 0,           width,
167                              0,     0,    true,  true,        false,
168                              false, outc, S_cr,  S_blankline, S_out};
169 
170   while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
171     cur = cmark_iter_get_node(iter);
172     if (!render_node(&renderer, cur, ev_type, options)) {
173       // a false value causes us to skip processing
174       // the node's contents.  this is used for
175       // autolinks.
176       cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT);
177     }
178   }
179 
180   // ensure final newline
181   if (renderer.buffer->size == 0 || renderer.buffer->ptr[renderer.buffer->size - 1] != '\n') {
182     cmark_strbuf_putc(renderer.buffer, '\n');
183   }
184 
185   result = (char *)cmark_strbuf_detach(renderer.buffer);
186 
187   cmark_iter_free(iter);
188   cmark_strbuf_free(renderer.prefix);
189   cmark_strbuf_free(renderer.buffer);
190 
191   return result;
192 }