From 41396ab246dc716596b4afaa696a727ca9d31920 Mon Sep 17 00:00:00 2001 From: Petra Baranski Date: Sun, 23 Jul 2023 09:43:32 +0200 Subject: [PATCH] feat: add config options to en-/disable each parser Also fix cmake line, so that ctest can find the test executable; update readme. --- LICENSE | 2 +- README.md | 10 +- include/maddy/paragraphparser.h | 21 +- include/maddy/parser.h | 236 ++++++++++++++++++--- include/maddy/parserconfig.h | 48 +++++ tests/CMakeLists.txt | 2 +- tests/maddy/test_maddy_paragraphparser.cpp | 3 +- tests/maddy/test_maddy_parser.cpp | 30 +++ tests/maddy/test_maddy_parser.h | 1 + 9 files changed, 310 insertions(+), 43 deletions(-) diff --git a/LICENSE b/LICENSE index 3879e0d..4f4e581 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2017, 2018, 2019, 2020 M. Petra Baranski +Copyright 2017, 2018, 2019, 2020, 2023 M. Petra Baranski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 83f9b6d..e519324 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,6 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Version: 1.1.2](https://img.shields.io/badge/Version-1.1.2-brightgreen.svg)](https://semver.org/) -[![Travis Build Status](https://travis-ci.org/progsource/maddy.svg?branch=master)](https://travis-ci.org/progsource/maddy) -[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/04m0lg27kigv1pg8/branch/master?svg=true)](https://ci.appveyor.com/project/progsource/maddy/branch/master) maddy is a C++ Markdown to HTML **header-only** parser library. @@ -45,13 +43,17 @@ std::stringstream markdownInput(""); // config is optional std::shared_ptr config = std::make_shared(); -config->isEmphasizedParserEnabled = true; // default -config->isHTMLWrappedInParagraph = true; // default +// config->isEmphasizedParserEnabled = false; // default true - this flag is deprecated +// config->isHTMLWrappedInParagraph = false; // default true - this flag is deprecated +config->enabledParsers &= ~maddy::types::EMPHASIZED_PARSER; +config->enabledParsers |= maddy::types::HTML_PARSER; std::shared_ptr parser = std::make_shared(config); std::string htmlOutput = parser->Parse(markdownInput); ``` +You can find all parser flags in [`include/maddy/parserconfig.h`](include/maddy/parserconfig.h). + ## How to run the tests *(tested on Linux with diff --git a/include/maddy/paragraphparser.h b/include/maddy/paragraphparser.h index 303e3b0..40d19cc 100644 --- a/include/maddy/paragraphparser.h +++ b/include/maddy/paragraphparser.h @@ -34,11 +34,13 @@ public: */ ParagraphParser( std::function parseLineCallback, - std::function(const std::string& line)> getBlockParserForLineCallback + std::function(const std::string& line)> getBlockParserForLineCallback, + bool isEnabled ) : BlockParser(parseLineCallback, getBlockParserForLineCallback) , isStarted(false) , isFinished(false) + , isEnabled(isEnabled) {} /** @@ -88,19 +90,31 @@ protected: void parseBlock(std::string& line) override { - if (!this->isStarted) + if (this->isEnabled && !this->isStarted) { line = "

" + line + " "; this->isStarted = true; return; } + else if (!this->isEnabled && !this->isStarted) + { + line += " "; + this->isStarted = true; + return; + } - if (line.empty()) + if (this->isEnabled && line.empty()) { line += "

"; this->isFinished = true; return; } + else if (!this->isEnabled && line.empty()) + { + line += "
"; + this->isFinished = true; + return; + } line += " "; } @@ -108,6 +122,7 @@ protected: private: bool isStarted; bool isFinished; + bool isEnabled; }; // class ParagraphParser // ----------------------------------------------------------------------------- diff --git a/include/maddy/parser.h b/include/maddy/parser.h index add4c45..ffca7aa 100644 --- a/include/maddy/parser.h +++ b/include/maddy/parser.h @@ -59,15 +59,82 @@ public: */ Parser(std::shared_ptr config = nullptr) : config(config) - , breakLineParser(std::make_shared()) - , emphasizedParser(std::make_shared()) - , imageParser(std::make_shared()) - , inlineCodeParser(std::make_shared()) - , italicParser(std::make_shared()) - , linkParser(std::make_shared()) - , strikeThroughParser(std::make_shared()) - , strongParser(std::make_shared()) - {} + { + // deprecated backward compatibility + // will be removed in 1.4.0 latest including the booleans + if (this->config && !this->config->isEmphasizedParserEnabled) + { + this->config->enabledParsers &= ~maddy::types::EMPHASIZED_PARSER; + } + if (this->config && !this->config->isHTMLWrappedInParagraph) + { + this->config->enabledParsers |= maddy::types::HTML_PARSER; + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::BREAKLINE_PARSER) != 0 + ) + { + this->breakLineParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::EMPHASIZED_PARSER) != 0 + ) + { + this->emphasizedParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::IMAGE_PARSER) != 0 + ) + { + this->imageParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::INLINE_CODE_PARSER) != 0 + ) + { + this->inlineCodeParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::ITALIC_PARSER) != 0 + ) + { + this->italicParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::LINK_PARSER) != 0 + ) + { + this->linkParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::STRIKETHROUGH_PARSER) != 0 + ) + { + this->strikeThroughParser = std::make_shared(); + } + + if ( + !this->config || + (this->config->enabledParsers & maddy::types::STRONG_PARSER) != 0 + ) + { + this->strongParser = std::make_shared(); + } + } /** * Parse @@ -132,24 +199,46 @@ private: runLineParser(std::string& line) const { // Attention! ImageParser has to be before LinkParser - this->imageParser->Parse(line); - this->linkParser->Parse(line); + if (this->imageParser) + { + this->imageParser->Parse(line); + } + + if (this->linkParser) + { + this->linkParser->Parse(line); + } // Attention! StrongParser has to be before EmphasizedParser - this->strongParser->Parse(line); + if (this->strongParser) + { + this->strongParser->Parse(line); + } - if (!this->config || this->config->isEmphasizedParserEnabled) + if (this->emphasizedParser) { this->emphasizedParser->Parse(line); } - this->strikeThroughParser->Parse(line); + if (this->strikeThroughParser) + { + this->strikeThroughParser->Parse(line); + } - this->inlineCodeParser->Parse(line); + if (this->inlineCodeParser) + { + this->inlineCodeParser->Parse(line); + } - this->italicParser->Parse(line); + if (this->italicParser) + { + this->italicParser->Parse(line); + } - this->breakLineParser->Parse(line); + if (this->breakLineParser) + { + this->breakLineParser->Parse(line); + } } std::shared_ptr @@ -157,66 +246,117 @@ private: { std::shared_ptr parser; - if (maddy::CodeBlockParser::IsStartingLine(line)) + if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::CODE_BLOCK_PARSER) != 0 + ) && + maddy::CodeBlockParser::IsStartingLine(line) + ) { parser = std::make_shared( nullptr, nullptr ); } - else if (maddy::HeadlineParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::HEADLINE_PARSER) != 0 + ) && + maddy::HeadlineParser::IsStartingLine(line) + ) { parser = std::make_shared( nullptr, nullptr ); } - else if (maddy::HorizontalLineParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::HORIZONTAL_LINE_PARSER) != 0 + ) && + maddy::HorizontalLineParser::IsStartingLine(line) + ) { parser = std::make_shared( nullptr, nullptr ); } - else if (maddy::QuoteParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::QUOTE_PARSER) != 0 + ) && + maddy::QuoteParser::IsStartingLine(line) + ) { parser = std::make_shared( [this](std::string& line){ this->runLineParser(line); }, [this](const std::string& line){ return this->getBlockParserForLine(line); } ); } - else if (maddy::TableParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::TABLE_PARSER) != 0 + ) && + maddy::TableParser::IsStartingLine(line) + ) { parser = std::make_shared( [this](std::string& line){ this->runLineParser(line); }, nullptr ); } - else if (maddy::ChecklistParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::CHECKLIST_PARSER) != 0 + ) && + maddy::ChecklistParser::IsStartingLine(line) + ) { parser = this->createChecklistParser(); } - else if (maddy::OrderedListParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::ORDERED_LIST_PARSER) != 0 + ) && + maddy::OrderedListParser::IsStartingLine(line) + ) { parser = this->createOrderedListParser(); } - else if (maddy::UnorderedListParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::UNORDERED_LIST_PARSER) != 0 + ) && + maddy::UnorderedListParser::IsStartingLine(line) + ) { parser = this->createUnorderedListParser(); } else if ( this->config && - !this->config->isHTMLWrappedInParagraph && + (this->config->enabledParsers & maddy::types::HTML_PARSER) != 0 && maddy::HtmlParser::IsStartingLine(line) ) { parser = std::make_shared(nullptr, nullptr); } - else if (maddy::ParagraphParser::IsStartingLine(line)) + else if ( + maddy::ParagraphParser::IsStartingLine(line) + ) { parser = std::make_shared( [this](std::string& line){ this->runLineParser(line); }, - nullptr + nullptr, + (!this->config || (this->config->enabledParsers & maddy::types::PARAGRAPH_PARSER) != 0) ); } @@ -232,7 +372,13 @@ private: { std::shared_ptr parser; - if (maddy::ChecklistParser::IsStartingLine(line)) + if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::CHECKLIST_PARSER) != 0 + ) && + maddy::ChecklistParser::IsStartingLine(line) + ) { parser = this->createChecklistParser(); } @@ -251,11 +397,23 @@ private: { std::shared_ptr parser; - if (maddy::OrderedListParser::IsStartingLine(line)) + if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::ORDERED_LIST_PARSER) != 0 + ) && + maddy::OrderedListParser::IsStartingLine(line) + ) { parser = this->createOrderedListParser(); } - else if (maddy::UnorderedListParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::UNORDERED_LIST_PARSER) != 0 + ) && + maddy::UnorderedListParser::IsStartingLine(line) + ) { parser = this->createUnorderedListParser(); } @@ -274,11 +432,23 @@ private: { std::shared_ptr parser; - if (maddy::OrderedListParser::IsStartingLine(line)) + if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::ORDERED_LIST_PARSER) != 0 + ) && + maddy::OrderedListParser::IsStartingLine(line) + ) { parser = this->createOrderedListParser(); } - else if (maddy::UnorderedListParser::IsStartingLine(line)) + else if ( + ( + !this->config || + (this->config->enabledParsers & maddy::types::UNORDERED_LIST_PARSER) != 0 + ) && + maddy::UnorderedListParser::IsStartingLine(line) + ) { parser = this->createUnorderedListParser(); } diff --git a/include/maddy/parserconfig.h b/include/maddy/parserconfig.h index e1b10ae..2395a2c 100644 --- a/include/maddy/parserconfig.h +++ b/include/maddy/parserconfig.h @@ -4,12 +4,50 @@ */ #pragma once +#include + // ----------------------------------------------------------------------------- namespace maddy { // ----------------------------------------------------------------------------- +namespace types { + +/** + * PARSER_TYPE + * + * Bitwise flags to turn on/off each parser +*/ +enum PARSER_TYPE : uint32_t +{ + NONE = 0, + + BREAKLINE_PARSER = 0b1, + CHECKLIST_PARSER = 0b10, + CODE_BLOCK_PARSER = 0b100, + EMPHASIZED_PARSER = 0b1000, + HEADLINE_PARSER = 0b10000, + HORIZONTAL_LINE_PARSER = 0b100000, + HTML_PARSER = 0b1000000, + IMAGE_PARSER = 0b10000000, + INLINE_CODE_PARSER = 0b100000000, + ITALIC_PARSER = 0b1000000000, + LINK_PARSER = 0b10000000000, + ORDERED_LIST_PARSER = 0b100000000000, + PARAGRAPH_PARSER = 0b1000000000000, + QUOTE_PARSER = 0b10000000000000, + STRIKETHROUGH_PARSER = 0b100000000000000, + STRONG_PARSER = 0b1000000000000000, + TABLE_PARSER = 0b10000000000000000, + UNORDERED_LIST_PARSER = 0b100000000000000000, + + DEFAULT = 0b111111111110111111, + ALL = 0b111111111111111111, +}; + +} // namespace types + /** * ParserConfig * @@ -17,12 +55,22 @@ namespace maddy { */ struct ParserConfig { + /** + * @deprecated will be removed in 1.4.0 latest + */ bool isEmphasizedParserEnabled; + + /** + * @deprecated will be removed in 1.4.0 latest + */ bool isHTMLWrappedInParagraph; + uint32_t enabledParsers; + ParserConfig() : isEmphasizedParserEnabled(true) , isHTMLWrappedInParagraph(true) + , enabledParsers(maddy::types::DEFAULT) {} }; // class ParserConfig diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0252b57..d8b9b2f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,4 +25,4 @@ target_include_directories(MaddyTests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(MaddyTests maddy gmock_main) -add_test(MaddyTests MaddyTests) +add_test(NAME MaddyTests COMMAND MaddyTests) diff --git a/tests/maddy/test_maddy_paragraphparser.cpp b/tests/maddy/test_maddy_paragraphparser.cpp index 90c0655..27e2fb4 100644 --- a/tests/maddy/test_maddy_paragraphparser.cpp +++ b/tests/maddy/test_maddy_paragraphparser.cpp @@ -20,7 +20,8 @@ protected: { this->pParser = std::make_shared( nullptr, - nullptr + nullptr, + true ); } }; diff --git a/tests/maddy/test_maddy_parser.cpp b/tests/maddy/test_maddy_parser.cpp index 6e0c76d..f69f44c 100644 --- a/tests/maddy/test_maddy_parser.cpp +++ b/tests/maddy/test_maddy_parser.cpp @@ -34,3 +34,33 @@ TEST(MADDY_PARSER, ItShouldParseWithConfig) ASSERT_EQ(testHtml2, output); } + +TEST(MADDY_PARSER, ItShouldParseWithBitwiseConfig) +{ + auto config = std::make_shared(); + config->enabledParsers &= ~maddy::types::EMPHASIZED_PARSER; + config->enabledParsers |= maddy::types::HTML_PARSER; + + auto parser = std::make_shared(config); + + std::stringstream markdown(testMarkdown); + + const std::string output = parser->Parse(markdown); + + ASSERT_EQ(testHtml2, output); +} + +TEST(MADDY_PARSER, ItShouldParseWithSmallConfig) +{ + auto config = std::make_shared(); + config->enabledParsers = maddy::types::EMPHASIZED_PARSER | + maddy::types::STRONG_PARSER; + + auto parser = std::make_shared(config); + + std::stringstream markdown(testMarkdown); + + const std::string output = parser->Parse(markdown); + + ASSERT_EQ(testHtml3, output); +} diff --git a/tests/maddy/test_maddy_parser.h b/tests/maddy/test_maddy_parser.h index e939fd8..aab0617 100644 --- a/tests/maddy/test_maddy_parser.h +++ b/tests/maddy/test_maddy_parser.h @@ -71,3 +71,4 @@ foot a|foot b|foot c\n\ const std::string testHtml = "

This is a test

This should result in a praragraph it's that simple.

  • an unordered list
    • with some hierarchy
      1. and an ordered
      2. list
      3. directly
    • inside
\nvar c = 'blub';\n

A Quote

With some text blocks inside

  • even a list
  • should be
  • possible

And well inline code should also work.

Another Headline

And not to forget link to progsource should work. And well - let's see how an image would be shown:

\"an


and more headlines

even a table

Left headermiddle headerlast header
cell 1cell 2cell 3
cell 4cell 5cell 6
foot afoot bfoot c
h5
h6
"; const std::string testHtml2 = "

This is a test

This should result in a praragraph it's that simple.

  • an unordered list
    • with some hierarchy
      1. and an _ordered_
      2. list
      3. directly
    • inside
\nvar c = 'blub';\n

A Quote

With some text blocks inside

  • even a list
  • should be
  • possible

And well inline code should also work.

Another Headline

And not to forget link to progsource should work. And well - let's see how an image would be shown:

\"an


and more headlines

even a table

Left headermiddle headerlast header
cell 1cell 2cell 3
cell 4cell 5cell 6
foot afoot bfoot c
h5
h6
"; +const std::string testHtml3 = "# This is a test
This should result in a praragraph it's that simple.
* an *unordered* list * with some hierarchy 1. and an ordered * list * directly * inside
``` var c = 'blub'; ```
> A Quote > > With some ~~text~~ blocks inside > > * even a list > * should be > * possible >
And well `inline code` should also work.
## Another Headline
And not to forget [link to progsource](http://progsource.de) should work. And well - let's see how an image would be shown:
![an image](http://progsource.de/img/progsource.png)
---

### and more headlines
- [ ] how - [ ] about - [ ] a - [x] nice - [x] check - [ ] list
#### even a table
|table> Left header|middle header|last header - | - | - cell 1|cell 2|cell 3 cell 4|cell 5|cell 6 - | - | - foot a|foot b|foot c |##### h5 ###### h6
";