Introducing new cmark extensions. Fixing superscript. And fuse all inline features together in insertText() method

master
Melroy van den Berg 2021-03-05 01:18:42 +01:00
parent 9336abe924
commit 3073331f60
11 changed files with 412 additions and 120 deletions

View File

@ -78,6 +78,7 @@
"syntax_extension.h": "c",
"render.h": "c",
"node.h": "c",
"*.in": "cpp"
"*.in": "cpp",
"superscript.h": "c"
}
}

View File

@ -11,6 +11,8 @@ set (LIBRARY_SOURCES
ext_scanners.h
tasklist.c
highlight.c
superscript.c
subscript.c
)
set_property(GLOBAL PROPERTY COMMONMARKER_EXTENSIONS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})

View File

@ -5,6 +5,8 @@
#include "tagfilter.h"
#include "tasklist.h"
#include "highlight.h"
#include "superscript.h"
#include "subscript.h"
#include "registry.h"
#include "plugin.h"
@ -16,6 +18,8 @@ static int core_extensions_registration(cmark_plugin *plugin) {
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_highlight_extension());
cmark_plugin_register_syntax_extension(plugin, create_superscript_extension());
cmark_plugin_register_syntax_extension(plugin, create_subscript_extension());
return 1;
}

View File

@ -1,5 +1,5 @@
/**
* Markdown Highlight extension by Melroy van den Berg
* Cmark Highlight inline extension by Melroy van den Berg
* Usage: ==Highlight text==
*/
#ifndef CMARK_GFM_HIGHLIGHT_H

View File

@ -0,0 +1,127 @@
#include "subscript.h"
#include <parser.h>
#include <render.h>
cmark_node_type CMARK_NODE_SUBSCRIPT;
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 left_flanking, right_flanking, punct_before, punct_after, delims;
char buffer[101] = {0};
if (character != '~')
return NULL;
delims = cmark_inline_parser_scan_delimiters(
inline_parser, sizeof(buffer) - 1, '~',
&left_flanking,
&right_flanking, &punct_before, &punct_after);
memset(buffer, '~', delims);
buffer[delims] = 0;
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
cmark_node_set_literal(res, buffer);
res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser) - delims;
if ((left_flanking || right_flanking) && (delims == 1)) {
cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
right_flanking, res);
}
return res;
}
static delimiter *insert(cmark_syntax_extension *self, cmark_parser *parser,
cmark_inline_parser *inline_parser, delimiter *opener,
delimiter *closer) {
cmark_node *subscript;
cmark_node *tmp, *next;
delimiter *delim, *tmp_delim;
delimiter *res = closer->next;
subscript = opener->inl_text;
if (opener->inl_text->as.literal.len != closer->inl_text->as.literal.len)
goto done;
if (!cmark_node_set_type(subscript, CMARK_NODE_SUBSCRIPT))
goto done;
cmark_node_set_syntax_extension(subscript, self);
tmp = cmark_node_next(opener->inl_text);
while (tmp) {
if (tmp == closer->inl_text)
break;
next = cmark_node_next(tmp);
cmark_node_append_child(subscript, tmp);
tmp = next;
}
subscript->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
cmark_node_free(closer->inl_text);
delim = closer;
while (delim != NULL && delim != opener) {
tmp_delim = delim->previous;
cmark_inline_parser_remove_delimiter(inline_parser, delim);
delim = tmp_delim;
}
cmark_inline_parser_remove_delimiter(inline_parser, opener);
done:
return res;
}
static const char *get_type_string(cmark_syntax_extension *extension,
cmark_node *node) {
return node->type == CMARK_NODE_SUBSCRIPT ? "subscript" : "<unknown>";
}
static int can_contain(cmark_syntax_extension *extension, cmark_node *node,
cmark_node_type child_type) {
if (node->type != CMARK_NODE_SUBSCRIPT)
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) {
renderer->out(renderer, node, "~", false, LITERAL);
}
static void plaintext_render(cmark_syntax_extension *extension,
cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options) {
renderer->out(renderer, node, "~", false, LITERAL);
}
cmark_syntax_extension *create_subscript_extension(void) {
cmark_syntax_extension *ext = cmark_syntax_extension_new("subscript");
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, plaintext_render);
CMARK_NODE_SUBSCRIPT = 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;
}

View File

@ -0,0 +1,15 @@
/**
* Cmark Subscript inline extension by Melroy van den Berg
* Usage: ~Subscript~
* TODO: Is conflicting with highlight, so I need to merge to 2 features into 1 solution.
* We have the same matching char (~) for both subscript: 1x~ and highlight 2x~
*/
#ifndef CMARK_GFM_SUBSCRIPT_H
#define CMARK_GFM_SUBSCRIPT_H
#include "cmark-gfm-core-extensions.h"
extern cmark_node_type CMARK_NODE_SUBSCRIPT;
cmark_syntax_extension *create_subscript_extension(void);
#endif

View File

