Skip to content

Commit 4413b9c

Browse files
committed
[vnotex] support display formula
1 parent de809d9 commit 4413b9c

11 files changed

Lines changed: 17655 additions & 6932 deletions

File tree

parser_test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(TEST_TARGETS
55
block_quote_tests
66
mark_tests
77
formula_inline_tests
8+
formula_block_tests
89
code_tests)
910

1011
foreach(TARGET ${TEST_TARGETS})

parser_test/formula_block_tests.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <cmark.h>
2+
3+
#include "test_utils.h"
4+
5+
int test_formula_block_simple() {
6+
return test_xml("$$\nE=mc^2\n$$",
7+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
8+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
9+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
10+
" <formula_block xml:space=\"preserve\">E=mc^2\n</formula_block>\n"
11+
"</document>\n",
12+
CMARK_OPT_DEFAULT);
13+
}
14+
15+
int test_formula_block_multiple() {
16+
return test_xml("$$\na+b\n$$\n\n$$\nc+d\n$$",
17+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
18+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
19+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
20+
" <formula_block xml:space=\"preserve\">a+b\n</formula_block>\n"
21+
" <formula_block xml:space=\"preserve\">c+d\n</formula_block>\n"
22+
"</document>\n",
23+
CMARK_OPT_DEFAULT);
24+
}
25+
26+
int test_formula_block_with_escape() {
27+
return test_xml("$$\na\\$\\$b\n$$",
28+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
29+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
30+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
31+
" <formula_block xml:space=\"preserve\">a\\$\\$b\n</formula_block>\n"
32+
"</document>\n",
33+
CMARK_OPT_DEFAULT);
34+
}
35+
36+
int test_formula_block_not_closed() {
37+
return test_xml("$$\nformula",
38+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
39+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
40+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
41+
" <formula_block xml:space=\"preserve\">formula\n</formula_block>\n"
42+
"</document>\n",
43+
CMARK_OPT_DEFAULT);
44+
}
45+
46+
int test_formula_block_empty() {
47+
return test_xml("$$\n$$",
48+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
49+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
50+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
51+
" <formula_block xml:space=\"preserve\"></formula_block>\n"
52+
"</document>\n",
53+
CMARK_OPT_DEFAULT);
54+
}
55+
56+
int main() {
57+
CASE(test_formula_block_simple);
58+
CASE(test_formula_block_multiple);
59+
CASE(test_formula_block_with_escape);
60+
CASE(test_formula_block_not_closed);
61+
CASE(test_formula_block_empty);
62+
return 0;
63+
}

src/blocks.c

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ static inline bool can_contain(cmark_node_type parent_type,
171171
static inline bool accepts_lines(cmark_node_type block_type) {
172172
return (block_type == CMARK_NODE_PARAGRAPH ||
173173
block_type == CMARK_NODE_HEADING ||
174-
block_type == CMARK_NODE_CODE_BLOCK);
174+
block_type == CMARK_NODE_CODE_BLOCK ||
175+
block_type == CMARK_NODE_FORMULA_BLOCK);
175176
}
176177

