From f10d17d46e4ea80990416844b4314d564b3502dc Mon Sep 17 00:00:00 2001 From: QuietMisdreavus Date: Fri, 31 Dec 2021 16:58:26 -0700 Subject: [PATCH] wip: half of the 'superscript' extension --- extensions/CMakeLists.txt | 1 + extensions/core-extensions.c | 2 + extensions/superscript.c | 141 +++++++++++++++++++++++++++++++++++ extensions/superscript.h | 9 +++ test/spec.txt | 44 +++++++++++ 5 files changed, 197 insertions(+) create mode 100644 extensions/superscript.c create mode 100644 extensions/superscript.h diff --git a/extensions/CMakeLists.txt b/extensions/CMakeLists.txt index 4977c98c3..d5d2284c0 100644 --- a/extensions/CMakeLists.txt +++ b/extensions/CMakeLists.txt @@ -11,6 +11,7 @@ set(LIBRARY_SOURCES ext_scanners.re ext_scanners.h tasklist.c + superscript.c ) include_directories( diff --git a/extensions/core-extensions.c b/extensions/core-extensions.c index 131cdf402..1456116bf 100644 --- a/extensions/core-extensions.c +++ b/extensions/core-extensions.c @@ -7,6 +7,7 @@ #include "tasklist.h" #include "registry.h" #include "plugin.h" +#include "superscript.h" static int core_extensions_registration(cmark_plugin *plugin) { cmark_plugin_register_syntax_extension(plugin, create_table_extension()); @@ -15,6 +16,7 @@ static int core_extensions_registration(cmark_plugin *plugin) { cmark_plugin_register_syntax_extension(plugin, create_autolink_extension()); cmark_plugin_register_syntax_extension(plugin, create_tagfilter_extension()); cmark_plugin_register_syntax_extension(plugin, create_tasklist_extension()); + cmark_plugin_register_syntax_extension(plugin, create_superscript_extension()); return 1; } diff --git a/extensions/superscript.c b/extensions/superscript.c new file mode 100644 index 000000000..64112cb9d --- /dev/null +++ b/extensions/superscript.c @@ -0,0 +1,141 @@ +#include "superscript.h" +#include +#include +#include + +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; + + cmark_chunk *chunk = cmark_inline_parser_get_chunk(inline_parser); + bufsize_t len = chunk->len; + + while (endpos < len && !cmark_isspace(cmark_inline_parser_peek_at(inline_parser, endpos))) + 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); + + return res; +} + +static const char *get_type_string(cmark_syntax_extension *extension, + cmark_node *node) { + return node->type == CMARK_NODE_SUPERSCRIPT ? "superscript" : ""; +} + +static int can_contain(cmark_syntax_extension *extension, cmark_node *node, + cmark_node_type child_type) { + if (node->type != CMARK_NODE_SUPERSCRIPT) + return false; + + return CMARK_NODE_TYPE_INLINE_P(child_type); +} + +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 entering = (ev_type == CMARK_EVENT_ENTER); + if (entering) { + if (should_wrap) + renderer->out(renderer, node, "^(", false, LITERAL); + else + renderer->out(renderer, node, "^", false, LITERAL); + } else if (!entering && should_wrap) { + renderer->out(renderer, node, ")", false, LITERAL); + } +} + +static void latex_render(cmark_syntax_extension *extension, + cmark_renderer *renderer, cmark_node *node, + cmark_event_type ev_type, int options) { + bool entering = (ev_type == CMARK_EVENT_ENTER); + if (entering) { + renderer->out(renderer, node, "^{", false, LITERAL); + } else { + renderer->out(renderer, node, "}", false, LITERAL); + } +} + +static void man_render(cmark_syntax_extension *extension, + cmark_renderer *renderer, cmark_node *node, + cmark_event_type ev_type, int options) { + // requires MOM + bool entering = (ev_type == CMARK_EVENT_ENTER); + if (entering) { + renderer->cr(renderer); + renderer->out(renderer, node, "\\*[SUP]", false, LITERAL); + } else { + renderer->out(renderer, node, "\\*[SUPX]", false, LITERAL); + renderer->cr(renderer); + } +} + +static void html_render(cmark_syntax_extension *extension, + cmark_html_renderer *renderer, cmark_node *node, + cmark_event_type ev_type, int options) { + bool entering = (ev_type == CMARK_EVENT_ENTER); + if (entering) { + cmark_strbuf_puts(renderer->html, ""); + } else { + cmark_strbuf_puts(renderer->html, ""); + } +} + +cmark_syntax_extension *create_superscript_extension(void) { + cmark_syntax_extension *ext = cmark_syntax_extension_new("superscript"); + cmark_llist *special_chars = NULL; + + cmark_syntax_extension_set_get_type_string_func(ext, get_type_string); + cmark_syntax_extension_set_can_contain_func(ext, can_contain); + cmark_syntax_extension_set_commonmark_render_func(ext, commonmark_render); + cmark_syntax_extension_set_plaintext_render_func(ext, commonmark_render); + cmark_syntax_extension_set_latex_render_func(ext, latex_render); + cmark_syntax_extension_set_man_render_func(ext, man_render); + cmark_syntax_extension_set_html_render_func(ext, html_render); + 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_mem *mem = cmark_get_default_mem_allocator(); + 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); + + return ext; +} diff --git a/extensions/superscript.h b/extensions/superscript.h new file mode 100644 index 000000000..465e1a2ab --- /dev/null +++ b/extensions/superscript.h @@ -0,0 +1,9 @@ +#ifndef CMARK_GFM_SUPERSCRIPT_H +#define CMARK_GFM_SUPERSCRIPT_H + +#include "cmark-gfm-core-extensions.h" + +extern cmark_node_type CMARK_NODE_SUPERSCRIPT; +cmark_syntax_extension *create_superscript_extension(void); + +#endif /* CMARK_GFM_SUPERSCRIPT_H */ diff --git a/test/spec.txt b/test/spec.txt index 582131d70..ce9bbed37 100644 --- a/test/spec.txt +++ b/test/spec.txt @@ -7734,6 +7734,50 @@ new paragraph~~. +
+ +## Superscript (extension) + +GFM provides the `superscript` extension, which adds "superscript" spans. + +There are two ways to write superscripts. For simple uses, you can use a +caret (`^`) to style the text between it and the next space (or the end of +the line): + +```````````````````````````````` example superscript +y = x^2 + 2 + +Superscripting a whole ^word +. +

y = x2 + 2

+

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 +I would like to ^(raise this whole phrase), please. +. +

I would like to raise this whole phrase, please.

+```````````````````````````````` + +Superscripts can be nested, by adding additional carets: + +```````````````````````````````` example superscript +z = t^x^2 +. +

z = tx2

+```````````````````````````````` + +```````````````````````````````` example superscript disabled +For my next trick, I will ^(raise my text ^(twice)), at the same time! +. +

For my next trick, I will raise my texttwice, at the same time!

+```````````````````````````````` + +
+ ## Links A link contains [link text] (the visible text), a [link destination]