@ -0,0 +1,127 @@
#include "superscript.h"
#include <parser.h>
#include <render.h>
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 left_flanking, right_flanking, punct_before, punct_after, delims;
char buffer[101] = {0};
if (character != '^')
return NULL;
delims = cmark_inline_parser_scan_delimiters(
inline_parser, sizeof(buffer) - 1, '^',
&left_flanking,
&right_flanking, &punct_before, &punct_after);
memset(buffer, '^', delims);
buffer[delims] = 0;
res = cmark_node_new_with_mem(CMARK_NODE_TEXT, parser->mem);
cmark_node_set_literal(res, buffer);
res->start_line = res->end_line = cmark_inline_parser_get_line(inline_parser);
res->start_column = cmark_inline_parser_get_column(inline_parser) - delims;
if ((left_flanking || right_flanking) && (delims == 1)) {
cmark_inline_parser_push_delimiter(inline_parser, character, left_flanking,
right_flanking, res);
}
return res;
}
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;
superscript = opener->inl_text;
if (opener->inl_text->as.literal.len != closer->inl_text->as.literal.len)
goto done;
if (!cmark_node_set_type(superscript, CMARK_NODE_SUPERSCRIPT))
goto done;
cmark_node_set_syntax_extension(superscript, self);
tmp = cmark_node_next(opener->inl_text);
while (tmp) {
if (tmp == closer->inl_text)
break;
next = cmark_node_next(tmp);
cmark_node_append_child(superscript, tmp);
tmp = next;
}
superscript->end_column = closer->inl_text->start_column + closer->inl_text->as.literal.len - 1;
cmark_node_free(closer->inl_text);
delim = closer;
while (delim != NULL && delim != opener) {
tmp_delim = delim->previous;
cmark_inline_parser_remove_delimiter(inline_parser, delim);
delim = tmp_delim;
}
cmark_inline_parser_remove_delimiter(inline_parser, opener);
done:
return res;
}
static const char *get_type_string(cmark_syntax_extension *extension,
cmark_node *node) {
return node->type == CMARK_NODE_SUPERSCRIPT ? "superscript" : "<unknown>";
}
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) {
renderer->out(renderer, node, "^", false, LITERAL);
}
static void plaintext_render(cmark_syntax_extension *extension,
cmark_renderer *renderer, cmark_node *node,
cmark_event_type ev_type, int options) {
renderer->out(renderer, node, "^", false, LITERAL);
}
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, plaintext_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;
}

View File

@ -0,0 +1,13 @@
/**
* Cmark Superscript inline extension by Melroy van den Berg
* Usage: ^Superscript^
*/
#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

View File

