static void remove_trailing_blank_lines(strbuf *ln) { int i; for (i = ln->size - 1; i >= 0; --i) { unsigned char c = ln->ptr[i]; if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break; } if (i < 0) { strbuf_clear(ln); return; } i = strbuf_strchr(ln, '\n', i); if (i >= 0) strbuf_truncate(ln, i); }
static void finalize(cmark_doc_parser *parser, cmark_node* b, int line_number) { int firstlinelen; int pos; cmark_node* item; cmark_node* subitem; if (!b->open) return; // don't do anything if the cmark_node is already closed b->open = false; if (line_number > b->start_line) { b->end_line = line_number - 1; } else { b->end_line = line_number; } switch (b->type) { case NODE_PARAGRAPH: pos = 0; while (strbuf_at(&b->string_content, 0) == '[' && (pos = parse_reference_inline(&b->string_content, parser->refmap))) { strbuf_drop(&b->string_content, pos); } if (is_blank(&b->string_content, 0)) { b->type = NODE_REFERENCE_DEF; } break; case NODE_INDENTED_CODE: remove_trailing_blank_lines(&b->string_content); strbuf_putc(&b->string_content, '\n'); break; case NODE_FENCED_CODE: // first line of contents becomes info firstlinelen = strbuf_strchr(&b->string_content, '\n', 0); strbuf_init(&b->as.code.info, 0); houdini_unescape_html_f( &b->as.code.info, b->string_content.ptr, firstlinelen ); strbuf_drop(&b->string_content, firstlinelen + 1); strbuf_trim(&b->as.code.info); strbuf_unescape(&b->as.code.info); break; case NODE_LIST: // determine tight/loose status b->as.list.tight = true; // tight by default item = b->first_child; while (item) { // check for non-final non-empty list item ending with blank line: if (item->last_line_blank && item->next) { b->as.list.tight = false; break; } // recurse into children of list item, to see if there are // spaces between them: subitem = item->first_child; while (subitem) { if (ends_with_blank_line(subitem) && (item->next || subitem->next)) { b->as.list.tight = false; break; } subitem = subitem->next; } if (!(b->as.list.tight)) { break; } item = item->next; } break; default: break; } }
// Convert a node_block list to HTML. Returns 0 on success, and sets result. static void blocks_to_html(strbuf *html, node_block *b) { struct ListData *data; render_stack* rstack = NULL; bool visit_children = false; bool tight = false; while(b != NULL) { visit_children = false; switch(b->tag) { case BLOCK_DOCUMENT: rstack = push_block(rstack, b->next, "", false, false); visit_children = true; break; case BLOCK_PARAGRAPH: if (tight) { inlines_to_html(html, b->inline_content); } else { cr(html); strbuf_puts(html, "<p>"); inlines_to_html(html, b->inline_content); strbuf_puts(html, "</p>\n"); } break; case BLOCK_BQUOTE: cr(html); strbuf_puts(html, "<blockquote>\n"); rstack = push_block(rstack, b->next, "</blockquote>\n", tight, false); tight = false; visit_children = true; break; case BLOCK_LIST_ITEM: cr(html); strbuf_puts(html, "<li>"); rstack = push_block(rstack, b->next, "</li>\n", tight, true); visit_children = true; break; case BLOCK_LIST: // make sure a list starts at the beginning of the line: cr(html); data = &(b->as.list); if (data->start > 1) { strbuf_printf(html, "<%s start=\"%d\">\n", data->list_type == bullet ? "ul" : "ol", data->start); } else { strbuf_puts(html, data->list_type == bullet ? "<ul>\n" : "<ol>\n"); } rstack = push_block(rstack, b->next, data->list_type == bullet ? "\n</ul>\n" : "\n</ol>\n", tight, false); tight = data->tight; visit_children = true; break; case BLOCK_ATX_HEADER: case BLOCK_SETEXT_HEADER: cr(html); strbuf_printf(html, "<h%d>", b->as.header.level); inlines_to_html(html, b->inline_content); strbuf_printf(html, "</h%d>\n", b->as.header.level); break; case BLOCK_INDENTED_CODE: case BLOCK_FENCED_CODE: cr(html); strbuf_puts(html, "<pre><code"); if (b->tag == BLOCK_FENCED_CODE) { strbuf *info = &b->as.code.info; if (strbuf_len(info) > 0) { int first_tag = strbuf_strchr(info, ' ', 0); if (first_tag < 0) first_tag = strbuf_len(info); strbuf_puts(html, " class=\"language-"); escape_html(html, info->ptr, first_tag); strbuf_putc(html, '"'); } } strbuf_putc(html, '>'); escape_html(html, b->string_content.ptr, b->string_content.size); strbuf_puts(html, "</code></pre>\n"); break; case BLOCK_HTML: strbuf_put(html, b->string_content.ptr, b->string_content.size); break; case BLOCK_HRULE: strbuf_puts(html, "<hr />\n"); break; case BLOCK_REFERENCE_DEF: break; default: assert(false); } if (visit_children) { b = b->children; } else { b = b->next; } while (b == NULL && rstack != NULL) { strbuf_puts(html, rstack->literal); if (rstack->trim) { strbuf_rtrim(html); } tight = rstack->tight; b = rstack->next_sibling.block; rstack = pop_render_stack(rstack); } } free_render_stack(rstack); }