Skip to content

Commit

Permalink
add parenthesis version of superscript
Browse files Browse the repository at this point in the history
  • Loading branch information
QuietMisdreavus committed Oct 31, 2022
1 parent d5e0f78 commit c2b9ecb
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 33 deletions.
165 changes: 135 additions & 30 deletions extensions/superscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,154 @@
#include <parser.h>
#include <render.h>

static cmark_chunk *S_get_node_literal_chunk(cmark_node *node) {
if (node == NULL) {
return NULL;
}

switch (node->type) {
case CMARK_NODE_HTML_BLOCK:
case CMARK_NODE_TEXT:
case CMARK_NODE_HTML_INLINE:
case CMARK_NODE_CODE:
case CMARK_NODE_FOOTNOTE_REFERENCE:
return &node->as.literal;

case CMARK_NODE_CODE_BLOCK:
return &node->as.code.literal;

default:
break;
}

return NULL;
}

static bool S_node_contains_space(cmark_node *node) {
cmark_chunk *chunk = S_get_node_literal_chunk(node);
if (chunk)
return (cmark_chunk_strchr(chunk, ' ', 0) != chunk->len);
else
return false;
}

static bool S_children_contain_space(cmark_node *parent) {
cmark_node *node = parent->first_child;
while (node) {
if (S_node_contains_space(node)) {
return true;
}
node = node->next;
}

return false;
}

cmark_node_type CMARK_NODE_SUPERSCRIPT;

static cmark_node *match(cmark_syntax_extension *self, cmark_parser *parser,
cmark_node *parent, unsigned char character,
cmark_inline_parser *inline_parser) {
cmark_node *res = NULL;
int startpos = cmark_inline_parser_get_offset(inline_parser) + 1;
int endpos = startpos;

if (character != '^')
return NULL;

// TODO: long-form parsing with parens
if (cmark_inline_parser_peek_at(inline_parser, endpos) == '(')
return NULL;
int initpos = cmark_inline_parser_get_offset(inline_parser);

if (character == '^') {
if (cmark_inline_parser_peek_at(inline_parser, initpos + 1) == '(') {
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
cmark_node_set_literal(res, "^(");
res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser);
res->end_column = res->start_column + 2;

cmark_inline_parser_set_offset(inline_parser, initpos + 2);
cmark_inline_parser_push_delimiter(inline_parser, '^', true, false, res);
} else {
int startpos = initpos + 1;
int endpos = startpos;

cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser);
bufsize_t len = chunk->len;

while (endpos < len) {
unsigned char seekchar = cmark_inline_parser_peek_at(inline_parser, endpos);
if (cmark_isspace(seekchar) || (cmark_ispunct(seekchar) && seekchar != '^'))
break;
endpos++;
}

int nodelen = endpos - startpos;

// don't emit an empty node
if (nodelen == 0)
return NULL;

cmark_inline_parser_set_offset(inline_parser, startpos);

res = cmark_node_new_with_mem_and_ext(CMARK_NODE_SUPERSCRIPT, parser->mem, self);
res->as.literal = cmark_chunk_dup(chunk, startpos, nodelen);
res->start_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser);

cmark_inline_parser_set_offset(inline_parser, endpos);

res->end_line = cmark_inline_parser_get_line(inline_parser);
res->end_column = cmark_inline_parser_get_column(inline_parser);

const char *text = cmark_chunk_to_cstr(parser->mem, &res->as.literal);
cmark_node_set_string_content(res, text);

cmark_parse_inlines(parser, res, parser->refmap, parser->options);
}
} else if (character == ')') {
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
cmark_node_set_literal(res, ")");
res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser);
res->end_column = res->start_column + 1;

cmark_inline_parser_set_offset(inline_parser, initpos + 1);
cmark_inline_parser_push_delimiter(inline_parser, '^', false, true, res);
}

cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser);
bufsize_t len = chunk->len;
return res;
}

