Skip to content

Commit

Permalink
Add inline directive syntax
Browse files Browse the repository at this point in the history
rdar://71571971
  • Loading branch information
parkera authored and QuietMisdreavus committed Jun 8, 2021
1 parent 57bee4e commit 3489555
Show file tree
Hide file tree
Showing 14 changed files with 222 additions and 18 deletions.
2 changes: 1 addition & 1 deletion extensions/table.c
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
} else if (node->type == CMARK_NODE_TABLE_CELL) {
return child_type == CMARK_NODE_TEXT || child_type == CMARK_NODE_CODE ||
child_type == CMARK_NODE_EMPH || child_type == CMARK_NODE_STRONG ||
child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE ||
child_type == CMARK_NODE_LINK || child_type == CMARK_NODE_IMAGE || child_type == CMARK_NODE_ATTRIBUTE ||
child_type == CMARK_NODE_STRIKETHROUGH ||
child_type == CMARK_NODE_HTML_INLINE ||
child_type == CMARK_NODE_FOOTNOTE_REFERENCE;
Expand Down
1 change: 1 addition & 0 deletions man/man3/cmark-gfm.3
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef enum {
CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009,
CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a,
CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
CMARK_NODE_ATTRIBUTE = CMARK_NODE_TYPE_INLINE | 0x000c,
} cmark_node_type;
.RE
\f[]
Expand Down
2 changes: 1 addition & 1 deletion src/cmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include "buffer.h"

cmark_node_type CMARK_NODE_LAST_BLOCK = CMARK_NODE_FOOTNOTE_DEFINITION;
cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_FOOTNOTE_REFERENCE;
cmark_node_type CMARK_NODE_LAST_INLINE = CMARK_NODE_ATTRIBUTE;

int cmark_version() { return CMARK_GFM_VERSION; }

Expand Down
10 changes: 10 additions & 0 deletions src/commonmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,16 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
}
break;

case CMARK_NODE_ATTRIBUTE:
if (entering) {
LIT("^[");
} else {
LIT("](");
OUT(cmark_node_get_attributes(node), false, LITERAL);
LIT(")");
}
break;

case CMARK_NODE_FOOTNOTE_REFERENCE:
if (entering) {
LIT("[^");
Expand Down
13 changes: 13 additions & 0 deletions src/html.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,19 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
}
break;

case CMARK_NODE_ATTRIBUTE:
// TODO: Output span, attributes potentially controlling class/id here. For now just output the main string.
/*
if (entering) {
cmark_strbuf_puts(html, "<span __attributes=\"");
cmark_strbuf_put(html, node->as.attribute.attributes.data, node->as.attribute.attributes.len);
cmark_strbuf_puts(html, "\">");
} else {
cmark_strbuf_puts(html, "</span>");
}
*/
break;