@ -31,6 +31,8 @@ Draw::Draw(MainWindow &mainWindow)
isItalic(false),
isStrikethrough(false),
isHighlight(false),
isSuperscript(false),
isSubscript(false),
isQuote(false),
bulletListLevel(0),
orderedListLevel(0),
@ -38,9 +40,6 @@ Draw::Draw(MainWindow &mainWindow)
isLink(false),
hovingOverLink(false),
defaultFont(fontFamily),
boldItalic(fontFamily),
bold(fontFamily),
italic(fontFamily),
heading1(fontFamily),
heading2(fontFamily),
heading3(fontFamily),
@ -59,15 +58,6 @@ Draw::Draw(MainWindow &mainWindow)
// set_pixels_inside_wrap(1);
set_wrap_mode(Gtk::WrapMode::WRAP_WORD_CHAR);
defaultFont.set_size(fontSize);
boldItalic.set_size(fontSize);
boldItalic.set_weight(Pango::WEIGHT_BOLD);
boldItalic.set_style(Pango::Style::STYLE_ITALIC);
bold.set_size(fontSize);
bold.set_weight(Pango::WEIGHT_BOLD);
italic.set_size(fontSize);
italic.set_style(Pango::Style::STYLE_ITALIC);
heading1.set_size(fontSize * PANGO_SCALE_XXX_LARGE);
heading1.set_weight(Pango::WEIGHT_BOLD);
heading2.set_size(fontSize * PANGO_SCALE_XX_LARGE);
@ -642,6 +632,16 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
isHighlight = entering;
return;
}
else if (strcmp(node->extension->name, "superscript") == 0)
{
isSuperscript = entering;
return;
}
else if (strcmp(node->extension->name, "subscript") == 0)
{
isSubscript = entering;
return;
}
}
switch (node->type)
@ -659,6 +659,8 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
isItalic = false;
isStrikethrough = false;
isHighlight = false;
isSuperscript = false;
isSubscript = false;
isQuote = false;
}
break;
@ -757,7 +759,9 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
case CMARK_NODE_THEMATIC_BREAK:
{
insertBold("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\n\n");
isBold = true;
insertText("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\n\n");
isBold = false;
}
break;
@ -840,36 +844,15 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
break;
}
}
// Bold, italic and strikethrough text
else if (isBold && isItalic)
{
insertBoldItalic(text);
}
else if (isBold)
{
insertBold(text);
}
else if (isItalic)
{
insertItalic(text);
}
else if (isStrikethrough)
{
insertStrikethrough(text);
}
else if (isHighlight)
{
insertHighlight(text);
}
// URL
else if (isLink)
{
insertLink(text, linkURL);
linkURL = "";
}
// Text (with optional inline formatting)
else
{
// Normal text only
insertText(text);
}
}
@ -923,7 +906,7 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
case CMARK_NODE_FOOTNOTE_DEFINITION:
break;
default:
throw std::runtime_error("Node type not found. Type (int): " + std::to_string(node->type));
throw std::runtime_error("Node type '" + std::string(cmark_node_get_type_string(node)) + "' not found.");
break;
}
}
@ -933,12 +916,50 @@ void Draw::processNode(cmark_node *node, cmark_event_type ev_type)
*/
void Draw::insertText(const std::string &text)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\"";
auto font = defaultFont;
std::string span;
std::string foreground;
std::string background;
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
// eg. font.set_size(8000);
foreground = "blue";
}
if (isStrikethrough)
{
span.append(" strikethrough=\"true\"");
}
if (isSuperscript)
{
font.set_size(8000);
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
if (isBold)
{
font.set_weight(Pango::WEIGHT_BOLD);
}
if (isItalic)
{
font.set_style(Pango::STYLE_ITALIC);
}
if (isHighlight)
{
foreground = "black";
background = "#FFFF00";
}
if (!foreground.empty())
{
span.append("foreground=\"" + foreground + "\"");
}
if (!background.empty())
{
span.append("background=\"" + background + "\"");
}
span.insert(0, "font_desc=\"" + font.to_string() + "\"");
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
@ -947,11 +968,19 @@ void Draw::insertText(const std::string &text)
*/
void Draw::insertCode(const std::string &code)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\" foreground=\"#323232\" background=\"#e0e0e0\"";
std::string span = "foreground=\"#323232\" background=\"#e0e0e0\"";
if (isQuote)
{
span.append(" size=\"2000\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + code + "</span>");
}
@ -974,6 +1003,14 @@ void Draw::insertHeading1(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
@ -984,6 +1021,14 @@ void Draw::insertHeading2(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
@ -994,6 +1039,14 @@ void Draw::insertHeading3(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
@ -1004,6 +1057,14 @@ void Draw::insertHeading4(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
@ -1014,6 +1075,14 @@ void Draw::insertHeading5(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
@ -1024,77 +1093,17 @@ void Draw::insertHeading6(const std::string &text)
{
span.append(" foreground=\"blue\"");
}
if (isSuperscript)
{
span.append(" rise=\"6000\"");
}
if (isSubscript)
{
span.append(" rise=\"-6000\"");
}
insertMarkupTextOnThread("\n<span " + span + ">" + text + "</span>\n\n");
}
void Draw::insertBoldItalic(const std::string &text)
{
std::string span = "font_desc=\"" + boldItalic.to_string() + "\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertBold(const std::string &text)
{
std::string span = "font_desc=\"" + bold.to_string() + "\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertItalic(const std::string &text)
{
std::string span = "font_desc=\"" + italic.to_string() + "\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertStrikethrough(const std::string &text)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\" strikethrough=\"true\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertHighlight(const std::string &text)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\" foreground=\"black\" background=\"#FFFF00\"";
if (isQuote)
{
span.append(" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertSubscript(const std::string &text)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\" rise=\"-20\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
void Draw::insertSuperscript(const std::string &text)
{
std::string span = "font_desc=\"" + defaultFont.to_string() + "\" rise=\"20\"";
if (isQuote)
{
span.append(" foreground=\"blue\" size=\"2000\"");
}
insertMarkupTextOnThread("<span " + span + ">" + text + "</span>");
}
/******************************************************
* Helper functions below

View File

@ -74,13 +74,6 @@ private:
void insertHeading4(const std::string &text);
void insertHeading5(const std::string &text);
void insertHeading6(const std::string &text);
void insertBoldItalic(const std::string &text);
void insertItalic(const std::string &text);
void insertBold(const std::string &text);
void insertStrikethrough(const std::string &text);
void insertHighlight(const std::string &text);
void insertSubscript(const std::string &text);
void insertSuperscript(const std::string &text);
void insertMarkupTextOnThread(const std::string &text);
void clearOnThread();
@ -101,6 +94,8 @@ private:
bool isItalic;
bool isStrikethrough;
bool isHighlight;
bool isSuperscript;
bool isSubscript;
bool isQuote;
int bulletListLevel;
int orderedListLevel;
@ -114,9 +109,6 @@ private:
bool hovingOverLink;
Pango::FontDescription defaultFont;
Pango::FontDescription boldItalic;
Pango::FontDescription bold;
Pango::FontDescription italic;
Pango::FontDescription heading1;
Pango::FontDescription heading2;
Pango::FontDescription heading3;

View File

@ -41,6 +41,8 @@ cmark_node *Parser::parseContent(const std::string &content)
// Add extensions
addMarkdownExtension(parser, "strikethrough");
addMarkdownExtension(parser, "highlight");
addMarkdownExtension(parser, "superscript");
addMarkdownExtension(parser, "subscript");
//addMarkdownExtension(parser, "table");
cmark_parser_feed(parser, data, strlen(data));