diff --git a/CHANGELOG.md b/CHANGELOG.md index 63c0fca..251add8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ maddy uses [semver versioning](https://semver.org/). ## Upcoming +* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) Only create emphasis tags at word boundaries, i.e. `not only_internal_underscores`. * ... ## version 1.5.0 2025-04-21 diff --git a/include/maddy/emphasizedparser.h b/include/maddy/emphasizedparser.h index 1705e6f..af30928 100644 --- a/include/maddy/emphasizedparser.h +++ b/include/maddy/emphasizedparser.h @@ -40,8 +40,10 @@ class EmphasizedParser : public LineParser */ void Parse(std::string& line) override { + // Modifed from previous version, with help from + // https://stackoverflow.com/questions/61346949/regex-for-markdown-emphasis static std::regex re( - R"((?!.*`.*|.*.*)_(?!.*`.*|.*<\/code>.*)([^_]*)_(?!.*`.*|.*<\/code>.*))" + R"((?!.*`.*|.*.*)\b_(?![\s])(?!.*`.*|.*<\/code>.*)(.*?[^\s])_\b(?!.*`.*|.*<\/code>.*))" ); static std::string replacement = "$1"; diff --git a/include/maddy/strongparser.h b/include/maddy/strongparser.h index 348f2d4..a28d18b 100644 --- a/include/maddy/strongparser.h +++ b/include/maddy/strongparser.h @@ -40,6 +40,23 @@ class StrongParser : public LineParser */ void Parse(std::string& line) override { + // This version of the regex is changed exactly the same way + // that the regex for the emphasized parser was changed, and + // it then passes all the 'disabled' tests in the 'strong parser' + // test, but then it fails general parsing. For some reason, + // "__text__" translates "text" even though there + // are no word boundaries at the correct places. It's weird! + + // static std::vector res{ + // std::regex{ + // R"((?!.*`.*|.*.*)\b\*\*(?![\s])(?!.*`.*|.*<\/code>.*)" + // "(.*?[^\s])\*\*\b(?!.*`.*|.*<\/code>.*))" + // }, + // std::regex{ + // R"((?!.*`.*|.*.*)\b__(?![\s])(?!.*`.*|.*<\/code>.*)" + // "(.*?[^\s])__\b(?!.*`.*|.*<\/code>.*))" + // } + // }; static std::vector res{ std::regex{ R"((?!.*`.*|.*.*)\*\*(?!.*`.*|.*<\/code>.*)([^\*\*]*)\*\*(?!.*`.*|.*<\/code>.*))" diff --git a/tests/maddy/test_maddy_emphasizedparser.cpp b/tests/maddy/test_maddy_emphasizedparser.cpp index 6442779..a70c248 100644 --- a/tests/maddy/test_maddy_emphasizedparser.cpp +++ b/tests/maddy/test_maddy_emphasizedparser.cpp @@ -21,6 +21,89 @@ TEST(MADDY_EMPHASIZEDPARSER, ItReplacesMarkdownWithEmphasizedHTML) ASSERT_EQ(expected, text); } +TEST(MADDY_EMPHASIZEDPARSER, ItReplacesUnderscoresAtStringEdges) +{ + std::string text = "_some text_"; + std::string expected = "some text"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItDoesNotReplaceMarkdownWithInlineUnderscores) +{ + std::string text = "some text_bla_text testing _it_ out"; + std::string expected = "some text_bla_text testing it out"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItOnlyReplacesUnderscoresAtWordBreaks) +{ + std::string text = "some _text_bla_ testing _it_ out"; + std::string expected = "some text_bla testing it out"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItReplacesUnderscoresWithMultipleWords) +{ + std::string text = "some _text testing it_ out"; + std::string expected = "some text testing it out"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItAllowsDoubleUnderscores) +{ + // I'm not sure if this is standard or not, but this is how the github + // markdown parser behaves. Other things I've seen want it to *not* + // match. + std::string text = "some __text testing it_ out"; + std::string expected = "some _text testing it out"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItDoesntReplaceUnderscoresInsideCodeBlocks) +{ + std::string text = + "Stuff inside blocks _shouldn't be emphasized_ at all"; + std::string expected = + "Stuff inside blocks _shouldn't be emphasized_ at all"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItDoesNotReplaceUnderscoresInURLs) +{ + std::string text = "[Link Title](http://example.com/what_you_didn't_know)"; + std::string expected = + "[Link Title](http://example.com/what_you_didn't_know)"; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + TEST(MADDY_EMPHASIZEDPARSER, ItDoesNotParseInsideInlineCode) { std::string text = "some text `*bla*` `/**text*/` testing _it_ out"; @@ -32,3 +115,33 @@ TEST(MADDY_EMPHASIZEDPARSER, ItDoesNotParseInsideInlineCode) ASSERT_EQ(expected, text); } + +TEST(MADDY_EMPHASIZEDPARSER, ItParsesOutsideCodeBlocks) +{ + std::string text = + "Stuff inside blocks _shouldn't be emphasized_ " + " but outside _should_."; + std::string expected = + "Stuff inside blocks _shouldn't be emphasized_ " + " but outside should."; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_EMPHASIZEDPARSER, ItParsesOutsideTickBlocks) +{ + std::string text = + "Stuff inside `blocks _shouldn't be emphasized_ `" + " but outside _should_."; + std::string expected = + "Stuff inside `blocks _shouldn't be emphasized_ `" + " but outside should."; + auto emphasizedParser = std::make_shared(); + + emphasizedParser->Parse(text); + + ASSERT_EQ(expected, text); +} diff --git a/tests/maddy/test_maddy_strongparser.cpp b/tests/maddy/test_maddy_strongparser.cpp index f006e26..466068d 100644 --- a/tests/maddy/test_maddy_strongparser.cpp +++ b/tests/maddy/test_maddy_strongparser.cpp @@ -83,3 +83,118 @@ TEST(MADDY_STRONGPARSER, ItDoesNotParseInsideInlineCode) ASSERT_EQ(test.expected, test.text); } } + +TEST(MADDY_STRONGPARSER, ItReplacesUnderscoresAtStringEdges) +{ + std::string text = "__some text__"; + std::string expected = "some text"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(DISABLED_MADDY_STRONGPARSER, ItDoesNotReplaceMarkdownWithInlineUnderscores) +{ + std::string text = "some text__bla__text testing __it__ out"; + std::string expected = "some text__bla__text testing it out"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(DISABLED_MADDY_STRONGPARSER, ItOnlyReplacesUnderscoresAtWordBreaks) +{ + std::string text = "some __text__bla__ testing __it__ out"; + std::string expected = + "some text__bla testing it out"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_STRONGPARSER, ItReplacesUnderscoresWithMultipleWords) +{ + std::string text = "some __text testing it__ out"; + std::string expected = "some text testing it out"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(DISABLED_MADDY_STRONGPARSER, ItAllowsTripleUnderscores) +{ + // I'm not sure if this is standard or not, but this is how the github + // markdown parser behaves. Other things I've seen want it to *not* + // match. + + std::string text = "some ___text testing it__ out"; + std::string expected = "some _text testing it out"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_STRONGPARSER, ItDoesntReplaceUnderscoresInsideCodeBlocks) +{ + std::string text = + "Stuff inside blocks __shouldn't be strong__ at all"; + std::string expected = + "Stuff inside blocks __shouldn't be strong__ at all"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(DISABLED_MADDY_STRONGPARSER, ItDoesNotReplaceUnderscoresInURLs) +{ + std::string text = "[Link Title](http://example.com/what__you__didn't__know)"; + std::string expected = + "[Link Title](http://example.com/what__you__didn't__know)"; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_STRONGPARSER, ItParsesOutsideCodeBlocks) +{ + std::string text = + "Stuff inside blocks __shouldn't be strong__ " + " but outside __should__."; + std::string expected = + "Stuff inside blocks __shouldn't be strong__ " + " but outside should."; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +} + +TEST(MADDY_STRONGPARSER, ItParsesOutsideTickBlocks) +{ + std::string text = + "Stuff inside `blocks __shouldn't be strong__ `" + " but outside __should__."; + std::string expected = + "Stuff inside `blocks __shouldn't be strong__ `" + " but outside should."; + auto strongParser = std::make_shared(); + + strongParser->Parse(text); + + ASSERT_EQ(expected, text); +}