This reverts commit 9e643720ec.
This commit is contained in:
John MacFarlane 2017-01-03 22:10:33 -08:00
Родитель 5a3f747222
Коммит fb6356e3aa
12 изменённых файлов: 96 добавлений и 2125 удалений

Просмотреть файл

@ -18,7 +18,6 @@ set(HEADERS
houdini.h
cmark_ctype.h
render.h
source_map.h
)
set(LIBRARY_SOURCES
cmark.c
@ -41,7 +40,6 @@ set(LIBRARY_SOURCES
houdini_html_e.c
houdini_html_u.c
cmark_ctype.c
source_map.c
${HEADERS}
)

Просмотреть файл

@ -28,10 +28,6 @@
#define MIN(x, y) ((x < y) ? x : y)
#endif
#ifndef MAX
#define MAX(x, y) ((x > y) ? x : y)
#endif
#define peek_at(i, n) (i)->data[n]
static bool S_last_line_blank(const cmark_node *node) {
@ -97,7 +93,6 @@ cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem) {
parser->root = document;
parser->current = document;
parser->line_number = 0;
parser->line_offset = 0;
parser->offset = 0;
parser->column = 0;
parser->first_nonspace = 0;
@ -108,7 +103,6 @@ cmark_parser *cmark_parser_new_with_mem(int options, cmark_mem *mem) {
parser->last_line_length = 0;
parser->options = options;
parser->last_buffer_ended_with_cr = false;
parser->source_map = source_map_new(mem);
return parser;
}
@ -122,7 +116,6 @@ void cmark_parser_free(cmark_parser *parser) {
cmark_mem *mem = parser->mem;
cmark_strbuf_free(&parser->curline);
cmark_strbuf_free(&parser->linebuf);
source_map_free(parser->source_map);
cmark_reference_map_free(parser->refmap);
mem->free(parser);
}
@ -262,13 +255,10 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
switch (S_type(b)) {
case CMARK_NODE_PARAGRAPH:
source_map_start_cursor(parser->source_map, parser->last_paragraph_extent);
while (cmark_strbuf_at(node_content, 0) == '[' &&
(pos = cmark_parse_reference_inline(parser->mem, node_content,
parser->refmap, parser->root,
parser->source_map))) {
parser->last_paragraph_extent = parser->source_map->cursor;
source_map_start_cursor(parser->source_map, parser->last_paragraph_extent);
parser->refmap))) {
cmark_strbuf_drop(node_content, pos);
}
if (is_blank(node_content, 0)) {
@ -276,6 +266,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
cmark_node_free(b);
}
break;
case CMARK_NODE_CODE_BLOCK:
if (!b->as.code.fenced) { // indented code
remove_trailing_blank_lines(node_content);
@ -370,32 +361,21 @@ static cmark_node *add_child(cmark_parser *parser, cmark_node *parent,
// Walk through node and all children, recursively, parsing
// string content into inline content where appropriate.
static void process_inlines(cmark_parser *parser) {
cmark_iter *iter = cmark_iter_new(parser->root);
static void process_inlines(cmark_mem *mem, cmark_node *root,
cmark_reference_map *refmap, int options) {
cmark_iter *iter = cmark_iter_new(root);
cmark_node *cur;
cmark_event_type ev_type;
cmark_source_extent *cur_extent = parser->source_map->head;
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
cur = cmark_iter_get_node(iter);
if (ev_type == CMARK_EVENT_ENTER) {
if (contains_inlines(S_type(cur))) {
while (cur_extent && cur_extent->node != cur) {
cur_extent = source_map_stitch_extent(parser->source_map, cur_extent, parser->root, parser->line_offset)->next;
}
assert(cur_extent);
source_map_start_cursor(parser->source_map, cur_extent);
cmark_parse_inlines(parser->mem, cur, parser->refmap, parser->options, parser->source_map, parser->line_offset);
cmark_parse_inlines(mem, cur, refmap, options);
}
}
}
while (cur_extent) {
cur_extent = source_map_stitch_extent(parser->source_map, cur_extent, parser->root, parser->line_offset)->next;
}
cmark_iter_free(iter);
}
@ -502,10 +482,7 @@ static cmark_node *finalize_document(cmark_parser *parser) {
}
finalize(parser, parser->root);
process_inlines(parser);
assert(source_map_check(parser->source_map, parser->line_offset));
process_inlines(parser->mem, parser->root, parser->refmap, parser->options);
return parser->root;
}
@ -547,7 +524,6 @@ void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len) {
static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
size_t len, bool eof) {
const unsigned char *end = buffer + len;
const unsigned char *skipped;
static const uint8_t repl[] = {239, 191, 189};
if (parser->last_buffer_ended_with_cr && *buffer == '\n') {
@ -558,7 +534,6 @@ static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
while (buffer < end) {
const unsigned char *eol;
bufsize_t chunk_len;
bufsize_t linebuf_size = 0;
bool process = false;
for (eol = buffer; eol < end; ++eol) {
if (S_is_line_end_char(*eol)) {
@ -576,7 +551,6 @@ static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
chunk_len = (eol - buffer);
if (process) {
if (parser->linebuf.size > 0) {
linebuf_size = cmark_strbuf_len(&parser->linebuf);
cmark_strbuf_put(&parser->linebuf, buffer, chunk_len);
S_process_line(parser, parser->linebuf.ptr, parser->linebuf.size);
cmark_strbuf_clear(&parser->linebuf);
@ -595,8 +569,6 @@ static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
}
buffer += chunk_len;
skipped = buffer;
if (buffer < end) {
if (*buffer == '\0') {
// skip over NULL
@ -612,11 +584,6 @@ static void S_parser_feed(cmark_parser *parser, const unsigned char *buffer,
buffer++;
}
}
chunk_len += buffer - skipped;
chunk_len += linebuf_size;
if (process)
parser->line_offset += chunk_len;
}
}
@ -676,13 +643,11 @@ static void S_find_first_nonspace(cmark_parser *parser, cmark_chunk *input) {
// indicates a number of columns; otherwise, a number of bytes.
// If advancing a certain number of columns partially consumes
// a tab character, parser->partially_consumed_tab is set to true.
static void S_advance_offset(cmark_parser *parser, cmark_node *container, cmark_extent_type type,
cmark_chunk *input, bufsize_t count, bool columns) {
static void S_advance_offset(cmark_parser *parser, cmark_chunk *input,
bufsize_t count, bool columns) {
char c;
int chars_to_tab;
int chars_to_advance;
int initial_pos = parser->offset + parser->line_offset;
while (count > 0 && (c = peek_at(input, parser->offset))) {
if (c == '\t') {
chars_to_tab = TAB_STOP - (parser->column % TAB_STOP);
@ -705,8 +670,6 @@ static void S_advance_offset(cmark_parser *parser, cmark_node *container, cmark_
count -= 1;
}
}
source_map_append_extent(parser->source_map, initial_pos, parser->offset + parser->line_offset, container, type);
}
static bool S_last_child_is_open(cmark_node *container) {
@ -714,7 +677,7 @@ static bool S_last_child_is_open(cmark_node *container) {
(container->last_child->flags & CMARK_NODE__OPEN);
}
static bool parse_block_quote_prefix(cmark_parser *parser, cmark_chunk *input, cmark_node *container) {
static bool parse_block_quote_prefix(cmark_parser *parser, cmark_chunk *input) {
bool res = false;
bufsize_t matched = 0;
@ -722,10 +685,10 @@ static bool parse_block_quote_prefix(cmark_parser *parser, cmark_chunk *input, c
parser->indent <= 3 && peek_at(input, parser->first_nonspace) == '>';
if (matched) {
S_advance_offset(parser, container, CMARK_EXTENT_OPENER, input, parser->indent + 1, true);
S_advance_offset(parser, input, parser->indent + 1, true);
if (S_is_space_or_tab(peek_at(input, parser->offset))) {
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, 1, true);
S_advance_offset(parser, input, 1, true);
}
res = true;
@ -739,7 +702,7 @@ static bool parse_node_item_prefix(cmark_parser *parser, cmark_chunk *input,
if (parser->indent >=
container->as.list.marker_offset + container->as.list.padding) {
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, container->as.list.marker_offset +
S_advance_offset(parser, input, container->as.list.marker_offset +
container->as.list.padding,
true);
res = true;
@ -747,7 +710,7 @@ static bool parse_node_item_prefix(cmark_parser *parser, cmark_chunk *input,
// if container->first_child is NULL, then the opening line
// of the list item was blank after the list marker; in this
// case, we are done with the list item.
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, parser->first_nonspace - parser->offset,
S_advance_offset(parser, input, parser->first_nonspace - parser->offset,
false);
res = true;
}
@ -761,10 +724,10 @@ static bool parse_code_block_prefix(cmark_parser *parser, cmark_chunk *input,
if (!container->as.code.fenced) { // indented
if (parser->indent >= CODE_INDENT) {
S_advance_offset(parser, container, CMARK_EXTENT_OPENER, input, CODE_INDENT, true);
S_advance_offset(parser, input, CODE_INDENT, true);
res = true;
} else if (parser->blank) {
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, parser->first_nonspace - parser->offset,
S_advance_offset(parser, input, parser->first_nonspace - parser->offset,
false);
res = true;
}
@ -780,14 +743,14 @@ static bool parse_code_block_prefix(cmark_parser *parser, cmark_chunk *input,
// closing fence - and since we're at
// the end of a line, we can stop processing it:
*should_continue = false;
S_advance_offset(parser, container, CMARK_EXTENT_OPENER, input, matched, false);
S_advance_offset(parser, input, matched, false);
parser->current = finalize(parser, container);
} else {
// skip opt. spaces of fence parser->offset
int i = container->as.code.fence_offset;
while (i > 0 && S_is_space_or_tab(peek_at(input, parser->offset))) {
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, 1, true);
S_advance_offset(parser, input, 1, true);
i--;
}
res = true;
@ -844,7 +807,7 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
switch (cont_type) {
case CMARK_NODE_BLOCK_QUOTE:
if (!parse_block_quote_prefix(parser, input, container))
if (!parse_block_quote_prefix(parser, input))
goto done;
break;
case CMARK_NODE_ITEM:
@ -904,26 +867,29 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
indented = parser->indent >= CODE_INDENT;
if (!indented && peek_at(input, parser->first_nonspace) == '>') {
*container = add_child(parser, *container, CMARK_NODE_BLOCK_QUOTE,
parser->first_nonspace + 1);
S_advance_offset(parser, *container, CMARK_EXTENT_OPENER, input,
bufsize_t blockquote_startpos = parser->first_nonspace;
S_advance_offset(parser, input,
parser->first_nonspace + 1 - parser->offset, false);
// optional following character
if (S_is_space_or_tab(peek_at(input, parser->offset))) {
S_advance_offset(parser, *container, CMARK_EXTENT_BLANK, input, 1, true);
S_advance_offset(parser, input, 1, true);
}
*container = add_child(parser, *container, CMARK_NODE_BLOCK_QUOTE,
blockquote_startpos + 1);
} else if (!indented && (matched = scan_atx_heading_start(
input, parser->first_nonspace))) {
bufsize_t hashpos;
int level = 0;
bufsize_t heading_startpos = parser->first_nonspace;
*container = add_child(parser, *container, CMARK_NODE_HEADING,
parser->first_nonspace + 1);
S_advance_offset(parser, *container, CMARK_EXTENT_OPENER, input,
S_advance_offset(parser, input,
parser->first_nonspace + matched - parser->offset,
false);
*container = add_child(parser, *container, CMARK_NODE_HEADING,
heading_startpos + 1);
hashpos = cmark_chunk_strchr(input, '#', parser->first_nonspace);
@ -945,7 +911,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
(*container)->as.code.fence_offset =
(int8_t)(parser->first_nonspace - parser->offset);
(*container)->as.code.info = cmark_chunk_literal("");
S_advance_offset(parser, *container, CMARK_EXTENT_OPENER, input,
S_advance_offset(parser, input,
parser->first_nonspace + matched - parser->offset,
false);
@ -965,14 +931,14 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
(*container)->type = (uint16_t)CMARK_NODE_HEADING;
(*container)->as.heading.level = lev;
(*container)->as.heading.setext = true;
S_advance_offset(parser, *container, CMARK_EXTENT_CLOSER, input, input->len - 1 - parser->offset, false);
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
} else if (!indented &&
!(cont_type == CMARK_NODE_PARAGRAPH && !all_matched) &&
(matched = scan_thematic_break(input, parser->first_nonspace))) {
// it's only now that we know the line is not part of a setext heading:
*container = add_child(parser, *container, CMARK_NODE_THEMATIC_BREAK,
parser->first_nonspace + 1);
S_advance_offset(parser, *container, CMARK_EXTENT_CONTENT, input, input->len - 1 - parser->offset, false);
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
} else if ((!indented || cont_type == CMARK_NODE_LIST) &&
(matched = parse_list_marker(
parser->mem, input, parser->first_nonspace,
@ -980,37 +946,20 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
// Note that we can have new list items starting with >= 4
// spaces indent, as long as the list container is still open.
cmark_node *list = NULL;
cmark_node *item = NULL;
cmark_source_extent *save_source_map_tail;
int i = 0;
if (cont_type != CMARK_NODE_LIST ||
!lists_match(&((*container)->as.list), data)) {
*container = add_child(parser, *container, CMARK_NODE_LIST,
parser->first_nonspace + 1);
list = *container;
}
// add the list item
*container = add_child(parser, *container, CMARK_NODE_ITEM,
parser->first_nonspace + 1);
item = *container;
// compute padding:
S_advance_offset(parser, *container, CMARK_EXTENT_OPENER, input,
S_advance_offset(parser, input,
parser->first_nonspace + matched - parser->offset,
false);
save_partially_consumed_tab = parser->partially_consumed_tab;
save_offset = parser->offset;
save_column = parser->column;
save_source_map_tail = parser->source_map->tail;
while (parser->column - save_column <= 5 &&
S_is_space_or_tab(peek_at(input, parser->offset))) {
S_advance_offset(parser, *container, CMARK_EXTENT_BLANK, input, 1, true);
S_advance_offset(parser, input, 1, true);
}
i = parser->column - save_column;
@ -1020,14 +969,9 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
data->padding = matched + 1;
parser->offset = save_offset;
parser->column = save_column;
if (save_source_map_tail) {
cmark_source_extent *tmp_extent;
for (tmp_extent = save_source_map_tail->next; tmp_extent; tmp_extent = source_map_free_extent(parser->source_map, tmp_extent));
}
parser->partially_consumed_tab = save_partially_consumed_tab;
if (i > 0) {
S_advance_offset(parser, *container, CMARK_EXTENT_BLANK, input, 1, true);
S_advance_offset(parser, input, 1, true);
}
} else {
data->padding = matched + i;
@ -1038,14 +982,22 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
data->marker_offset = parser->indent;
/* TODO: static */
if (list)
memcpy(&(list->as.list), data, sizeof(*data));
if (item)
memcpy(&(item->as.list), data, sizeof(*data));
if (cont_type != CMARK_NODE_LIST ||
!lists_match(&((*container)->as.list), data)) {
*container = add_child(parser, *container, CMARK_NODE_LIST,
parser->first_nonspace + 1);
memcpy(&((*container)->as.list), data, sizeof(*data));
}
// add the list item
*container = add_child(parser, *container, CMARK_NODE_ITEM,
parser->first_nonspace + 1);
/* TODO: static */
memcpy(&((*container)->as.list), data, sizeof(*data));
parser->mem->free(data);
} else if (indented && !maybe_lazy && !parser->blank) {
S_advance_offset(parser, input, CODE_INDENT, true);
*container = add_child(parser, *container, CMARK_NODE_CODE_BLOCK,
parser->offset + 1);
(*container)->as.code.fenced = false;
@ -1054,7 +1006,6 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
(*container)->as.code.fence_offset = 0;
(*container)->as.code.info = cmark_chunk_literal("");
S_advance_offset(parser, *container, CMARK_EXTENT_OPENER, input, CODE_INDENT, true);
} else {
break;
}
@ -1119,11 +1070,6 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
}
if (S_type(container) == CMARK_NODE_CODE_BLOCK) {
source_map_append_extent(parser->source_map,
parser->offset + parser->line_offset,
parser->line_offset + input->len,
container,
CMARK_EXTENT_CONTENT);
add_line(container, input, parser);
} else if (S_type(container) == CMARK_NODE_HTML_BLOCK) {
add_line(container, input, parser);
@ -1164,43 +1110,22 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
container = finalize(parser, container);
assert(parser->current != NULL);
}
source_map_append_extent(parser->source_map,
parser->offset + parser->line_offset,
parser->line_offset + input->len,
container,
CMARK_EXTENT_CONTENT);
} else if (parser->blank) {
source_map_append_extent(parser->source_map,
parser->line_offset + parser->offset,
parser->line_offset + input->len,
container,
CMARK_EXTENT_BLANK);
// ??? do nothing
} else if (accepts_lines(S_type(container))) {
bufsize_t initial_len = input->len;
bool chopped = false;
if (S_type(container) == CMARK_NODE_HEADING &&
container->as.heading.setext == false) {
chop_trailing_hashtags(input);
chopped = true;
}
S_advance_offset(parser, container, CMARK_EXTENT_BLANK, input, parser->first_nonspace - parser->offset,
S_advance_offset(parser, input, parser->first_nonspace - parser->offset,
false);
add_line(container, input, parser);
if (chopped)
source_map_append_extent(parser->source_map,
MAX(parser->line_offset + parser->offset, parser->line_offset + input->len),
parser->line_offset + initial_len,
container,
CMARK_EXTENT_CLOSER);
} else {
// create paragraph container for line
container = add_child(parser, container, CMARK_NODE_PARAGRAPH,
parser->first_nonspace + 1);
S_advance_offset(parser, container, CMARK_EXTENT_OPENER, input, parser->first_nonspace - parser->offset,
S_advance_offset(parser, input, parser->first_nonspace - parser->offset,
false);
parser->last_paragraph_extent = parser->source_map->tail;
add_line(container, input, parser);
}
@ -1262,7 +1187,6 @@ finished:
cmark_node *cmark_parser_finish(cmark_parser *parser) {
if (parser->linebuf.size) {
S_process_line(parser, parser->linebuf.ptr, parser->linebuf.size);
parser->line_offset += parser->linebuf.size;
cmark_strbuf_clear(&parser->linebuf);
}
@ -1281,9 +1205,3 @@ cmark_node *cmark_parser_finish(cmark_parser *parser) {
#endif
return parser->root;
}
cmark_source_extent *
cmark_parser_get_first_source_extent(cmark_parser *parser)
{
return parser->source_map->head;
}

Просмотреть файл

@ -24,11 +24,6 @@ static void *xrealloc(void *ptr, size_t size) {
return new_ptr;
}
void cmark_default_mem_free(void *ptr)
{
free(ptr);
}
cmark_mem DEFAULT_MEM_ALLOCATOR = {xcalloc, xrealloc, free};
char *cmark_markdown_to_html(const char *text, size_t len, int options) {

Просмотреть файл

@ -2,7 +2,6 @@
#define CMARK_H
#include <stdio.h>
#include <stdint.h>
#include <cmark_export.h>
#include <cmark_version.h>
@ -66,21 +65,6 @@ typedef enum {
CMARK_NODE_LAST_INLINE = CMARK_NODE_IMAGE,
} cmark_node_type;
typedef enum {
CMARK_EXTENT_NONE,
CMARK_EXTENT_OPENER,
CMARK_EXTENT_CLOSER,
CMARK_EXTENT_BLANK,
CMARK_EXTENT_CONTENT,
CMARK_EXTENT_PUNCTUATION,
CMARK_EXTENT_LINK_DESTINATION,
CMARK_EXTENT_LINK_TITLE,
CMARK_EXTENT_LINK_LABEL,
CMARK_EXTENT_REFERENCE_DESTINATION,
CMARK_EXTENT_REFERENCE_LABEL,
CMARK_EXTENT_REFERENCE_TITLE,
} cmark_extent_type;
/* For backwards compatibility: */
#define CMARK_NODE_HEADER CMARK_NODE_HEADING
#define CMARK_NODE_HRULE CMARK_NODE_THEMATIC_BREAK
@ -102,7 +86,6 @@ typedef enum {
typedef struct cmark_node cmark_node;
typedef struct cmark_parser cmark_parser;
typedef struct cmark_iter cmark_iter;
typedef struct cmark_source_extent cmark_source_extent;
/**
* ## Custom memory allocator support
@ -117,11 +100,6 @@ typedef struct cmark_mem {
void (*free)(void *);
} cmark_mem;
/** Convenience function for bindings.
*/
CMARK_EXPORT
void cmark_default_mem_free(void *ptr);
/**
* ## Creating and Destroying Nodes
*/
@ -499,11 +477,6 @@ void cmark_parser_feed(cmark_parser *parser, const char *buffer, size_t len);
CMARK_EXPORT
cmark_node *cmark_parser_finish(cmark_parser *parser);
/** Return a pointer to the first extent of the parser's source map
*/
CMARK_EXPORT
cmark_source_extent *cmark_parser_get_first_source_extent(cmark_parser *parser);
/** Parse a CommonMark document in 'buffer' of length 'len'.
* Returns a pointer to a tree of nodes. The memory allocated for
* the node tree should be released using 'cmark_node_free'
@ -519,39 +492,6 @@ cmark_node *cmark_parse_document(const char *buffer, size_t len, int options);
CMARK_EXPORT
cmark_node *cmark_parse_file(FILE *f, int options);
/**
* ## Source map API
*/
/* Return the index, in bytes, of the start of this extent */
CMARK_EXPORT
uint64_t cmark_source_extent_get_start(cmark_source_extent *extent);
/* Return the index, in bytes, of the stop of this extent. This
* index is not included in the extent*/
CMARK_EXPORT
uint64_t cmark_source_extent_get_stop(cmark_source_extent *extent);
/* Return the extent immediately following 'extent' */
CMARK_EXPORT
cmark_source_extent *cmark_source_extent_get_next(cmark_source_extent *extent);
/* Return the extent immediately preceding 'extent' */
CMARK_EXPORT
cmark_source_extent *cmark_source_extent_get_previous(cmark_source_extent *extent);
/* Return the node 'extent' maps to */
CMARK_EXPORT
cmark_node *cmark_source_extent_get_node(cmark_source_extent *extent);
/* Return the type of 'extent' */
CMARK_EXPORT
cmark_extent_type cmark_source_extent_get_type(cmark_source_extent *extent);
/* Return a string representation of 'extent' */
CMARK_EXPORT
const char *cmark_source_extent_get_type_string(cmark_source_extent *extent);
/**
* ## Rendering
*/

Просмотреть файл

@ -13,10 +13,6 @@
#include "scanners.h"
#include "inlines.h"
#ifndef MIN
#define MIN(x, y) ((x < y) ? x : y)
#endif
static const char *EMDASH = "\xE2\x80\x94";
static const char *ENDASH = "\xE2\x80\x93";
static const char *ELLIPSES = "\xE2\x80\xA6";
@ -44,7 +40,6 @@ typedef struct delimiter {
unsigned char delim_char;
bool can_open;
bool can_close;
cmark_source_extent *extent;
} delimiter;
typedef struct bracket {
@ -55,7 +50,6 @@ typedef struct bracket {
bool image;
bool active;
bool bracket_after;
cmark_source_extent *extent;
} bracket;
typedef struct {
@ -67,7 +61,6 @@ typedef struct {
bracket *last_bracket;
bufsize_t backticks[MAXBACKTICKS + 1];
bool scanned_for_backticks;
cmark_source_map *source_map;
} subject;
static CMARK_INLINE bool S_is_line_end_char(char c) {
@ -80,7 +73,7 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
static int parse_inline(subject *subj, cmark_node *parent, int options);
static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer,
cmark_reference_map *refmap, cmark_source_map *source_map);
cmark_reference_map *refmap);
static bufsize_t subject_find_special_char(subject *subj, int options);
// Create an inline with a literal string value.
@ -156,7 +149,7 @@ static CMARK_INLINE cmark_node *make_autolink(cmark_mem *mem, cmark_chunk url,
}
static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer,
cmark_reference_map *refmap, cmark_source_map *source_map) {
cmark_reference_map *refmap) {
int i;
e->mem = mem;
e->input.data = buffer->ptr;
@ -166,8 +159,6 @@ static void subject_from_buf(cmark_mem *mem, subject *e, cmark_strbuf *buffer,
e->refmap = refmap;
e->last_delim = NULL;
e->last_bracket = NULL;
e->source_map = source_map;
for (i=0; i <= MAXBACKTICKS; i++) {
e->backticks[i] = 0;
}
@ -415,7 +406,6 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
if (delim->previous != NULL) {
delim->previous->next = delim;
}
delim->extent = NULL;
subj->last_delim = delim;
}
@ -431,12 +421,11 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
b->previous_delimiter = subj->last_delim;
b->position = subj->pos;
b->bracket_after = false;
b->extent = NULL;
subj->last_bracket = b;
}
// Assumes the subject has a c at the current position.
static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart, bool *pushed) {
static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart) {
bufsize_t numdelims;
cmark_node *inl_text;
bool can_open, can_close;
@ -457,9 +446,6 @@ static cmark_node *handle_delim(subject *subj, unsigned char c, bool smart, bool
if ((can_open || can_close) && (!(c == '\'' || c == '"') || smart)) {
push_delimiter(subj, c, can_open, can_close, inl_text);
*pushed = true;
} else {
*pushed = false;
}
return inl_text;
@ -620,7 +606,6 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
bufsize_t opener_num_chars = opener_inl->as.literal.len;
bufsize_t closer_num_chars = closer_inl->as.literal.len;
cmark_node *tmp, *tmpnext, *emph;
cmark_source_extent *tmp_extent;
// calculate the actual number of characters used from this closer
if (closer_num_chars < 3 || opener_num_chars < 3) {
@ -656,28 +641,9 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
}
cmark_node_insert_after(opener_inl, emph);
tmp_extent = closer->extent->prev;
source_map_insert_extent(subj->source_map,
opener->extent,
opener->extent->stop - use_delims,
opener->extent->stop,
emph,
CMARK_EXTENT_OPENER);
opener->extent->stop -= use_delims;
source_map_insert_extent(subj->source_map,
tmp_extent,
closer->extent->start,
closer->extent->start + use_delims,
emph,
CMARK_EXTENT_CLOSER);
closer->extent->start += use_delims;
// if opener has 0 characters, remove it and its associated inline
if (opener_num_chars == 0) {
cmark_node_free(opener_inl);
source_map_free_extent(subj->source_map, opener->extent);
remove_delimiter(subj, opener);
}
@ -687,7 +653,6 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
cmark_node_free(closer_inl);
// remove closer from list
tmp_delim = closer->next;
source_map_free_extent(subj->source_map, closer->extent);
remove_delimiter(subj, closer);
closer = tmp_delim;
}
@ -910,8 +875,6 @@ static cmark_node *handle_close_bracket(subject *subj) {
int found_label;
cmark_node *tmp, *tmpnext;
bool is_image;
bool is_inline = false;
bool is_shortcut = false;
advance(subj); // advance past ]
initial_pos = subj->pos;
@ -962,7 +925,6 @@ static cmark_node *handle_close_bracket(subject *subj) {
title = cmark_clean_title(subj->mem, &title_chunk);
cmark_chunk_free(subj->mem, &url_chunk);
cmark_chunk_free(subj->mem, &title_chunk);
is_inline = true;
goto match;
} else {
@ -985,7 +947,6 @@ static cmark_node *handle_close_bracket(subject *subj) {
cmark_chunk_free(subj->mem, &raw_label);
raw_label = cmark_chunk_dup(&subj->input, opener->position,
initial_pos - opener->position - 1);
is_shortcut = true;
found_label = true;
}
@ -1015,28 +976,6 @@ match:
cmark_node_insert_before(opener->inl_text, inl);
// Add link text:
tmp = opener->inl_text->next;
assert(opener->extent);
opener->extent->node = inl;
opener->extent->type = CMARK_EXTENT_PUNCTUATION;
source_map_splice_extent(subj->source_map, initial_pos - 1, initial_pos, inl, CMARK_EXTENT_PUNCTUATION);
if (is_inline) {
source_map_splice_extent(subj->source_map, after_link_text_pos, starturl, inl, CMARK_EXTENT_PUNCTUATION);
source_map_splice_extent(subj->source_map, starturl, endurl, inl, CMARK_EXTENT_LINK_DESTINATION);
if (endtitle != starttitle) {
source_map_splice_extent(subj->source_map, endurl, starttitle, inl, CMARK_EXTENT_BLANK);
source_map_splice_extent(subj->source_map, starttitle, endtitle, inl, CMARK_EXTENT_LINK_TITLE);
source_map_splice_extent(subj->source_map, endtitle, subj->pos, inl, CMARK_EXTENT_BLANK);
} else {
source_map_splice_extent(subj->source_map, endurl, subj->pos, inl, CMARK_EXTENT_BLANK);
}
} else if (!is_shortcut) {
source_map_splice_extent(subj->source_map, initial_pos, initial_pos + 1, inl, CMARK_EXTENT_PUNCTUATION);
source_map_splice_extent(subj->source_map, initial_pos + 1, subj->pos - 1, inl, CMARK_EXTENT_LINK_LABEL);
source_map_splice_extent(subj->source_map, subj->pos - 1, subj->pos, inl, CMARK_EXTENT_PUNCTUATION);
}
while (tmp) {
tmpnext = tmp->next;
cmark_node_append_child(inl, tmp);
@ -1140,10 +1079,6 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
cmark_chunk contents;
unsigned char c;
bufsize_t endpos;
bufsize_t startpos = subj->pos;
bool add_extent_to_last_bracket = false;
bool add_extent_to_last_delimiter = false;
c = peek_char(subj);
if (c == 0) {
return 0;
@ -1169,7 +1104,7 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
case '_':
case '\'':
case '"':
new_inl = handle_delim(subj, c, (options & CMARK_OPT_SMART) != 0, &add_extent_to_last_delimiter);
new_inl = handle_delim(subj, c, (options & CMARK_OPT_SMART) != 0);
break;
case '-':
new_inl = handle_hyphen(subj, (options & CMARK_OPT_SMART) != 0);
@ -1181,7 +1116,6 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
advance(subj);
new_inl = make_str(subj->mem, cmark_chunk_literal("["));
push_bracket(subj, false, new_inl);
add_extent_to_last_bracket = true;
break;
case ']':
new_inl = handle_close_bracket(subj);
@ -1192,7 +1126,6 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
advance(subj);
new_inl = make_str(subj->mem, cmark_chunk_literal("!["));
push_bracket(subj, true, new_inl);
add_extent_to_last_bracket = true;
} else {
new_inl = make_str(subj->mem, cmark_chunk_literal("!"));
}
@ -1209,17 +1142,7 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
new_inl = make_str(subj->mem, contents);
}
if (new_inl != NULL) {
cmark_source_extent *extent;
extent = source_map_splice_extent(subj->source_map, startpos, subj->pos, new_inl, CMARK_EXTENT_CONTENT);
if (add_extent_to_last_bracket)
subj->last_bracket->extent = extent;
else if (add_extent_to_last_delimiter)
subj->last_delim->extent = extent;
cmark_node_append_child(parent, new_inl);
}
@ -1228,11 +1151,9 @@ static int parse_inline(subject *subj, cmark_node *parent, int options) {
// Parse inlines from parent's string_content, adding as children of parent.
extern void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent,
cmark_reference_map *refmap, int options,
cmark_source_map *source_map, uint64_t total_length) {
cmark_reference_map *refmap, int options) {
subject subj;
subject_from_buf(mem, &subj, &parent->content, refmap, source_map);
bufsize_t initial_len = subj.input.len;
subject_from_buf(mem, &subj, &parent->content, refmap);
cmark_chunk_rtrim(&subj.input);
while (!is_eof(&subj) && parse_inline(&subj, parent, options))
@ -1246,13 +1167,6 @@ extern void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent,
while (subj.last_bracket) {
pop_bracket(&subj);
}
source_map_insert_extent(source_map,
source_map->cursor,
source_map->cursor->stop,
MIN(source_map->cursor->stop + initial_len - subj.input.len, total_length),
parent,
CMARK_EXTENT_BLANK);
}
// Parse zero or more space characters, including at most one newline.
@ -1268,30 +1182,22 @@ static void spnl(subject *subj) {
// Return 0 if no reference found, otherwise position of subject
// after reference is parsed.
bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input,
cmark_reference_map *refmap,
cmark_node *root,
cmark_source_map *source_map) {
cmark_reference_map *refmap) {
subject subj;
cmark_node *container = source_map->cursor->node;
cmark_source_extent *tmp_extent = source_map->cursor;
cmark_chunk lab;
cmark_chunk url;
cmark_chunk title;
bufsize_t matchlen = 0;
bufsize_t starttitle, endtitle;
bufsize_t endlabel;
bufsize_t starturl, endurl;
bufsize_t beforetitle;
subject_from_buf(mem, &subj, input, NULL, source_map);
subject_from_buf(mem, &subj, input, NULL);
// parse label:
if (!link_label(&subj, &lab) || lab.len == 0)
return 0;
endlabel = subj.pos - 1;
// colon:
if (peek_char(&subj) == ':') {
advance(&subj);
@ -1301,7 +1207,6 @@ bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input,
// parse link url:
spnl(&subj);
starturl = subj.pos;
matchlen = manual_scan_link_url(&subj.input, subj.pos);
if (matchlen > 0) {
url = cmark_chunk_dup(&subj.input, subj.pos, matchlen);
@ -1311,29 +1216,22 @@ bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input,
}
// parse optional link_title
endurl = subj.pos;
beforetitle = subj.pos;
spnl(&subj);
starttitle = subj.pos;
matchlen = scan_link_title(&subj.input, subj.pos);
if (matchlen) {
title = cmark_chunk_dup(&subj.input, subj.pos, matchlen);
subj.pos += matchlen;
} else {
subj.pos = endurl;
starttitle = endurl;
endtitle = endurl;
subj.pos = beforetitle;
title = cmark_chunk_literal("");
}
endtitle = subj.pos;
// parse final spaces and newline:
skip_spaces(&subj);
if (!skip_line_end(&subj)) {
if (matchlen) { // try rewinding before title
subj.pos = endurl;
starttitle = endurl;
endtitle = endurl;
subj.pos = beforetitle;
skip_spaces(&subj);
if (!skip_line_end(&subj)) {
return 0;
@ -1344,22 +1242,5 @@ bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input,
}
// insert reference into refmap
cmark_reference_create(refmap, &lab, &url, &title);
// Mark the extents of the reference
source_map_splice_extent(source_map, 0, 1, root, CMARK_EXTENT_PUNCTUATION);
source_map_splice_extent(source_map, 1, endlabel, root, CMARK_EXTENT_REFERENCE_LABEL);
source_map_splice_extent(source_map, endlabel, endlabel + 2, root, CMARK_EXTENT_PUNCTUATION);
source_map_splice_extent(source_map, endlabel + 2, starturl, root, CMARK_EXTENT_BLANK);
source_map_splice_extent(source_map, starturl, endurl, root, CMARK_EXTENT_REFERENCE_DESTINATION);
source_map_splice_extent(source_map, endurl, starttitle, root, CMARK_EXTENT_BLANK);
source_map_splice_extent(source_map, starttitle, endtitle, root, CMARK_EXTENT_REFERENCE_TITLE);
source_map_splice_extent(source_map, endtitle, subj.pos, root, CMARK_EXTENT_BLANK);
while (tmp_extent != source_map->cursor) {
if (tmp_extent->node == container)
tmp_extent->node = root;
tmp_extent = tmp_extent->next;
}
return subj.pos;
}

Просмотреть файл

@ -1,10 +1,6 @@
#ifndef CMARK_INLINES_H
#define CMARK_INLINES_H
#include "chunk.h"
#include "references.h"
#include "source_map.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -13,13 +9,10 @@ cmark_chunk cmark_clean_url(cmark_mem *mem, cmark_chunk *url);
cmark_chunk cmark_clean_title(cmark_mem *mem, cmark_chunk *title);
void cmark_parse_inlines(cmark_mem *mem, cmark_node *parent,
cmark_reference_map *refmap, int options,
cmark_source_map *source_map, uint64_t total_length);
cmark_reference_map *refmap, int options);
bufsize_t cmark_parse_reference_inline(cmark_mem *mem, cmark_strbuf *input,
cmark_reference_map *refmap,
cmark_node *root,
cmark_source_map *source_map);
cmark_reference_map *refmap);
#ifdef __cplusplus
}

Просмотреть файл

@ -5,7 +5,6 @@
#include "node.h"
#include "buffer.h"
#include "memory.h"
#include "source_map.h"
#ifdef __cplusplus
extern "C" {
@ -28,12 +27,9 @@ struct cmark_parser {
bool partially_consumed_tab;
cmark_strbuf curline;
bufsize_t last_line_length;
bufsize_t line_offset;
cmark_strbuf linebuf;
int options;
bool last_buffer_ended_with_cr;
cmark_source_map *source_map;
cmark_source_extent *last_paragraph_extent;
};
#ifdef __cplusplus

Просмотреть файл

@ -1,293 +0,0 @@
#include <assert.h>
#include "source_map.h"
cmark_source_map *
source_map_new(cmark_mem *mem)
{
cmark_source_map *res = (cmark_source_map *) mem->calloc(1, sizeof(cmark_source_map));
res->mem = mem;
return res;
}
void
source_map_free(cmark_source_map *self)
{
cmark_source_extent *tmp;
for (tmp = self->head; tmp; tmp = source_map_free_extent(self, tmp));
self->mem->free(self);
}
cmark_source_extent *
source_map_append_extent(cmark_source_map *self, uint64_t start, uint64_t stop, cmark_node *node, cmark_extent_type type)
{
assert (start <= stop);
assert (!self->tail || self->tail->stop <= start);
cmark_source_extent *res = (cmark_source_extent *) self->mem->calloc(1, sizeof(cmark_source_extent));
res->start = start;
res->stop = stop;
res->node = node;
res->type = type;
res->next = NULL;
res->prev = self->tail;
if (!self->head)
self->head = res;
else
self->tail->next = res;
self->tail = res;
return res;
}
cmark_source_extent *
source_map_insert_extent(cmark_source_map *self, cmark_source_extent *previous,
uint64_t start, uint64_t stop, cmark_node *node, cmark_extent_type type)
{
if (start == stop)
return previous;
cmark_source_extent *extent = (cmark_source_extent *) self->mem->calloc(1, sizeof(cmark_source_extent));
extent->start = start;
extent->stop = stop;
extent->node = node;
extent->type = type;
extent->next = previous->next;
extent->prev = previous;
previous->next = extent;
if (extent->next)
extent->next->prev = extent;
else
self->tail = extent;
return extent;
}
cmark_source_extent *
source_map_free_extent(cmark_source_map *self, cmark_source_extent *extent)
{
cmark_source_extent *next = extent->next;
if (extent->prev)
extent->prev->next = next;
if (extent->next)
extent->next->prev = extent->prev;
if (extent == self->tail)
self->tail = extent->prev;
if (extent == self->head)
self->head = extent->next;
if (extent == self->cursor) {
self->cursor = extent->prev;
}
if (extent == self->next_cursor) {
self->next_cursor = extent->next;
}
self->mem->free(extent);
return next;
}
cmark_source_extent *
source_map_stitch_extent(cmark_source_map *self, cmark_source_extent *extent,
cmark_node *node, uint64_t total_length)
{
cmark_source_extent *next_extent = extent->next;
cmark_source_extent *res;
while (next_extent && extent->start == extent->stop) {
extent = source_map_free_extent(self, extent);
extent = next_extent;
next_extent = extent->next;
}
if (next_extent) {
res = source_map_insert_extent(self,
extent,
extent->stop,
extent->next->start,
node,
CMARK_EXTENT_BLANK);
} else {
res = source_map_insert_extent(self,
extent,
extent->stop,
total_length,
node,
CMARK_EXTENT_BLANK);
}
if (extent->start == extent->stop)
source_map_free_extent(self, extent);
return res;
}
cmark_source_extent *
source_map_splice_extent(cmark_source_map *self, uint64_t start, uint64_t stop,
cmark_node *node, cmark_extent_type type)
{
if (!self->next_cursor) {
self->cursor = source_map_insert_extent(self,
self->cursor,
start + self->cursor_offset,
stop + self->cursor_offset, node, type);
return self->cursor;
} else if (start + self->cursor_offset < self->next_cursor->start &&
stop + self->cursor_offset <= self->next_cursor->start) {
self->cursor = source_map_insert_extent(self,
self->cursor,
start + self->cursor_offset,
stop + self->cursor_offset, node, type);
return self->cursor;
} else if (start + self->cursor_offset < self->next_cursor->start) {
uint64_t new_start = self->next_cursor->start - self->cursor_offset;
self->cursor = source_map_insert_extent(self,
self->cursor,
start + self->cursor_offset,
self->next_cursor->start,
node, type);
if (new_start == stop)
return self->cursor;
start = new_start;
}
while (self->next_cursor && start + self->cursor_offset >= self->next_cursor->start) {
self->cursor_offset += self->next_cursor->stop - self->next_cursor->start;
self->cursor = self->cursor->next;
self->next_cursor = self->cursor->next;
}
return source_map_splice_extent(self, start, stop, node, type);
}
bool
source_map_start_cursor(cmark_source_map *self, cmark_source_extent *cursor)
{
self->cursor = cursor ? cursor : self->head;
if (!self->cursor)
return false;
self->next_cursor = self->cursor->next;
self->cursor_offset = self->cursor->stop;
return true;
}
void
source_map_pretty_print(cmark_source_map *self) {
cmark_source_extent *tmp;
for (tmp = self->head; tmp; tmp = tmp->next) {
printf ("%lu:%lu - %s, %s (%p)\n", tmp->start, tmp->stop,
cmark_node_get_type_string(tmp->node),
cmark_source_extent_get_type_string(tmp),
(void *) tmp->node);
}
}
bool
source_map_check(cmark_source_map *self, uint64_t total_length)
{
uint64_t last_stop = 0;
cmark_source_extent *tmp;
for (tmp = self->head; tmp; tmp = tmp->next) {
if (tmp->start != last_stop) {
return false;
} if (tmp->start == tmp->stop)
return false;
last_stop = tmp->stop;
}
if (last_stop != total_length)
return false;
return true;
}
uint64_t
cmark_source_extent_get_start(cmark_source_extent *extent)
{
return extent->start;
}
uint64_t
cmark_source_extent_get_stop(cmark_source_extent *extent)
{
return extent->stop;
}
cmark_node *
cmark_source_extent_get_node(cmark_source_extent *extent)
{
return extent->node;
}
cmark_source_extent *
cmark_source_extent_get_next(cmark_source_extent *extent)
{
return extent->next;
}
cmark_source_extent *
cmark_source_extent_get_previous(cmark_source_extent *extent)
{
return extent->prev;
}
cmark_extent_type
cmark_source_extent_get_type(cmark_source_extent *extent)
{
return extent->type;
}
const char *
cmark_source_extent_get_type_string(cmark_source_extent *extent)
{
switch (extent->type) {
case CMARK_EXTENT_NONE:
return "unknown";
case CMARK_EXTENT_OPENER:
return "opener";
case CMARK_EXTENT_CLOSER:
return "closer";
case CMARK_EXTENT_BLANK:
return "blank";
case CMARK_EXTENT_CONTENT:
return "content";
case CMARK_EXTENT_PUNCTUATION:
return "punctuation";
case CMARK_EXTENT_LINK_DESTINATION:
return "link_destination";
case CMARK_EXTENT_LINK_TITLE:
return "link_title";
case CMARK_EXTENT_LINK_LABEL:
return "link_label";
case CMARK_EXTENT_REFERENCE_DESTINATION:
return "reference_destination";
case CMARK_EXTENT_REFERENCE_LABEL:
return "reference_label";
case CMARK_EXTENT_REFERENCE_TITLE:
return "reference_title";
}
return "unknown";
}

Просмотреть файл

@ -1,66 +0,0 @@
#ifndef CMARK_SOURCE_MAP_H
#define CMARK_SOURCE_MAP_H
#include "cmark.h"
#include "config.h"
typedef struct _cmark_source_map
{
cmark_source_extent *head;
cmark_source_extent *tail;
cmark_source_extent *cursor;
cmark_source_extent *next_cursor;
uint64_t cursor_offset;
cmark_mem *mem;
} cmark_source_map;
struct cmark_source_extent
{
uint64_t start;
uint64_t stop;
struct cmark_source_extent *next;
struct cmark_source_extent *prev;
cmark_node *node;
cmark_extent_type type;
};
cmark_source_map * source_map_new (cmark_mem *mem);
void source_map_free (cmark_source_map *self);
bool source_map_check (cmark_source_map *self,
uint64_t total_length);
void source_map_pretty_print (cmark_source_map *self);
cmark_source_extent * source_map_append_extent(cmark_source_map *self,
uint64_t start,
uint64_t stop,
cmark_node *node,
cmark_extent_type type);
cmark_source_extent * source_map_insert_extent(cmark_source_map *self,
cmark_source_extent *previous,
uint64_t start,
uint64_t stop,
cmark_node *node,
cmark_extent_type type);
cmark_source_extent * source_map_free_extent (cmark_source_map *self,
cmark_source_extent *extent);
cmark_source_extent * source_map_stitch_extent(cmark_source_map *self,
cmark_source_extent *extent,
cmark_node *node,
uint64_t total_length);
cmark_source_extent * source_map_splice_extent(cmark_source_map *self,
uint64_t start,
uint64_t stop,
cmark_node *node,
cmark_extent_type type);
bool source_map_start_cursor (cmark_source_map *self,
cmark_source_extent *cursor);
#endif

Просмотреть файл

@ -73,20 +73,3 @@ ELSE(PYTHONINTERP_FOUND)
ENDIF(PYTHONINTERP_FOUND)
if (PYTHON_BINDING_TESTS)
find_package(PythonInterp 3 REQUIRED)
else(PYTHON_BINDING_TESTS)
find_package(PythonInterp 3)
endif(PYTHON_BINDING_TESTS)
IF (PYTHONINTERP_FOUND)
add_test(python3_bindings
${PYTHON_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}/test_cmark.py"
"${CMAKE_CURRENT_BINARY_DIR}/../src"
)
ELSE(PYTHONINTERP_FOUND)
message("\n*** A python 3 interpreter is required to run the python binding tests.\n")
add_test(skipping_python_binding_tests
echo "Skipping python binding tests, because no python 3 interpreter is available.")
ENDIF(PYTHONINTERP_FOUND)

Просмотреть файл

@ -1,490 +0,0 @@
# -*- coding: utf8 -*-
from __future__ import unicode_literals
import sys
import os
import unittest
import argparse
here = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.join(here, os.pardir, 'wrappers'))
from wrapper import *
class TestHighLevel(unittest.TestCase):
def test_markdown_to_html(self):
self.assertEqual(markdown_to_html('foo'), '<p>foo</p>\n')
def test_parse_document(self):
doc = parse_document('foo')
self.assertEqual(type(doc), Document)
class TestParser(unittest.TestCase):
def test_lifecycle(self):
parser = Parser()
del parser
def test_feed(self):
parser = Parser()
parser.feed('')
def test_finish(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
def test_source_map(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
source_map = parser.get_source_map()
extents = [e for e in source_map]
self.assertEqual(len(extents), 1)
self.assertEqual(extents[0].type, ExtentType.CONTENT)
self.assertEqual(extents[0].start, 0)
self.assertEqual(extents[0].stop, 3)
def test_render_html(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
res = doc.to_html()
self.assertEqual(res, '<p></p>\n')
def test_render_xml(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
res = doc.to_xml()
self.assertEqual(
res,
'<?xml version="1.0" encoding="UTF-8"?>\n'
'<!DOCTYPE document SYSTEM "CommonMark.dtd">\n'
'<document xmlns="http://commonmark.org/xml/1.0">\n'
' <paragraph>\n'
' <text></text>\n'
' </paragraph>\n'
'</document>\n')
def test_render_commonmark(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
res = doc.to_commonmark()
self.assertEqual(res, '\n')
def test_render_man(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
res = doc.to_man()
self.assertEqual(
res,
'.PP\n'
'\[oq]\n')
def test_render_latex(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
res = doc.to_latex()
self.assertEqual(res, '`\n')
class TestNode(unittest.TestCase):
def test_type(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
self.assertEqual(type(doc), Document)
def test_first_child(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
child1 = doc.first_child
child2 = doc.first_child
self.assertEqual(child1, child2)
self.assertEqual((child1 != child2), False)
def test_last_child(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
child1 = doc.first_child
child2 = doc.last_child
self.assertEqual(child1, child2)
self.assertEqual((child1 != child2), False)
def test_next(self):
parser = Parser()
parser.feed('foo *bar*')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
text = para.first_child
self.assertEqual(type(text), Text)
emph = text.next
self.assertEqual(type(emph), Emph)
self.assertEqual(para.next, None)
def test_previous(self):
parser = Parser()
parser.feed('foo *bar*')
doc = parser.finish()
para = doc.first_child
text = para.first_child
emph = text.next
self.assertEqual(emph.previous, text)
self.assertEqual(para.previous, None)
def test_children(self):
parser = Parser()
parser.feed('foo *bar*')
doc = parser.finish()
para = doc.first_child
children = [c for c in para]
self.assertEqual(len(children), 2)
self.assertEqual(type(children[0]), Text)
self.assertEqual(type(children[1]), Emph)
def test_new(self):
with self.assertRaises(NotImplementedError):
n = Node()
def test_unlink(self):
parser = Parser()
parser.feed('foo *bar*')
doc = parser.finish()
para = doc.first_child
para.unlink()
self.assertEqual(doc.to_html(), '')
def test_append_child(self):
parser = Parser()
parser.feed('')
doc = parser.finish()
doc.append_child(Paragraph())
self.assertEqual(doc.to_html(), '<p></p>\n')
with self.assertRaises(LibcmarkError):
doc.append_child(Text(literal='foo'))
def test_prepend_child(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
doc.prepend_child(Paragraph())
self.assertEqual(doc.to_html(), '<p></p>\n<p>foo</p>\n')
with self.assertRaises(LibcmarkError):
doc.prepend_child(Text(literal='foo'))
def test_insert_before(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
para = doc.first_child
para.insert_before(Paragraph())
self.assertEqual(doc.to_html(), '<p></p>\n<p>foo</p>\n')
with self.assertRaises(LibcmarkError):
para.insert_before(Text(literal='foo'))
def test_insert_after(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
para = doc.first_child
para.insert_after(Paragraph())
self.assertEqual(doc.to_html(), '<p>foo</p>\n<p></p>\n')
with self.assertRaises(LibcmarkError):
para.insert_after(Text(literal='foo'))
def test_consolidate_text_nodes(self):
parser = Parser()
parser.feed('foo **bar*')
doc = parser.finish()
self.assertEqual(len([c for c in doc.first_child]), 3)
doc.consolidate_text_nodes()
self.assertEqual(len([c for c in doc.first_child]), 2)
class TestLiteral(unittest.TestCase):
def test_text(self):
parser = Parser()
parser.feed('foo')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
text = para.first_child
self.assertEqual(type(text), Text)
self.assertEqual(text.literal, 'foo')
text.literal = 'bar'
self.assertEqual(text.to_html(), 'bar')
class TestDocument(unittest.TestCase):
def test_new(self):
doc = Document()
self.assertEqual(doc.to_html(),
'')
class TestBlockQuote(unittest.TestCase):
def test_new(self):
bq = BlockQuote()
self.assertEqual(bq.to_html(),
'<blockquote>\n</blockquote>\n')
class TestList(unittest.TestCase):
def test_new(self):
list_ = List()
self.assertEqual(list_.to_html(),
'<ul>\n</ul>\n')
def test_type(self):
parser = Parser()
parser.feed('* foo')
doc = parser.finish()
list_ = doc.first_child
self.assertEqual(type(list_), List)
self.assertEqual(list_.type, ListType.BULLET)
list_.type = ListType.ORDERED
self.assertEqual(doc.to_html(),
'<ol>\n'
'<li>foo</li>\n'
'</ol>\n')
def test_start(self):
parser = Parser()
parser.feed('2. foo')
doc = parser.finish()
list_ = doc.first_child
self.assertEqual(type(list_), List)
self.assertEqual(list_.start, 2)
list_.start = 1
self.assertEqual(doc.to_commonmark(),
'1. foo\n')
with self.assertRaises(LibcmarkError):
list_.start = -1
list_.type = ListType.BULLET
def test_delim(self):
parser = Parser()
parser.feed('1. foo')
doc = parser.finish()
list_ = doc.first_child
self.assertEqual(type(list_), List)
self.assertEqual(list_.delim, '.')
list_.delim = ')'
self.assertEqual(doc.to_commonmark(),
'1) foo\n')
def test_tight(self):
parser = Parser()
parser.feed('* foo\n'
'\n'
'* bar\n')
doc = parser.finish()
list_ = doc.first_child
self.assertEqual(type(list_), List)
self.assertEqual(list_.tight, False)
self.assertEqual(doc.to_commonmark(),
' - foo\n'
'\n'
' - bar\n')
list_.tight = True
self.assertEqual(doc.to_commonmark(),
' - foo\n'
' - bar\n')
with self.assertRaises(LibcmarkError):
list_.tight = 42
class TestItem(unittest.TestCase):
def test_new(self):
item = Item()
self.assertEqual(item.to_html(),
'<li></li>\n')
class TestCodeBlock(unittest.TestCase):
def test_new(self):
cb = CodeBlock(literal='foo', fence_info='python')
self.assertEqual(cb.to_html(),
'<pre><code class="language-python">foo</code></pre>\n')
def test_fence_info(self):
parser = Parser()
parser.feed('``` markdown\n'
'hello\n'
'```\n')
doc = parser.finish()
code_block = doc.first_child
self.assertEqual(type(code_block), CodeBlock)
self.assertEqual(code_block.fence_info, 'markdown')
code_block.fence_info = 'python'
self.assertEqual(doc.to_commonmark(),
'``` python\n'
'hello\n'
'```\n')
class TestHtmlBlock(unittest.TestCase):
def test_new(self):
hb = HtmlBlock(literal='<p>foo</p>')
self.assertEqual(hb.to_html(),
'<p>foo</p>\n')
class TestCustomBlock(unittest.TestCase):
def test_new(self):
cb = CustomBlock()
self.assertEqual(cb.to_html(),
'')
class TestParagraph(unittest.TestCase):
def test_new(self):
para = Paragraph()
self.assertEqual(para.to_html(),
'<p></p>\n')
class TestHeading(unittest.TestCase):
def test_new(self):
heading = Heading(level=3)
self.assertEqual(heading.to_html(),
'<h3></h3>\n')
def test_level(self):
parser = Parser()
parser.feed('# foo')
doc = parser.finish()
heading = doc.first_child
self.assertEqual(type(heading), Heading)
self.assertEqual(heading.level, 1)
heading.level = 3
self.assertEqual(heading.level, 3)
self.assertEqual(doc.to_html(),
'<h3>foo</h3>\n')
with self.assertRaises(LibcmarkError):
heading.level = 10
class TestThematicBreak(unittest.TestCase):
def test_new(self):
tb = ThematicBreak()
self.assertEqual(tb.to_html(),
'<hr />\n')
class TestText(unittest.TestCase):
def test_new(self):
text = Text(literal='foo')
self.assertEqual(text.to_html(),
'foo')
class TestSoftBreak(unittest.TestCase):
def test_new(self):
sb = SoftBreak()
self.assertEqual(sb.to_html(), '\n')
self.assertEqual(sb.to_html(options=Parser.OPT_HARDBREAKS),
'<br />\n')
self.assertEqual(sb.to_html(options=Parser.OPT_NOBREAKS),
' ')
class TestLineBreak(unittest.TestCase):
def test_new(self):
lb = LineBreak()
self.assertEqual(lb.to_html(), '<br />\n')
class TestCode(unittest.TestCase):
def test_new(self):
code = Code(literal='bar')
self.assertEqual(code.to_html(), '<code>bar</code>')
class TestHtmlInline(unittest.TestCase):
def test_new(self):
hi = HtmlInline(literal='<b>baz</b>')
self.assertEqual(hi.to_html(), '<b>baz</b>')
class TestCustomInline(unittest.TestCase):
def test_new(self):
ci = CustomInline()
self.assertEqual(ci.to_html(),
'')
class TestEmph(unittest.TestCase):
def test_new(self):
emph = Emph()
self.assertEqual(emph.to_html(),
'<em></em>')
class TestStrong(unittest.TestCase):
def test_new(self):
strong = Strong()
self.assertEqual(strong.to_html(),
'<strong></strong>')
class TestLink(unittest.TestCase):
def test_new(self):
link = Link(url='http://foo.com', title='foo')
self.assertEqual(link.to_html(),
'<a href="http://foo.com" title="foo"></a>')
def test_url(self):
parser = Parser()
parser.feed('<http://foo.com>\n')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
link = para.first_child
self.assertEqual(type(link), Link)
self.assertEqual(link.url, 'http://foo.com')
link.url = 'http://bar.net'
# Yeah that's crappy behaviour but not our problem here
self.assertEqual(doc.to_commonmark(),
'[http://foo.com](http://bar.net)\n')
def test_title(self):
parser = Parser()
parser.feed('<http://foo.com>\n')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
link = para.first_child
self.assertEqual(type(link), Link)
self.assertEqual(link.title, '')
link.title = 'foo'
self.assertEqual(doc.to_html(),
'<p><a href="http://foo.com" title="foo">http://foo.com</a></p>\n')
class TestImage(unittest.TestCase):
def test_new(self):
image = Image(url='http://foo.com', title='foo')
self.assertEqual(image.to_html(),
'<img src="http://foo.com" alt="" title="foo" />')
def test_url(self):
parser = Parser()
parser.feed('![image](image.com)\n')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
link = para.first_child
self.assertEqual(type(link), Image)
self.assertEqual(link.url, 'image.com')
link.url = 'http://bar.net'
self.assertEqual(doc.to_commonmark(),
'![image](http://bar.net)\n')
def test_title(self):
parser = Parser()
parser.feed('![image](image.com "ze image")\n')
doc = parser.finish()
para = doc.first_child
self.assertEqual(type(para), Paragraph)
image = para.first_child
self.assertEqual(type(image), Image)
self.assertEqual(image.title, 'ze image')
image.title = 'foo'
self.assertEqual(doc.to_html(),
'<p><img src="image.com" alt="image" title="foo" /></p>\n')
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('libdir')
args = parser.parse_known_args()
conf.set_library_path(args[0].libdir)
unittest.main(argv=[sys.argv[0]] + args[1])

944
wrappers/wrapper.py Normal file → Executable file
Просмотреть файл

@ -1,921 +1,37 @@
from __future__ import unicode_literals
#!/usr/bin/env python
from ctypes import *
# Example for using the shared library from python
# Will work with either python 2 or python 3
# Requires cmark library to be installed
from ctypes import CDLL, c_char_p, c_long
import sys
import platform
c_object_p = POINTER(c_void_p)
sysname = platform.system()
if sysname == 'Windows':
libc = CDLL('msvcrt.dll')
if sysname == 'Darwin':
libname = "libcmark.dylib"
elif sysname == 'Windows':
libname = "cmark.dll"
else:
libc = CDLL('libc.so.6')
if sys.version_info[0] > 2:
def bytes_and_length(text):
if type(text) == str:
text = text.encode("utf8")
return text, len(text)
else:
def bytes_and_length(text):
if type(text) == unicode:
text = text.encode("utf8")
return text, len(text)
def unicode_from_char_p(res, fn, args):
ret = res.decode("utf8")
return ret
class owned_char_p(c_void_p):
def __del__(self):
conf.lib.cmark_default_mem_free(self.value)
def unicode_from_owned_char_p(res, fn, args):
ret = cast(res, c_char_p).value.decode("utf8")
return ret
def boolean_from_result(res, fn, args):
return bool(res)
def delim_from_int(res, fn, args):
if res == 0:
return ''
elif res == 1:
return '.'
elif res == 2:
return ')'
class BaseEnumeration(object):
def __init__(self, value):
if value >= len(self.__class__._kinds):
self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1)
if self.__class__._kinds[value] is not None:
raise ValueError('{0} value {1} already loaded'.format(
str(self.__class__), value))
self.value = value
self.__class__._kinds[value] = self
self.__class__._name_map = None
def from_param(self):
return self.value
@classmethod
def from_id(cls, id, fn, args):
if id >= len(cls._kinds) or cls._kinds[id] is None:
raise ValueError('Unknown template argument kind %d' % id)
return cls._kinds[id]
@property
def name(self):
"""Get the enumeration name of this cursor kind."""
if self._name_map is None:
self._name_map = {}
for key, value in self.__class__.__dict__.items():
if isinstance(value, self.__class__):
self._name_map[value] = key
return str(self._name_map[self])
def __repr__(self):
return '%s.%s' % (self.__class__.__name__, self.name,)
class Parser(object):
OPT_DEFAULT = 0
OPT_SOURCEPOS = 1 << 1
OPT_HARDBREAKS = 1 << 2
OPT_SAFE = 1 << 3
OPT_NOBREAKS = 1 << 4
OPT_NORMALIZE = 1 << 8
OPT_VALIDATE_UTF8 = 1 << 9
OPT_SMART = 1 << 10
def __init__(self, options=0):
self._parser = conf.lib.cmark_parser_new(options)
def __del__(self):
conf.lib.cmark_parser_free(self._parser)
def feed(self, text):
conf.lib.cmark_parser_feed(self._parser, *bytes_and_length(text))
def finish(self):
return conf.lib.cmark_parser_finish(self._parser)
def get_source_map(self):
return conf.lib.cmark_parser_get_first_source_extent(self._parser)
class LibcmarkError(Exception):
def __init__(self, message):
self.m = message
def __str__(self):
return self.m
class NodeType(BaseEnumeration):
_kinds = []
_name_map = None
NodeType.NONE = NodeType(0)
NodeType.DOCUMENT = NodeType(1)
NodeType.BLOCK_QUOTE = NodeType(2)
NodeType.LIST = NodeType(3)
NodeType.ITEM = NodeType(4)
NodeType.CODE_BLOCK = NodeType(5)
NodeType.HTML_BLOCK = NodeType(6)
NodeType.CUSTOM_BLOCK = NodeType(7)
NodeType.PARAGRAPH = NodeType(8)
NodeType.HEADING = NodeType(9)
NodeType.THEMATIC_BREAK = NodeType(10)
NodeType.TEXT = NodeType(11)
NodeType.SOFTBREAK = NodeType(12)
NodeType.LINEBREAK = NodeType(13)
NodeType.CODE = NodeType(14)
NodeType.HTML_INLINE = NodeType(15)
NodeType.CUSTOM_INLINE = NodeType(16)
NodeType.EMPH = NodeType(17)
NodeType.STRONG = NodeType(18)
NodeType.LINK = NodeType(19)
NodeType.IMAGE = NodeType(20)
class ListType(BaseEnumeration):
_kinds = []
_name_map = None
ListType.BULLET = ListType(1)
ListType.ORDERED = ListType(2)
class Node(object):
__subclass_map = {}
def __init__(self):
self._owned = False
raise NotImplementedError
@staticmethod
def from_result(res, fn=None, args=None):
try:
res.contents
except ValueError:
return None
cls = Node.get_subclass_map()[conf.lib.cmark_node_get_type(res)]
ret = cls.__new__(cls)
ret._node = res
ret._owned = False
return ret
@classmethod
def get_subclass_map(cls):
if cls.__subclass_map:
return cls.__subclass_map
res = {c._node_type: c for c in cls.__subclasses__()}
for c in cls.__subclasses__():
res.update(c.get_subclass_map())
return res
def unlink(self):
conf.lib.cmark_node_unlink(self._node)
self._owned = True
def append_child(self, child):
res = conf.lib.cmark_node_append_child(self._node, child._node)
if not res:
raise LibcmarkError("Can't append child %s to node %s" % (str(child), str(self)))
child._owned = False
def prepend_child(self, child):
res = conf.lib.cmark_node_prepend_child(self._node, child._node)
if not res:
raise LibcmarkError("Can't prepend child %s to node %s" % (str(child), str(self)))
child._owned = False
def insert_before(self, sibling):
res = conf.lib.cmark_node_insert_before(self._node, sibling._node)
if not res:
raise LibcmarkError("Can't insert sibling %s before node %s" % (str(sibling), str(self)))
sibling._owned = False
def insert_after(self, sibling):
res = conf.lib.cmark_node_insert_after(self._node, sibling._node)
if not res:
raise LibcmarkError("Can't insert sibling %s after node %s" % (str(sibling), str(self)))
sibling._owned = False
def consolidate_text_nodes(self):
conf.lib.cmark_consolidate_text_nodes(self._node)
def to_html(self, options=Parser.OPT_DEFAULT):
return conf.lib.cmark_render_html(self._node, options)
def to_xml(self, options=Parser.OPT_DEFAULT):
return conf.lib.cmark_render_xml(self._node, options)
def to_commonmark(self, options=Parser.OPT_DEFAULT, width=0):
return conf.lib.cmark_render_commonmark(self._node, options, width)
def to_man(self, options=Parser.OPT_DEFAULT, width=0):
return conf.lib.cmark_render_man(self._node, options, width)
def to_latex(self, options=Parser.OPT_DEFAULT, width=0):
return conf.lib.cmark_render_latex(self._node, options, width)
@property
def first_child(self):
return conf.lib.cmark_node_first_child(self._node)
@property
def last_child(self):
return conf.lib.cmark_node_last_child(self._node)
@property
def next(self):
return conf.lib.cmark_node_next(self._node)
@property
def previous(self):
return conf.lib.cmark_node_previous(self._node)
def __eq__(self, other):
return self._node.contents.value == other._node.contents.value
def __ne__(self, other):
return self._node.contents.value != other._node.contents.value
def __del__(self):
if self._owned:
conf.lib.cmark_node_free(self._node)
def __iter__(self):
cur = self.first_child
while (cur):
yield cur
cur = cur.next
class Literal(Node):
_node_type = NodeType.NONE
@property
def literal(self):
return conf.lib.cmark_node_get_literal(self._node)
@literal.setter
def literal(self, value):
bytes_, _ = bytes_and_length(value)
if not conf.lib.cmark_node_set_literal(self._node, bytes_):
raise LibcmarkError("Invalid literal %s\n" % str(value))
class Document(Node):
_node_type = NodeType.DOCUMENT
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class BlockQuote(Node):
_node_type = NodeType.BLOCK_QUOTE
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class List(Node):
_node_type = NodeType.LIST
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
@property
def type(self):
return conf.lib.cmark_node_get_list_type(self._node)
@type.setter
def type(self, type_):
if not conf.lib.cmark_node_set_list_type(self._node, type_.value):
raise LibcmarkError("Invalid type %s" % str(type_))
@property
def delim(self):
return conf.lib.cmark_node_get_list_delim(self._node)
@delim.setter
def delim(self, value):
if value == '.':
delim_type = 1
elif value == ')':
delim_type = 2
else:
raise LibcmarkError('Invalid delim type %s' % str(value))
conf.lib.cmark_node_set_list_delim(self._node, delim_type)
@property
def start(self):
return conf.lib.cmark_node_get_list_start(self._node)
@start.setter
def start(self, value):
if not conf.lib.cmark_node_set_list_start(self._node, value):
raise LibcmarkError("Invalid list start %s\n" % str(value))
@property
def tight(self):
return conf.lib.cmark_node_get_list_tight(self._node)
@tight.setter
def tight(self, value):
if value is True:
tightness = 1
elif value is False:
tightness = 0
else:
raise LibcmarkError("Invalid list tightness %s\n" % str(value))
if not conf.lib.cmark_node_set_list_tight(self._node, tightness):
raise LibcmarkError("Invalid list tightness %s\n" % str(value))
class Item(Node):
_node_type = NodeType.ITEM
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class CodeBlock(Literal):
_node_type = NodeType.CODE_BLOCK
def __init__(self, literal='', fence_info=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.literal = literal
self.fence_info = fence_info
@property
def fence_info(self):
return conf.lib.cmark_node_get_fence_info(self._node)
@fence_info.setter
def fence_info(self, value):
bytes_, _ = bytes_and_length(value)
if not conf.lib.cmark_node_set_fence_info(self._node, bytes_):
raise LibcmarkError("Invalid fence info %s\n" % str(value))
class HtmlBlock(Literal):
_node_type = NodeType.HTML_BLOCK
def __init__(self, literal=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.literal = literal
class CustomBlock(Node):
_node_type = NodeType.CUSTOM_BLOCK
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Paragraph(Node):
_node_type = NodeType.PARAGRAPH
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Heading(Node):
_node_type = NodeType.HEADING
def __init__(self, level=1):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self.level = level
self._owned = True
@property
def level(self):
return int(conf.lib.cmark_node_get_heading_level(self._node))
@level.setter
def level(self, value):
res = conf.lib.cmark_node_set_heading_level(self._node, value)
if (res == 0):
raise LibcmarkError("Invalid heading level %s" % str(value))
class ThematicBreak(Node):
_node_type = NodeType.THEMATIC_BREAK
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Text(Literal):
_node_type = NodeType.TEXT
def __init__(self, literal=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.literal = literal
class SoftBreak(Node):
_node_type = NodeType.SOFTBREAK
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class LineBreak(Node):
_node_type = NodeType.LINEBREAK
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Code(Literal):
_node_type = NodeType.CODE
def __init__(self, literal=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.literal = literal
class HtmlInline(Literal):
_node_type = NodeType.HTML_INLINE
def __init__(self, literal=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.literal = literal
class CustomInline(Node):
_node_type = NodeType.CUSTOM_INLINE
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Emph(Node):
_node_type = NodeType.EMPH
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Strong(Node):
_node_type = NodeType.STRONG
def __init__(self):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
class Link(Node):
_node_type = NodeType.LINK
def __init__(self, url='', title=''):
self._node = conf.lib.cmark_node_new(self.__class__._node_type.value)
self._owned = True
self.url = url
self.title = title
@property
def url(self):
return conf.lib.cmark_node_get_url(self._node)
@url.setter
def url(self, value):
bytes_, _ = bytes_and_length(value)
if not conf.lib.cmark_node_set_url(self._node, bytes_):
raise LibcmarkError("Invalid url %s\n" % str(value))
@property
def title(self):
return conf.lib.cmark_node_get_title(self._node)
@title.setter
def title(self, value):
bytes_, _ = bytes_and_length(value)
if not conf.lib.cmark_node_set_title(self._node, bytes_):
raise LibcmarkError("Invalid title %s\n" % str(value))
class Image(Link):
_node_type = NodeType.IMAGE
class ExtentType(BaseEnumeration):
_kinds = []
_name_map = None
ExtentType.NONE = ExtentType(0)
ExtentType.OPENER = ExtentType(1)
ExtentType.CLOSER = ExtentType(2)
ExtentType.BLANK = ExtentType(3)
ExtentType.CONTENT = ExtentType(4)
ExtentType.PUNCTUATION = ExtentType(5)
ExtentType.LINK_DESTINATION = ExtentType(6)
ExtentType.LINK_TITLE = ExtentType(7)
ExtentType.LINK_LABEL = ExtentType(8)
ExtentType.REFERENCE_DESTINATION = ExtentType(9)
ExtentType.REFERENCE_LABEL = ExtentType(10)
ExtentType.REFERENCE_TITLE = ExtentType(11)
class Extent(object):
@staticmethod
def from_result(res, fn=None, args=None):
ret = Extent()
ret._extent = res
return ret
@property
def start(self):
return conf.lib.cmark_source_extent_get_start(self._extent)
@property
def stop(self):
return conf.lib.cmark_source_extent_get_stop(self._extent)
@property
def type(self):
return conf.lib.cmark_source_extent_get_type(self._extent)
@property
def node(self):
return conf.lib.cmark_source_extent_get_node(self._extent)
class SourceMap(object):
@staticmethod
def from_result(res, fn, args):
ret = SourceMap()
ret._root = res
return ret
def __iter__(self):
cur = self._root
while (cur):
yield Extent.from_result(cur)
cur = conf.lib.cmark_source_extent_get_next(cur)
def markdown_to_html(text, options=Parser.OPT_DEFAULT):
bytes_, length = bytes_and_length(text)
return conf.lib.cmark_markdown_to_html(bytes_, length, options)
def parse_document(text, options=Parser.OPT_DEFAULT):
bytes_, length = bytes_and_length(text)
return conf.lib.cmark_parse_document(bytes_, length, options)
functionList = [
("cmark_default_mem_free",
[c_void_p]),
("cmark_markdown_to_html",
[c_char_p, c_long, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_parse_document",
[c_char_p, c_long, c_int],
c_object_p,
Node.from_result),
("cmark_parser_new",
[c_int],
c_object_p),
("cmark_parser_free",
[c_object_p]),
("cmark_parser_feed",
[c_object_p, c_char_p, c_long]),
("cmark_parser_finish",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_parser_get_first_source_extent",
[c_object_p],
c_object_p,
SourceMap.from_result),
("cmark_source_extent_get_next",
[c_object_p],
c_object_p),
("cmark_source_extent_get_start",
[c_object_p],
c_ulonglong),
("cmark_source_extent_get_stop",
[c_object_p],
c_ulonglong),
("cmark_source_extent_get_type",
[c_object_p],
c_int,
ExtentType.from_id),
("cmark_source_extent_get_node",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_render_html",
[c_object_p, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_render_xml",
[c_object_p, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_render_commonmark",
[c_object_p, c_int, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_render_man",
[c_object_p, c_int, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_render_latex",
[c_object_p, c_int, c_int],
owned_char_p,
unicode_from_owned_char_p),
("cmark_node_new",
[c_int],
c_object_p),
("cmark_node_free",
[c_object_p]),
("cmark_node_get_type",
[c_object_p],
c_int,
NodeType.from_id),
("cmark_node_first_child",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_node_last_child",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_node_next",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_node_previous",
[c_object_p],
c_object_p,
Node.from_result),
("cmark_node_unlink",
[c_object_p]),
("cmark_node_append_child",
[c_object_p, c_object_p],
c_int,
boolean_from_result),
("cmark_node_prepend_child",
[c_object_p, c_object_p],
c_int,
boolean_from_result),
("cmark_node_insert_before",
[c_object_p, c_object_p],
c_int,
boolean_from_result),
("cmark_node_insert_after",
[c_object_p, c_object_p],
c_int,
boolean_from_result),
("cmark_consolidate_text_nodes",
[c_object_p]),
("cmark_node_get_literal",
[c_object_p],
c_char_p,
unicode_from_char_p),
("cmark_node_set_literal",
[c_object_p, c_char_p],
c_int,
boolean_from_result),
("cmark_node_get_heading_level",
[c_object_p],
c_int),
("cmark_node_set_heading_level",
[c_object_p, c_int],
c_int,
boolean_from_result),
("cmark_node_get_list_type",
[c_object_p],
c_int,
ListType.from_id),
("cmark_node_set_list_type",
[c_object_p],
c_int,
boolean_from_result),
("cmark_node_get_list_delim",
[c_object_p],
c_int,
delim_from_int),
("cmark_node_set_list_delim",
[c_object_p, c_int],
c_int),
("cmark_node_get_list_start",
[c_object_p],
c_int),
("cmark_node_set_list_start",
[c_object_p, c_int],
c_int,
boolean_from_result),
("cmark_node_get_list_tight",
[c_object_p],
c_int,
boolean_from_result),
("cmark_node_set_list_tight",
[c_object_p, c_int],
c_int,
boolean_from_result),
("cmark_node_get_fence_info",
[c_object_p],
c_char_p,
unicode_from_char_p),
("cmark_node_set_fence_info",
[c_object_p, c_char_p],
c_int,
boolean_from_result),
("cmark_node_get_url",
[c_object_p],
c_char_p,
unicode_from_char_p),
("cmark_node_set_url",
[c_object_p, c_char_p],
c_int,
boolean_from_result),
("cmark_node_get_title",
[c_object_p],
c_char_p,
unicode_from_char_p),
("cmark_node_set_title",
[c_object_p, c_char_p],
c_int,
boolean_from_result),
]
# Taken from clang.cindex
def register_function(lib, item, ignore_errors):
# A function may not exist, if these bindings are used with an older or
# incompatible version of libcmark.so.
try:
func = getattr(lib, item[0])
except AttributeError as e:
msg = str(e) + ". Please ensure that your python bindings are "\
"compatible with your libcmark version."
if ignore_errors:
return
raise LibcmarkError(msg)
if len(item) >= 2:
func.argtypes = item[1]
if len(item) >= 3:
func.restype = item[2]
if len(item) == 4:
func.errcheck = item[3]
def register_functions(lib, ignore_errors):
"""Register function prototypes with a libccmark library instance.
This must be called as part of library instantiation so Python knows how
to call out to the shared library.
"""
def register(item):
return register_function(lib, item, ignore_errors)
for f in functionList:
register(f)
class Config:
library_path = None
library_file = None
compatibility_check = True
loaded = False
lib_ = None
@staticmethod
def set_library_path(path):
"""Set the path in which to search for libcmark"""
if Config.loaded:
raise Exception("library path must be set before before using " \
"any other functionalities in libcmark.")
Config.library_path = path
@staticmethod
def set_library_file(filename):
"""Set the exact location of libcmark"""
if Config.loaded:
raise Exception("library file must be set before before using " \
"any other functionalities in libcmark.")
Config.library_file = filename
@staticmethod
def set_compatibility_check(check_status):
""" Perform compatibility check when loading libcmark
The python bindings are only tested and evaluated with the version of
libcmark they are provided with. To ensure correct behavior a (limited)
compatibility check is performed when loading the bindings. This check
will throw an exception, as soon as it fails.
In case these bindings are used with an older version of libcmark, parts
that have been stable between releases may still work. Users of the
python bindings can disable the compatibility check. This will cause
the python bindings to load, even though they are written for a newer
version of libcmark. Failures now arise if unsupported or incompatible
features are accessed. The user is required to test themselves if the
features they are using are available and compatible between different
libcmark versions.
"""
if Config.loaded:
raise Exception("compatibility_check must be set before before " \
"using any other functionalities in libcmark.")
Config.compatibility_check = check_status
@property
def lib(self):
if self.lib_:
return self.lib_
lib = self.get_cmark_library()
register_functions(lib, not Config.compatibility_check)
Config.loaded = True
self.lib_ = lib
return lib
def get_filename(self):
if Config.library_file:
return Config.library_file
import platform
name = platform.system()
if name == 'Darwin':
file = 'libcmark.dylib'
elif name == 'Windows':
file = 'cmark.dll'
else:
file = 'libcmark.so'
if Config.library_path:
file = Config.library_path + '/' + file
return file
def get_cmark_library(self):
try:
library = cdll.LoadLibrary(self.get_filename())
except OSError as e:
msg = str(e) + "(%s). To provide a path to libcmark use " \
"Config.set_library_path() or " \
"Config.set_library_file()." % self.get_filename()
raise LibcmarkError(msg)
return library
def function_exists(self, name):
try:
getattr(self.lib, name)
except AttributeError:
return False
return True
conf = Config()
__alla__ = [
'Parser',
'LibcmarkError',
'NodeType',
'ListType',
'Node',
'Document',
'BlockQuote',
'List',
'Item',
'CodeBlock',
'HtmlBlock',
'CustomBlock',
'Paragraph',
'Heading',
'ThematicBreak',
'Text',
'SoftBreak',
'LineBreak',
'Code',
'HtmlInline',
'CustomInline',
'Emph',
'Strong',
'Link',
'Image',
'ExtentType',
'Extent',
'SourceMap',
'markdown_to_html',
'parse_document',
'Config',
'conf'
]
libname = "libcmark.so"
cmark = CDLL(libname)
markdown = cmark.cmark_markdown_to_html
markdown.restype = c_char_p
markdown.argtypes = [c_char_p, c_long, c_long]
opts = 0 # defaults
def md2html(text):
if sys.version_info >= (3,0):
textbytes = text.encode('utf-8')
textlen = len(textbytes)
return markdown(textbytes, textlen, opts).decode('utf-8')
else:
textbytes = text
textlen = len(text)
return markdown(textbytes, textlen, opts)
sys.stdout.write(md2html(sys.stdin.read()))