177178
static inline bool contains_inlines(cmark_node_type block_type) {
@@ -270,6 +271,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
270271
b->end_column = parser->last_line_length;
271272
} else if (S_type(b) == CMARK_NODE_DOCUMENT ||
272273
(S_type(b) == CMARK_NODE_CODE_BLOCK && b->as.code.fenced) ||
274+
S_type(b) == CMARK_NODE_FORMULA_BLOCK ||
273275
(S_type(b) == CMARK_NODE_HEADING && b->as.heading.setext)) {
274276
b->end_line = parser->line_number;
275277
b->end_column = parser->curline.size;
@@ -330,6 +332,11 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
330332
b->data = cmark_strbuf_detach(node_content);
331333
break;
332334

335+
case CMARK_NODE_FORMULA_BLOCK:
336+
b->len = node_content->size;
337+
b->data = cmark_strbuf_detach(node_content);
338+
break;
339+
333340
case CMARK_NODE_HEADING:
334341
case CMARK_NODE_HTML_BLOCK:
335342
b->len = node_content->size;
@@ -859,6 +866,35 @@ static bool parse_code_block_prefix(cmark_parser *parser, cmark_chunk *input,
859866
return res;
860867
}
861868

869+
static bool parse_formula_block_prefix(cmark_parser *parser, cmark_chunk *input,
870+
cmark_node *container, bool *should_continue) {
871+
bool res = false;
872+
bufsize_t matched = 0;
873+
874+
if (parser->indent <= 3) {
875+
matched = scan_formula_block_end(input, parser->first_nonspace);
876+
}
877+
878+
if (matched) {
879+
// closing fence - and since we're at
880+
// the end of a line, we can stop processing it:
881+
*should_continue = false;
882+
S_advance_offset(parser, input, matched, false);
883+
parser->current = finalize(parser, container);
884+
} else {
885+
// skip opt. spaces of fence parser->offset
886+
int i = container->as.formula.fence_offset;
887+
888+
while (i > 0 && S_is_space_or_tab(peek_at(input, parser->offset))) {
889+
S_advance_offset(parser, input, 1, true);
890+
i--;
891+
}
892+
res = true;
893+
}
894+
895+
return res;
896+
}
897+
862898
static bool parse_html_block_prefix(cmark_parser *parser,
863899
cmark_node *container) {
864900
bool res = false;
@@ -924,6 +960,7 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
924960
// first blank line was processed. Certain block types accept
925961
// empty lines as content, so add them here.
926962
if (parser->current->type == CMARK_NODE_CODE_BLOCK ||
963+
parser->current->type == CMARK_NODE_FORMULA_BLOCK ||
927964
parser->current->type == CMARK_NODE_HTML_BLOCK) {
928965
add_line(input, parser);
929966
}
@@ -942,6 +979,10 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
942979
if (!parse_code_block_prefix(parser, input, container, &should_continue))
943980
goto done;
944981
break;
982+
case CMARK_NODE_FORMULA_BLOCK:
983+
if (!parse_formula_block_prefix(parser, input, container, &should_continue))
984+
goto done;
985+
break;
945986
case CMARK_NODE_HEADING:
946987
// a heading can never contain more than one line
947988
goto done;
@@ -986,6 +1027,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
9861027
int save_column;
9871028

9881029
while (cont_type != CMARK_NODE_CODE_BLOCK &&
1030+
cont_type != CMARK_NODE_FORMULA_BLOCK &&
9891031
cont_type != CMARK_NODE_HTML_BLOCK) {
9901032

9911033
S_find_first_nonspace(parser, input);
@@ -1028,18 +1070,28 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
10281070
(*container)->as.heading.internal_offset = matched;
10291071

10301072
} else if (!indented && (matched = scan_open_code_fence(
1031-
input, parser->first_nonspace))) {
1073+
input, parser->first_nonspace))) {
10321074
*container = add_child(parser, *container, CMARK_NODE_CODE_BLOCK,
1033-
parser->first_nonspace + 1);
1075+
parser->first_nonspace + 1);
10341076
(*container)->as.code.fenced = true;
10351077
(*container)->as.code.fence_char = peek_at(input, parser->first_nonspace);
10361078
(*container)->as.code.fence_length = (matched > 255) ? 255 : matched;
10371079
(*container)->as.code.fence_offset =
10381080
(int8_t)(parser->first_nonspace - parser->offset);
10391081
(*container)->as.code.info = NULL;
10401082
S_advance_offset(parser, input,
1041-
parser->first_nonspace + matched - parser->offset,
1042-
false);
1083+
parser->first_nonspace + matched - parser->offset,
1084+
false);
1085+
1086+
} else if (!indented && (matched = scan_formula_block_start(
1087+
input, parser->first_nonspace))) {
1088+
*container = add_child(parser, *container, CMARK_NODE_FORMULA_BLOCK,
1089+
parser->first_nonspace + 1);
1090+
(*container)->as.formula.fence_offset =
1091+
(int8_t)(parser->first_nonspace - parser->offset);
1092+
S_advance_offset(parser, input,
1093+
parser->first_nonspace + matched - parser->offset,
1094+
false);
10431095

10441096
} else if (!indented && ((matched = scan_html_block_start(
10451097
input, parser->first_nonspace)) ||
@@ -1175,6 +1227,7 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
11751227
const bool last_line_blank =
11761228
(parser->blank && ctype != CMARK_NODE_BLOCK_QUOTE &&
11771229
ctype != CMARK_NODE_HEADING && ctype != CMARK_NODE_THEMATIC_BREAK &&
1230+
ctype != CMARK_NODE_FORMULA_BLOCK &&
11781231
!(ctype == CMARK_NODE_CODE_BLOCK && container->as.code.fenced) &&
11791232
!(ctype == CMARK_NODE_ITEM && container->first_child == NULL &&
11801233
container->start_line == parser->line_number));
@@ -1204,7 +1257,8 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
12041257
assert(parser->current != NULL);
12051258
}
12061259

1207-
if (S_type(container) == CMARK_NODE_CODE_BLOCK) {
1260+
if (S_type(container) == CMARK_NODE_CODE_BLOCK ||
1261+
S_type(container) == CMARK_NODE_FORMULA_BLOCK) {
12081262
add_line(input, parser);
12091263
} else if (S_type(container) == CMARK_NODE_HTML_BLOCK) {
12101264
add_line(input, parser);

src/cmark.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ typedef enum {
4646
CMARK_NODE_PARAGRAPH,
4747
CMARK_NODE_HEADING,
4848
CMARK_NODE_THEMATIC_BREAK,
49+
CMARK_NODE_FORMULA_BLOCK,
4950

5051
CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
51-
CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
52+
CMARK_NODE_LAST_BLOCK = CMARK_NODE_FORMULA_BLOCK,
5253

5354
/* Inline */
5455
CMARK_NODE_TEXT,
@@ -207,11 +208,13 @@ CMARK_EXPORT cmark_node *cmark_node_last_child(cmark_node *node);
207208
* * CMARK_NODE_HTML_BLOCK
208209
* * CMARK_NODE_THEMATIC_BREAK
209210
* * CMARK_NODE_CODE_BLOCK
211+
* * CMARK_NODE_FORMULA_BLOCK
210212
* * CMARK_NODE_TEXT
211213
* * CMARK_NODE_SOFTBREAK
212214
* * CMARK_NODE_LINEBREAK
213215
* * CMARK_NODE_CODE
214216
* * CMARK_NODE_HTML_INLINE
217+
* * CMARK_NODE_FORMULA_INLINE
215218
*
216219
* Nodes must only be modified after an `EXIT` event, or an `ENTER` event for
217220
* leaf nodes.
@@ -677,6 +680,7 @@ const char *cmark_version_string(void);
677680
#define NODE_LIST CMARK_NODE_LIST
678681
#define NODE_ITEM CMARK_NODE_ITEM
679682
#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
683+
#define NODE_FORMULA_BLOCK CMARK_NODE_FORMULA_BLOCK
680684
#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
681685
#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
682686
#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH

src/iterator.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
static const int S_leaf_mask =
1010
(1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
1111
(1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
12+
(1 << CMARK_NODE_FORMULA_BLOCK) | (1 << CMARK_NODE_FORMULA_INLINE) |
1213
(1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
1314
(1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);
1415

src/node.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ bool cmark_node_is_leaf(cmark_node *node) {
2929
switch (node->type) {
3030
case CMARK_NODE_THEMATIC_BREAK: return true;
3131
case CMARK_NODE_CODE_BLOCK : return true;
32+
case CMARK_NODE_FORMULA_BLOCK : return true;
3233
case CMARK_NODE_TEXT : return true;
3334
case CMARK_NODE_SOFTBREAK : return true;
3435
case CMARK_NODE_LINEBREAK : return true;
@@ -135,6 +136,7 @@ static void S_free_nodes(cmark_node *e) {
135136
case CMARK_NODE_CODE:
136137
case CMARK_NODE_HTML_BLOCK:
137138
case CMARK_NODE_FORMULA_INLINE:
139+
case CMARK_NODE_FORMULA_BLOCK:
138140
mem->free(e->data);
139141
break;
140142
case CMARK_NODE_LINK:
@@ -225,6 +227,8 @@ const char *cmark_node_get_type_string(cmark_node *node) {
225227
return "mark";
226228
case CMARK_NODE_FORMULA_INLINE:
227229
return "formula_inline";
230+
case CMARK_NODE_FORMULA_BLOCK:
231+
return "formula_block";
228232
case CMARK_NODE_LINK:
229233
return "link";
230234
case CMARK_NODE_IMAGE:
@@ -322,6 +326,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
322326
case CMARK_NODE_CODE:
323327
case CMARK_NODE_CODE_BLOCK:
324328
case CMARK_NODE_FORMULA_INLINE:
329+
case CMARK_NODE_FORMULA_BLOCK:
325330
return node->data ? (char *)node->data : "";
326331

327332
default:
@@ -343,6 +348,7 @@ int cmark_node_set_literal(cmark_node *node, const char *content) {
343348
case CMARK_NODE_CODE:
344349
case CMARK_NODE_CODE_BLOCK:
345350
case CMARK_NODE_FORMULA_INLINE:
351+
case CMARK_NODE_FORMULA_BLOCK:
346352
node->len = cmark_set_cstr(node->mem, &node->data, content);
347353
return 1;
348354

src/node.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ typedef struct {
3030
int8_t fenced;
3131
} cmark_code;
3232

33+
typedef struct {
34+
uint8_t fence_offset;
35+
} cmark_formula;
36+
3337
typedef struct {
3438
int internal_offset;
3539
int8_t level;
@@ -82,6 +86,7 @@ struct cmark_node {
8286
cmark_heading heading;
8387
cmark_link link;
8488
cmark_custom custom;
89+
cmark_formula formula;
8590
int html_block_type;
8691
} as;
8792
};

0 commit comments

Comments
 (0)