Skip to content

Commit 3489555

Browse files
parkeraQuietMisdreavus
authored andcommitted
Add inline directive syntax
rdar://71571971
1 parent 57bee4e commit 3489555

File tree

14 files changed

+222
-18
lines changed

14 files changed

+222
-18
lines changed

extensions/table.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
439439
} else if (node->type == CMARK_NODE_TABLE_CELL) {
440440
return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
441441
child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
442-
child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
442+
child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE || child_type == CMARK_NODE_ATTRIBUTE ||
443443
child_type == CMARK_NODE_STRIKETHROUGH ||
444444
child_type == CMARK_NODE_HTML_INLINE ||
445445
child_type == CMARK_NODE_FOOTNOTE_REFERENCE;

man/man3/cmark-gfm.3

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ typedef enum {
5454
CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009,
5555
CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a,
5656
CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
57+
CMARK_NODE_ATTRIBUTE = CMARK_NODE_TYPE_INLINE | 0x000c,
5758
} cmark_node_type;
5859
.RE
5960
\f[]

src/cmark.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include "buffer.h"
99

1010
cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION;
11-
cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_FOOTNOTE_REFERENCE;
11+
cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_ATTRIBUTE;
1212

1313
int cmark_version() { return CMARK_GFM_VERSION; }
1414

src/commonmark.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,16 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
474474
}
475475
break;
476476

477+
case CMARK_NODE_ATTRIBUTE:
478+
if (entering) {
479+
LIT("^[");
480+
} else {
481+
LIT("](");
482+
OUT(cmark_node_get_attributes(node), false, LITERAL);
483+
LIT(")");
484+
}
485+
break;
486+
477487
case CMARK_NODE_FOOTNOTE_REFERENCE:
478488
if (entering) {
479489
LIT("[^");

src/html.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,19 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
389389
}
390390
break;
391391

392+
case CMARK_NODE_ATTRIBUTE:
393+
// TODO: Output span, attributes potentially controlling class/id here. For now just output the main string.
394+
/*
395+
if (entering) {
396+
cmark_strbuf_puts(html, "<span __attributes=\"");
397+
cmark_strbuf_put(html, node->as.attribute.attributes.data, node->as.attribute.attributes.len);
398+
cmark_strbuf_puts(html, "\">");
399+
} else {
400+
cmark_strbuf_puts(html, "</span>");
401+
}
402+
*/
403+
break;
404+
392405
case CMARK_NODE_FOOTNOTE_DEFINITION:
393406
if (entering) {
394407
if (renderer->footnote_ix == 0) {

src/include/cmark-gfm-extension_api.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,10 @@ void cmark_inline_parser_set_offset(cmark_inline_parser *parser, int offset);
635635
CMARK_GFM_EXPORT
636636
struct cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser);
637637

638-
/** Returns 1 if the inline parser is currently in a bracket; pass 1 for 'image'
639-
* if you want to know about an image-type bracket, 0 for link-type. */
638+
/** Returns 1 if the inline parser is currently in a bracket; pass 2 for attribute,
639+
* 1 for 'image' if you want to know about an image-type bracket, 0 for link-type. */
640640
CMARK_GFM_EXPORT
641-
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image);
641+
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int type);
642642

643643
/** Remove the last n characters from the last child of the given node.
644644
* This only works where all n characters are in the single last child, and the last

src/include/cmark-gfm.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ typedef enum {
6666
CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009,
6767
CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a,
6868
CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
69+
CMARK_NODE_ATTRIBUTE = CMARK_NODE_TYPE_INLINE | 0x000c,
6970
} cmark_node_type;
7071

7172
extern cmark_node_type CMARK_NODE_LAST_BLOCK;
@@ -453,6 +454,17 @@ CMARK_GFM_EXPORT const char *cmark_node_get_title(cmark_node *node);
453454
*/
454455
CMARK_GFM_EXPORT int cmark_node_set_title(cmark_node *node, const char *title);
455456

