From f91ff5856e2276825d450610d1b040fcf8bda6cd Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 8 Nov 2022 20:01:27 +0100 Subject: Add syntax highlighting (currently only for matrix codeblocks) --- src/plugins/Matrix.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index a9284f5..d1a060d 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -2289,10 +2289,21 @@ namespace QuickMedia { bool inside_font_tag = false; bool font_tag_has_custom_color = false; bool inside_code_tag = false; + std::string_view code_tag_language; bool allow_formatted_text = false; + bool inside_source_highlight = false; + bool supports_syntax_highlight = false; mgl::Color font_color = mgl::Color(255, 255, 255, 255); }; + static int accumulate_string(char *data, int size, void *userdata) { + std::string *str = (std::string*)userdata; + if(str->size() + size > 1024 * 1024 * 100) // 100mb sane limit, TODO: make configurable + return 1; + str->append(data, size); + return 0; + } + // TODO: Full proper parsing with tag depth static int formattext_text_parser_callback(HtmlParser *html_parser, HtmlParseType parse_type, void *userdata) { FormattedTextParseUserdata &parse_userdata = *(FormattedTextParseUserdata*)userdata; @@ -2304,8 +2315,10 @@ namespace QuickMedia { parse_userdata.inside_font_tag = true; else if(html_parser->tag_name.size == 8 && memcmp(html_parser->tag_name.data, "mx-reply", 8) == 0) ++parse_userdata.mx_reply_depth; - else if(html_parser->tag_name.size == 4 && memcmp(html_parser->tag_name.data, "code", 4) == 0) + else if(html_parser->tag_name.size == 4 && memcmp(html_parser->tag_name.data, "code", 4) == 0) { parse_userdata.inside_code_tag = true; + parse_userdata.code_tag_language = std::string_view(); + } break; } case HTML_PARSE_TAG_END: { @@ -2325,6 +2338,9 @@ namespace QuickMedia { if(parse_userdata.inside_font_tag && html_parser->attribute_key.size == 5 && memcmp(html_parser->attribute_key.data, "color", 5) == 0) { if(parse_hex_set_color(html_parser->attribute_value.data, html_parser->attribute_value.size, parse_userdata.font_color)) parse_userdata.font_tag_has_custom_color = true; + } else if(parse_userdata.inside_code_tag && html_parser->attribute_key.size == 5 && memcmp(html_parser->attribute_key.data, "class", 5) == 0) { + if(html_parser->attribute_value.size > 9 && memcmp(html_parser->attribute_value.data, "language-", 9) == 0) + parse_userdata.code_tag_language = std::string_view(html_parser->attribute_value.data + 9, html_parser->attribute_value.size - 9); } break; } @@ -2335,12 +2351,32 @@ namespace QuickMedia { html_unescape_sequences(text_to_add); uint8_t formatted_text_flags = FORMATTED_TEXT_FLAG_NONE; - if(parse_userdata.font_tag_has_custom_color) - formatted_text_flags |= FORMATTED_TEXT_FLAG_COLOR; - if(parse_userdata.inside_code_tag) - formatted_text_flags |= FORMATTED_TEXT_FLAG_MONOSPACE; + if(parse_userdata.allow_formatted_text) { + if(parse_userdata.font_tag_has_custom_color) + formatted_text_flags |= FORMATTED_TEXT_FLAG_COLOR; + + if(parse_userdata.inside_source_highlight || !parse_userdata.supports_syntax_highlight || (parse_userdata.inside_code_tag && parse_userdata.code_tag_language.size() == 0)) { + formatted_text_flags |= FORMATTED_TEXT_FLAG_MONOSPACE; + } else if(parse_userdata.inside_code_tag) { + formatted_text_flags |= FORMATTED_TEXT_FLAG_MONOSPACE; + // TODO: guess language from code if no language is set. + // TODO: Allow the user to choose style in config file. + + const std::string code_language(parse_userdata.code_tag_language); + const char *args[] = { "source-highlight", "-f", "html", "-s", code_language.c_str(), "--style-file=esc256.style", "-o", "STDOUT", nullptr }; + std::string output; + if(exec_program_write_stdin(args, text_to_add.c_str(), text_to_add.size(), accumulate_string, &output) == 0) { + FormattedTextParseUserdata code_parse_userdata; + code_parse_userdata.allow_formatted_text = true; + code_parse_userdata.inside_source_highlight = true; + html_parser_parse(output.c_str(), output.size(), formattext_text_parser_callback, &code_parse_userdata); + text_to_add = std::move(code_parse_userdata.result); + formatted_text_flags = FORMATTED_TEXT_FLAG_NONE; + } + } + } - if(formatted_text_flags != FORMATTED_TEXT_FLAG_NONE && parse_userdata.allow_formatted_text) + if(formatted_text_flags != FORMATTED_TEXT_FLAG_NONE) parse_userdata.result += Text::formatted_text(text_to_add, parse_userdata.font_color, formatted_text_flags); else parse_userdata.result += std::move(text_to_add); @@ -2354,6 +2390,7 @@ namespace QuickMedia { std::string formatted_text_to_qm_text(const char *str, size_t size, bool allow_formatted_text) { FormattedTextParseUserdata parse_userdata; parse_userdata.allow_formatted_text = allow_formatted_text; + parse_userdata.supports_syntax_highlight = is_program_executable_by_name("source-highlight"); html_parser_parse(str, size, formattext_text_parser_callback, &parse_userdata); return std::move(parse_userdata.result); } -- cgit v1.2.3