Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions parser_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(TEST_TARGETS
block_quote_tests
mark_tests
formula_inline_tests
formula_block_tests
code_tests)

foreach(TARGET ${TEST_TARGETS})
Expand Down
74 changes: 74 additions & 0 deletions parser_test/formula_block_tests.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include <cmark.h>

#include "test_utils.h"

int test_formula_block_simple() {
return test_xml("$$\nE=mc^2\n$$",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\">E=mc^2\n</formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int test_formula_block_raw() {
return test_xml("\\begin{equa}\nE=mc^2\n\\end{equa}",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\">E=mc^2\n</formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int test_formula_block_multiple() {
return test_xml("$$\na+b\n$$\n\n$$\nc+d\n$$",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\">a+b\n</formula_block>\n"
" <formula_block xml:space=\"preserve\">c+d\n</formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int test_formula_block_with_escape() {
return test_xml("$$\na\\$\\$b\n$$",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\">a\\$\\$b\n</formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int test_formula_block_not_closed() {
return test_xml("$$\nformula",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\">formula\n</formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int test_formula_block_empty() {
return test_xml("$$\n$$",
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
" <formula_block xml:space=\"preserve\"></formula_block>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
}

int main() {
CASE(test_formula_block_simple);
CASE(test_formula_block_raw);
CASE(test_formula_block_multiple);
CASE(test_formula_block_with_escape);
CASE(test_formula_block_not_closed);
CASE(test_formula_block_empty);
return 0;
}
6 changes: 3 additions & 3 deletions parser_test/formula_inline_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ int test_formula_inline_not_closed() {
}

int test_formula_inline_empty() {
return test_xml("$$",
return test_xml("abc $$",
"<?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 xml:space=\"preserve\">$$</text>\n"
" <text xml:space=\"preserve\">abc $$</text>\n"
" </paragraph>\n"
"</document>\n",
CMARK_OPT_DEFAULT);
Expand All @@ -71,4 +71,4 @@ int main() {
CASE(test_formula_inline_not_closed);
CASE(test_formula_inline_empty);
return 0;
}
}
61 changes: 59 additions & 2 deletions src/blocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ static inline bool can_contain(cmark_node_type parent_type,
static inline bool accepts_lines(cmark_node_type block_type) {
return (block_type == CMARK_NODE_PARAGRAPH ||
block_type == CMARK_NODE_HEADING ||
block_type == CMARK_NODE_CODE_BLOCK);
block_type == CMARK_NODE_CODE_BLOCK ||
block_type == CMARK_NODE_FORMULA_BLOCK);
}

static inline bool contains_inlines(cmark_node_type block_type) {
Expand Down Expand Up @@ -270,6 +271,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
b->end_column = parser->last_line_length;
} else if (S_type(b) == CMARK_NODE_DOCUMENT ||
(S_type(b) == CMARK_NODE_CODE_BLOCK && b->as.code.fenced) ||
S_type(b) == CMARK_NODE_FORMULA_BLOCK ||
(S_type(b) == CMARK_NODE_HEADING && b->as.heading.setext)) {
b->end_line = parser->line_number;
b->end_column = parser->curline.size;
Expand Down Expand Up @@ -330,6 +332,11 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
b->data = cmark_strbuf_detach(node_content);
break;

case CMARK_NODE_FORMULA_BLOCK:
b->len = node_content->size;
b->data = cmark_strbuf_detach(node_content);
break;

case CMARK_NODE_HEADING:
case CMARK_NODE_HTML_BLOCK:
b->len = node_content->size;
Expand Down Expand Up @@ -859,6 +866,37 @@ static bool parse_code_block_prefix(cmark_parser *parser, cmark_chunk *input,
return res;
}

static bool parse_formula_block_prefix(cmark_parser *parser, cmark_chunk *input,
cmark_node *container, bool *should_continue) {
bool res = false;
bufsize_t matched = 0;

if (parser->indent <= 3) {
matched = scan_formula_block_end(input, parser->first_nonspace);
}

if (matched &&
(matched == container->as.formula.fence_length ||
matched == container->as.formula.fence_length - 2)) {
// closing fence - and since we're at
// the end of a line, we can stop processing it:
*should_continue = 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.formula.fence_offset;

while (i > 0 && S_is_space_or_tab(peek_at(input, parser->offset))) {
S_advance_offset(parser, input, 1, true);
i--;
}
res = true;
}

return res;
}

static bool parse_html_block_prefix(cmark_parser *parser,
cmark_node *container) {
bool res = false;
Expand Down Expand Up @@ -924,6 +962,7 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
// first blank line was processed. Certain block types accept
// empty lines as content, so add them here.
if (parser->current->type == CMARK_NODE_CODE_BLOCK ||
parser->current->type == CMARK_NODE_FORMULA_BLOCK ||
parser->current->type == CMARK_NODE_HTML_BLOCK) {
add_line(input, parser);
}
Expand All @@ -942,6 +981,10 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
if (!parse_code_block_prefix(parser, input, container, &should_continue))
goto done;
break;
case CMARK_NODE_FORMULA_BLOCK:
if (!parse_formula_block_prefix(parser, input, container, &should_continue))
goto done;
break;
case CMARK_NODE_HEADING:
// a heading can never contain more than one line
goto done;
Expand Down Expand Up @@ -986,6 +1029,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
int save_column;

while (cont_type != CMARK_NODE_CODE_BLOCK &&
cont_type != CMARK_NODE_FORMULA_BLOCK &&
cont_type != CMARK_NODE_HTML_BLOCK) {

S_find_first_nonspace(parser, input);
Expand Down Expand Up @@ -1041,6 +1085,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
parser->first_nonspace + matched - parser->offset,
false);

} else if (!indented && (matched = scan_formula_block_start(
input, parser->first_nonspace))) {
*container = add_child(parser, *container, CMARK_NODE_FORMULA_BLOCK,
parser->first_nonspace + 1);
(*container)->as.formula.fence_length = matched;
(*container)->as.formula.fence_offset =
(int8_t)(parser->first_nonspace - parser->offset);
S_advance_offset(parser, input,
parser->first_nonspace + matched - parser->offset,
false);

} else if (!indented && ((matched = scan_html_block_start(
input, parser->first_nonspace)) ||
(cont_type != CMARK_NODE_PARAGRAPH &&
Expand Down Expand Up @@ -1175,6 +1230,7 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
const bool last_line_blank =
(parser->blank && ctype != CMARK_NODE_BLOCK_QUOTE &&
ctype != CMARK_NODE_HEADING && ctype != CMARK_NODE_THEMATIC_BREAK &&
ctype != CMARK_NODE_FORMULA_BLOCK &&
!(ctype == CMARK_NODE_CODE_BLOCK && container->as.code.fenced) &&
!(ctype == CMARK_NODE_ITEM && container->first_child == NULL &&
container->start_line == parser->line_number));
Expand Down Expand Up @@ -1204,7 +1260,8 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
assert(parser->current != NULL);
}

if (S_type(container) == CMARK_NODE_CODE_BLOCK) {
if (S_type(container) == CMARK_NODE_CODE_BLOCK ||
S_type(container) == CMARK_NODE_FORMULA_BLOCK) {
add_line(input, parser);
} else if (S_type(container) == CMARK_NODE_HTML_BLOCK) {
add_line(input, parser);
Expand Down
6 changes: 5 additions & 1 deletion src/cmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ typedef enum {
CMARK_NODE_PARAGRAPH,
CMARK_NODE_HEADING,
CMARK_NODE_THEMATIC_BREAK,
CMARK_NODE_FORMULA_BLOCK,

CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
CMARK_NODE_LAST_BLOCK = CMARK_NODE_FORMULA_BLOCK,

/* Inline */
CMARK_NODE_TEXT,
Expand Down Expand Up @@ -207,11 +208,13 @@ CMARK_EXPORT cmark_node *cmark_node_last_child(cmark_node *node);
* * CMARK_NODE_HTML_BLOCK
* * CMARK_NODE_THEMATIC_BREAK
* * CMARK_NODE_CODE_BLOCK
* * CMARK_NODE_FORMULA_BLOCK
* * CMARK_NODE_TEXT
* * CMARK_NODE_SOFTBREAK
* * CMARK_NODE_LINEBREAK
* * CMARK_NODE_CODE
* * CMARK_NODE_HTML_INLINE
* * CMARK_NODE_FORMULA_INLINE
*
* Nodes must only be modified after an `EXIT` event, or an `ENTER` event for
* leaf nodes.
Expand Down Expand Up @@ -677,6 +680,7 @@ const char *cmark_version_string(void);
#define NODE_LIST CMARK_NODE_LIST
#define NODE_ITEM CMARK_NODE_ITEM
#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
#define NODE_FORMULA_BLOCK CMARK_NODE_FORMULA_BLOCK
#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH
Expand Down
1 change: 1 addition & 0 deletions src/iterator.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
static const int S_leaf_mask =
(1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
(1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
(1 << CMARK_NODE_FORMULA_BLOCK) | (1 << CMARK_NODE_FORMULA_INLINE) |
(1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
(1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);

Expand Down
6 changes: 6 additions & 0 deletions src/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bool cmark_node_is_leaf(cmark_node *node) {
switch (node->type) {
case CMARK_NODE_THEMATIC_BREAK: return true;
case CMARK_NODE_CODE_BLOCK : return true;
case CMARK_NODE_FORMULA_BLOCK : return true;
case CMARK_NODE_TEXT : return true;
case CMARK_NODE_SOFTBREAK : return true;
case CMARK_NODE_LINEBREAK : return true;
Expand Down Expand Up @@ -135,6 +136,7 @@ static void S_free_nodes(cmark_node *e) {
case CMARK_NODE_CODE:
case CMARK_NODE_HTML_BLOCK:
case CMARK_NODE_FORMULA_INLINE:
case CMARK_NODE_FORMULA_BLOCK:
mem->free(e->data);
break;
case CMARK_NODE_LINK:
Expand Down Expand Up @@ -225,6 +227,8 @@ const char *cmark_node_get_type_string(cmark_node *node) {
return "mark";
case CMARK_NODE_FORMULA_INLINE:
return "formula_inline";
case CMARK_NODE_FORMULA_BLOCK:
return "formula_block";
case CMARK_NODE_LINK:
return "link";
case CMARK_NODE_IMAGE:
Expand Down Expand Up @@ -322,6 +326,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
case CMARK_NODE_CODE:
case CMARK_NODE_CODE_BLOCK:
case CMARK_NODE_FORMULA_INLINE:
case CMARK_NODE_FORMULA_BLOCK:
return node->data ? (char *)node->data : "";

default:
Expand All @@ -343,6 +348,7 @@ int cmark_node_set_literal(cmark_node *node, const char *content) {
case CMARK_NODE_CODE:
case CMARK_NODE_CODE_BLOCK:
case CMARK_NODE_FORMULA_INLINE:
case CMARK_NODE_FORMULA_BLOCK:
node->len = cmark_set_cstr(node->mem, &node->data, content);
return 1;

Expand Down
6 changes: 6 additions & 0 deletions src/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ typedef struct {
int8_t fenced;
} cmark_code;

typedef struct {
uint8_t fence_length;
uint8_t fence_offset;
} cmark_formula;

typedef struct {
int internal_offset;
int8_t level;
Expand Down Expand Up @@ -82,6 +87,7 @@ struct cmark_node {
cmark_heading heading;
cmark_link link;
cmark_custom custom;
cmark_formula formula;
int html_block_type;
} as;
};
Expand Down
Loading