457+
/** Returns the attributes of an attribute 'node', or an empty string
458+
if no attributes are set. Returns NULL if called on a node that is
459+
not an attribute.
460+
*/
461+
CMARK_GFM_EXPORT const char *cmark_node_get_attributes(cmark_node *node);
462+
463+
/** Sets the attributes of an attribute 'node'. Returns 1 on success,
464+
* 0 on failure.
465+
*/
466+
CMARK_GFM_EXPORT int cmark_node_set_attributes(cmark_node *node, const char *attributes);
467+
456468
/** Returns the literal "on enter" text for a custom 'node', or
457469
an empty string if no on_enter is set. Returns NULL if called
458470
on a non-custom node.

src/include/node.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ typedef struct {
4343
cmark_chunk title;
4444
} cmark_link;
4545

46+
typedef struct {
47+
cmark_chunk attributes;
48+
} cmark_attribute;
49+
4650
typedef struct {
4751
cmark_chunk on_enter;
4852
cmark_chunk on_exit;
@@ -83,6 +87,7 @@ struct cmark_node {
8387
cmark_code code;
8488
cmark_heading heading;
8589
cmark_link link;
90+
cmark_attribute attribute;
8691
cmark_custom custom;
8792
int html_block_type;
8893
void *opaque;

src/inlines.c

Lines changed: 127 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,18 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
3333

3434
#define MAXBACKTICKS 80
3535

36+
typedef enum {
37+
LINK,
38+
IMAGE,
39+
ATTRIBUTE
40+
} bracket_type;
41+
3642
typedef struct bracket {
3743
struct bracket *previous;
3844
struct delimiter *previous_delimiter;
3945
cmark_node *inl_text;
4046
bufsize_t position;
41-
bool image;
47+
bracket_type type;
4248
bool active;
4349
bool bracket_after;
4450
} bracket;
@@ -513,12 +519,12 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
513519
subj->last_delim = delim;
514520
}
515521

516-
static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
522+
static void push_bracket(subject *subj, bracket_type type, cmark_node *inl_text) {
517523
bracket *b = (bracket *)subj->mem->calloc(1, sizeof(bracket));
518524
if (subj->last_bracket != NULL) {
519525
subj->last_bracket->bracket_after = true;
520526
}
521-
b->image = image;
527+
b->type = type;
522528
b->active = true;
523529
b->inl_text = inl_text;
524530
b->previous = subj->last_bracket;
@@ -1032,7 +1038,91 @@ static bufsize_t manual_scan_link_url(cmark_chunk *input, bufsize_t offset,
10321038
return i - offset;
10331039
}
10341040

1035-
// Return a link, an image, or a literal close bracket.
1041+
static bufsize_t manual_scan_attribute_attributes(cmark_chunk *input, bufsize_t offset,
1042+
cmark_chunk *output) {
1043+
bufsize_t i = offset;
1044+
size_t nb_p = 0;
1045+
1046+
while (i < input->len) {
1047+
if (input->data[i] == '\\' &&
1048+
i + 1 < input->len &&
1049+
cmark_ispunct(input->data[i+1]))
1050+
i += 2;
1051+
else if (input->data[i] == '(') {
1052+
++nb_p;
1053+
++i;
1054+
if (nb_p > 32)
1055+
return -1;
1056+
} else if (input->data[i] == ')') {
1057+
if (nb_p == 0)
1058+
break;
1059+
--nb_p;
1060+
++i;
1061+
} else {
1062+
++i;
1063+
}
1064+
}
1065+
1066+
if (i >= input->len)
1067+
return -1;
1068+
1069+
{
1070+
cmark_chunk result = {input->data + offset, i - offset, 0};
1071+
*output = result;
1072+
}
1073+
return i - offset;
1074+
}
1075+
1076+
static cmark_node *handle_close_bracket_attribute(cmark_parser *parser, subject *subj, bracket *opener) {
1077+
bufsize_t startattributes, endattributes;
1078+
cmark_chunk attributes;
1079+
bufsize_t n;
1080+
cmark_node *inl;
1081+
cmark_chunk raw_label;
1082+
int found_label;
1083+
cmark_node *tmp, *tmpnext;
1084+
1085+
// ^name[content](attributes)
1086+
// TODO: support name. we will not even enter this with a name because we fail the match first
1087+
1088+
startattributes = subj->pos + 1;
1089+
1090+
if (peek_char(subj) == '(' &&
1091+
((n = manual_scan_attribute_attributes(&subj->input, subj->pos + 1,
1092+
&attributes)) > -1)) {
1093+
1094+
endattributes = subj->pos + 1 + n;
1095+
1096+
if (peek_at(subj, endattributes) == ')') {
1097+
subj->pos = endattributes + 1;
1098+
attributes = cmark_chunk_dup(&subj->input, startattributes, endattributes - startattributes);
1099+
}
1100+
}
1101+
1102+
inl = make_simple(subj->mem, CMARK_NODE_ATTRIBUTE);
1103+
inl->as.attribute.attributes = attributes;
1104+
inl->start_line = inl->end_line = subj->line;
1105+
inl->start_column = opener->inl_text->start_column;
1106+
inl->end_column = subj->pos + subj->column_offset + subj->block_offset;
1107+
cmark_node_insert_before(opener->inl_text, inl);
1108+
// Add content text:
1109+
tmp = opener->inl_text->next;
1110+
while (tmp) {
1111+
tmpnext = tmp->next;
1112+
cmark_node_append_child(inl, tmp);
1113+
tmp = tmpnext;
1114+
}
1115+
1116+
// Free the bracket ^[:
1117+
cmark_node_free(opener->inl_text);
1118+
1119+
process_emphasis(parser, subj, opener->previous_delimiter);
1120+
pop_bracket(subj);
1121+
1122+
return NULL;
1123+
}
1124+
1125+
// Return a link, an image, an attribute, or a literal close bracket.
10361126
static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
10371127
bufsize_t initial_pos, after_link_text_pos;
10381128
bufsize_t endurl, starttitle, endtitle, endall;
@@ -1050,7 +1140,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
10501140
advance(subj); // advance past ]
10511141
initial_pos = subj->pos;
10521142

1053-
// get last [ or ![
1143+
// get last [ or ![ or ^[
10541144
opener = subj->last_bracket;
10551145

10561146
if (opener == NULL) {
@@ -1063,9 +1153,13 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
10631153
return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
10641154
}
10651155

1156+
if (opener->type == ATTRIBUTE) {
1157+
return handle_close_bracket_attribute(parser, subj, opener);
1158+
}
1159+
10661160
// If we got here, we matched a potential link/image text.
10671161
// Now we check to see if it's a link/image.
1068-
is_image = opener->image;
1162+
is_image = opener->type == IMAGE;
10691163

10701164
after_link_text_pos = subj->pos;
10711165

@@ -1188,7 +1282,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
11881282
if (!is_image) {
11891283
opener = subj->last_bracket;
11901284
while (opener != NULL) {
1191-
if (!opener->image) {
1285+
if (opener->type == LINK) {
11921286
if (!opener->active) {
11931287
break;
11941288
} else {
@@ -1341,7 +1435,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
13411435
case '[':
13421436
advance(subj);
13431437
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("["));
1344-
push_bracket(subj, false, new_inl);
1438+
push_bracket(subj, LINK, new_inl);
13451439
break;
13461440
case ']':
13471441
new_inl = handle_close_bracket(parser, subj);
@@ -1351,11 +1445,22 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
13511445
if (peek_char(subj) == '[' && peek_char_n(subj, 1) != '^') {
13521446
advance(subj);
13531447
new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("!["));
1354-
push_bracket(subj, true, new_inl);
1448+
push_bracket(subj, IMAGE, new_inl);
13551449
} else {
13561450
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("!"));
13571451
}
13581452
break;
1453+
case '^':
1454+
advance(subj);
1455+
// TODO: Support a name between ^ and [
1456+
if (peek_char(subj) == '[') {
1457+
advance(subj);
1458+
new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("^["));
1459+
push_bracket(subj, ATTRIBUTE, new_inl);
1460+
} else {
1461+
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("^"));
1462+
}
1463+
break;
13591464
default:
13601465
new_inl = try_extensions(parser, parent, c, subj);
13611466
if (new_inl != NULL)
@@ -1604,10 +1709,19 @@ cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
16041709
return &parser->input;
16051710
}
16061711

1607-
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image) {
1608-
for (bracket *b = parser->last_bracket; b; b = b->previous)
1609-
if (b->active && b->image == (image != 0))
1610-
return 1;
1712+
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int type) {
1713+
for (bracket *b = parser->last_bracket; b; b = b->previous) {
1714+
if (b->active) {
1715+
switch (type) {
1716+
case 0:
1717+
return b->type == LINK;
1718+
case 1:
1719+
return b->type == IMAGE;
1720+
case 2:
1721+
return b->type == ATTRIBUTE;
1722+
}
1723+
}
1724+
}
16111725
return 0;
16121726
}
16131727

src/latex.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
446446

447447
case CMARK_NODE_FOOTNOTE_DEFINITION:
448448
case CMARK_NODE_FOOTNOTE_REFERENCE:
449+
case CMARK_NODE_ATTRIBUTE:
449450
// TODO
450451
break;
451452

0 commit comments

Comments
 (0)