case CMARK_NODE_FOOTNOTE_DEFINITION:
if (entering) {
if (renderer->footnote_ix == 0) {
Expand Down
6 changes: 3 additions & 3 deletions src/include/cmark-gfm-extension_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -635,10 +635,10 @@ void cmark_inline_parser_set_offset(cmark_inline_parser *parser, int offset);
CMARK_GFM_EXPORT
struct cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser);

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

/** Remove the last n characters from the last child of the given node.
* This only works where all n characters are in the single last child, and the last
Expand Down
12 changes: 12 additions & 0 deletions src/include/cmark-gfm.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ typedef enum {
CMARK_NODE_LINK = CMARK_NODE_TYPE_INLINE | 0x0009,
CMARK_NODE_IMAGE = CMARK_NODE_TYPE_INLINE | 0x000a,
CMARK_NODE_FOOTNOTE_REFERENCE = CMARK_NODE_TYPE_INLINE | 0x000b,
CMARK_NODE_ATTRIBUTE = CMARK_NODE_TYPE_INLINE | 0x000c,
} cmark_node_type;

extern cmark_node_type CMARK_NODE_LAST_BLOCK;
Expand Down Expand Up @@ -453,6 +454,17 @@ CMARK_GFM_EXPORT const char *cmark_node_get_title(cmark_node *node);
*/
CMARK_GFM_EXPORT int cmark_node_set_title(cmark_node *node, const char *title);

/** Returns the attributes of an attribute 'node', or an empty string
if no attributes are set. Returns NULL if called on a node that is
not an attribute.
*/
CMARK_GFM_EXPORT const char *cmark_node_get_attributes(cmark_node *node);

/** Sets the attributes of an attribute 'node'. Returns 1 on success,
* 0 on failure.
*/
CMARK_GFM_EXPORT int cmark_node_set_attributes(cmark_node *node, const char *attributes);

/** Returns the literal "on enter" text for a custom 'node', or
an empty string if no on_enter is set. Returns NULL if called
on a non-custom node.
Expand Down
5 changes: 5 additions & 0 deletions src/include/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ typedef struct {
cmark_chunk title;
} cmark_link;

typedef struct {
cmark_chunk attributes;
} cmark_attribute;

typedef struct {
cmark_chunk on_enter;
cmark_chunk on_exit;
Expand Down Expand Up @@ -83,6 +87,7 @@ struct cmark_node {
cmark_code code;
cmark_heading heading;
cmark_link link;
cmark_attribute attribute;
cmark_custom custom;
int html_block_type;
void *opaque;
Expand Down
140 changes: 127 additions & 13 deletions src/inlines.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";

#define MAXBACKTICKS 80

typedef enum {
LINK,
IMAGE,
ATTRIBUTE
} bracket_type;

typedef struct bracket {
struct bracket *previous;
struct delimiter *previous_delimiter;
cmark_node *inl_text;
bufsize_t position;
bool image;
bracket_type type;
bool active;
bool bracket_after;
} bracket;
Expand Down Expand Up @@ -513,12 +519,12 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
subj->last_delim = delim;
}

static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
static void push_bracket(subject *subj, bracket_type type, cmark_node *inl_text) {
bracket *b = (bracket *)subj->mem->calloc(1, sizeof(bracket));
if (subj->last_bracket != NULL) {
subj->last_bracket->bracket_after = true;
}
b->image = image;
b->type = type;
b->active = true;
b->inl_text = inl_text;
b->previous = subj->last_bracket;
Expand Down Expand Up @@ -1032,7 +1038,91 @@ static bufsize_t manual_scan_link_url(cmark_chunk *input, bufsize_t offset,
return i - offset;
}

// Return a link, an image, or a literal close bracket.
static bufsize_t manual_scan_attribute_attributes(cmark_chunk *input, bufsize_t offset,
cmark_chunk *output) {
bufsize_t i = offset;
size_t nb_p = 0;

while (i < input->len) {
if (input->data[i] == '\\' &&
i + 1 < input->len &&
cmark_ispunct(input->data[i+1]))
i += 2;
else if (input->data[i] == '(') {
++nb_p;
++i;
if (nb_p > 32)
return -1;
} else if (input->data[i] == ')') {
if (nb_p == 0)
break;
--nb_p;
++i;
} else {
++i;
}
}

if (i >= input->len)
return -1;

{
cmark_chunk result = {input->data + offset, i - offset, 0};
*output = result;
}
return i - offset;
}

static cmark_node *handle_close_bracket_attribute(cmark_parser *parser, subject *subj, bracket *opener) {
bufsize_t startattributes, endattributes;
cmark_chunk attributes;
bufsize_t n;
cmark_node *inl;
cmark_chunk raw_label;
int found_label;
cmark_node *tmp, *tmpnext;

// ^name[content](attributes)
// TODO: support name. we will not even enter this with a name because we fail the match first

startattributes = subj->pos + 1;

if (peek_char(subj) == '(' &&
((n = manual_scan_attribute_attributes(&subj->input, subj->pos + 1,
&attributes)) > -1)) {

endattributes = subj->pos + 1 + n;

if (peek_at(subj, endattributes) == ')') {
subj->pos = endattributes + 1;
attributes = cmark_chunk_dup(&subj->input, startattributes, endattributes - startattributes);
}
}

inl = make_simple(subj->mem, CMARK_NODE_ATTRIBUTE);
inl->as.attribute.attributes = attributes;
inl->start_line = inl->end_line = subj->line;
inl->start_column = opener->inl_text->start_column;
inl->end_column = subj->pos + subj->column_offset + subj->block_offset;
cmark_node_insert_before(opener->inl_text, inl);
// Add content text:
tmp = opener->inl_text->next;
while (tmp) {
tmpnext = tmp->next;
cmark_node_append_child(inl, tmp);
tmp = tmpnext;
}

// Free the bracket ^[:
cmark_node_free(opener->inl_text);

process_emphasis(parser, subj, opener->previous_delimiter);
pop_bracket(subj);

return NULL;
}

// Return a link, an image, an attribute, or a literal close bracket.
static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
bufsize_t initial_pos, after_link_text_pos;
bufsize_t endurl, starttitle, endtitle, endall;
Expand All @@ -1050,7 +1140,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
advance(subj); // advance past ]
initial_pos = subj->pos;

// get last [ or ![
// get last [ or ![ or ^[
opener = subj->last_bracket;

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

if (opener->type == ATTRIBUTE) {
return handle_close_bracket_attribute(parser, subj, opener);
}

// If we got here, we matched a potential link/image text.
// Now we check to see if it's a link/image.
is_image = opener->image;
is_image = opener->type == IMAGE;

after_link_text_pos = subj->pos;

Expand Down Expand Up @@ -1188,7 +1282,7 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
if (!is_image) {
opener = subj->last_bracket;
while (opener != NULL) {
if (!opener->image) {
if (opener->type == LINK) {
if (!opener->active) {
break;
} else {
Expand Down Expand Up @@ -1341,7 +1435,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
case '[':
advance(subj);
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("["));
push_bracket(subj, false, new_inl);
push_bracket(subj, LINK, new_inl);
break;
case ']':
new_inl = handle_close_bracket(parser, subj);
Expand All @@ -1351,11 +1445,22 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
if (peek_char(subj) == '[' && peek_char_n(subj, 1) != '^') {
advance(subj);
new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("!["));
push_bracket(subj, true, new_inl);
push_bracket(subj, IMAGE, new_inl);
} else {
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("!"));
}
break;
case '^':
advance(subj);
// TODO: Support a name between ^ and [
if (peek_char(subj) == '[') {
advance(subj);
new_inl = make_str(subj, subj->pos - 2, subj->pos - 1, cmark_chunk_literal("^["));
push_bracket(subj, ATTRIBUTE, new_inl);
} else {
new_inl = make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("^"));
}
break;
default:
new_inl = try_extensions(parser, parent, c, subj);
if (new_inl != NULL)
Expand Down Expand Up @@ -1604,10 +1709,19 @@ cmark_chunk *cmark_inline_parser_get_chunk(cmark_inline_parser *parser) {
return &parser->input;
}

int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int image) {
for (bracket *b = parser->last_bracket; b; b = b->previous)
if (b->active && b->image == (image != 0))
return 1;
int cmark_inline_parser_in_bracket(cmark_inline_parser *parser, int type) {
for (bracket *b = parser->last_bracket; b; b = b->previous) {
if (b->active) {
switch (type) {
case 0:
return b->type == LINK;
case 1:
return b->type == IMAGE;
case 2:
return b->type == ATTRIBUTE;
}
}
}
return 0;
}

Expand Down
1 change: 1 addition & 0 deletions src/latex.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,

case CMARK_NODE_FOOTNOTE_DEFINITION:
case CMARK_NODE_FOOTNOTE_REFERENCE:
case CMARK_NODE_ATTRIBUTE:
// TODO
break;

Expand Down
4 changes: 4 additions & 0 deletions src/man.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
}
break;

case CMARK_NODE_ATTRIBUTE:
OUT(cmark_node_get_literal(node), allow_wrap, NORMAL);
break;

case CMARK_NODE_FOOTNOTE_DEFINITION:
case CMARK_NODE_FOOTNOTE_REFERENCE:
// TODO
Expand Down
Loading

0 comments on commit 3489555

Please sign in to comment.