while (endpos < len && !cmark_isspace(cmark_inline_parser_peek_at(inline_parser, endpos)))
endpos++;
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
cmark_inline_parser *inline_parser, delimiter *opener,
delimiter *closer) {
cmark_node *superscript;
cmark_node *tmp, *next;
delimiter *delim, *tmp_delim;
delimiter *res = closer->next;

int nodelen = endpos - startpos;
superscript = opener->inl_text;

// don't emit an empty node
if (nodelen == 0)
return NULL;
if (!cmark_node_set_type(superscript, CMARK_NODE_SUPERSCRIPT))
return res;

cmark_inline_parser_set_offset(inline_parser, startpos);
cmark_node_set_syntax_extension(superscript, self);

res = cmark_node_new_with_mem_and_ext(CMARK_NODE_SUPERSCRIPT, parser->mem, self);
res->as.literal = cmark_chunk_dup(chunk, startpos, nodelen);
res->start_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser);
tmp = cmark_node_next(opener->inl_text);

cmark_inline_parser_set_offset(inline_parser, endpos);
while (tmp) {
if (tmp == closer->inl_text)
break;
next = cmark_node_next(tmp);
cmark_node_append_child(superscript, tmp);
tmp = next;
}

res->end_line = cmark_inline_parser_get_line(inline_parser);
res->end_column = cmark_inline_parser_get_column(inline_parser);
superscript->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
cmark_node_free(closer->inl_text);

const char *text = cmark_chunk_to_cstr(parser->mem, &res->as.literal);
cmark_node_set_string_content(res, text);
delim = closer;
while (delim != NULL && delim != opener) {
tmp_delim = delim->previous;
cmark_inline_parser_remove_delimiter(inline_parser, delim);
delim = tmp_delim;
}

cmark_parse_inlines(parser, res, parser->refmap, parser->options);
cmark_inline_parser_remove_delimiter(inline_parser, opener);

return res;
}
Expand All @@ -67,7 +171,7 @@ static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
static void commonmark_render(cmark_syntax_extension *extension,
cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options) {
bool should_wrap = (cmark_strbuf_strchr(&node->content, ' ', 0) != -1);
bool should_wrap = S_children_contain_space(node);
bool entering = (ev_type == CMARK_EVENT_ENTER);
if (entering) {
if (should_wrap)
Expand Down Expand Up @@ -129,10 +233,11 @@ cmark_syntax_extension *create_superscript_extension(void) {
CMARK_NODE_SUPERSCRIPT = cmark_syntax_extension_add_node(1);

cmark_syntax_extension_set_match_inline_func(ext, match);
// cmark_syntax_extension_set_inline_from_delim_func(ext, insert);
cmark_syntax_extension_set_inline_from_delim_func(ext, insert);

cmark_mem *mem = cmark_get_default_mem_allocator();
special_chars = cmark_llist_append(mem, special_chars, (void *)'^');
special_chars = cmark_llist_append(mem, special_chars, (void *)')');
cmark_syntax_extension_set_special_inline_chars(ext, special_chars);

cmark_syntax_extension_set_emphasis(ext, 1);
Expand Down
6 changes: 3 additions & 3 deletions test/spec.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7756,7 +7756,7 @@ Superscripting a whole ^word
In addition, if you would like to raise more than one word, you can add
parentheses around the text you would like to style in a superscript:

```````````````````````````````` example superscript disabled
```````````````````````````````` example superscript
I would like to ^(raise this whole phrase), please.
.
<p>I would like to <sup>raise this whole phrase</sup>, please.</p>
Expand All @@ -7770,10 +7770,10 @@ z = t^x^2
<p>z = t<sup>x<sup>2</sup></sup></p>
````````````````````````````````

```````````````````````````````` example superscript disabled
```````````````````````````````` example superscript
For my next trick, I will ^(raise my text ^(twice)), at the same time!
.
<p>For my next trick, I will <sup>raise my text<sup>twice</sup></sup>, at the same time!</p>
<p>For my next trick, I will <sup>raise my text <sup>twice</sup></sup>, at the same time!</p>
````````````````````````````````

</div>
Expand Down

0 comments on commit c2b9ecb

Please sign in to comment.