Changes
diff --git a/src/block_parser.cpp b/src/block_parser.cpp
index a83df52..0f83f36 100644
--- a/src/block_parser.cpp
+++ b/src/block_parser.cpp
@@ -577,8 +577,17 @@ void BlockParser::process_line(const std::string& line)
return;
}
+ // If the current tip is a List, we must close it because a List can only
+ // contain ListItems. A paragraph encountered here indicates the list has
+ // ended.
+ if(tip->type == BlockType::List)
+ {
+ close_unmatched_blocks(open_blocks.size() - 2);
+ tip = open_blocks.back().block;
+ }
+
if(tip->type == BlockType::Document || tip->type == BlockType::Quote ||
- tip->type == BlockType::List || tip->type == BlockType::ListItem)
+ tip->type == BlockType::ListItem)
{
auto p = std::make_unique<Block>(BlockType::Paragraph);
Block* p_ptr = p.get();
diff --git a/src/macrodown.cpp b/src/macrodown.cpp
index 568381b..f98ec87 100644
--- a/src/macrodown.cpp
+++ b/src/macrodown.cpp
@@ -17,6 +17,9 @@ MacroDown::MacroDown()
std::unique_ptr<Node> MacroDown::parse(const std::string& input)
{
+ // Inline pre-processing to handle Link Reference Definitions.
+ // Example: [1]: http://b.org
+ // These lines are extracted and stored in a map.
std::string processed_input;
std::map<std::string, std::string> link_refs;
std::regex link_def_re(R"(^[ \t]*\[([^\]]+)\]:[ \t]*([^\s]+)[ \t]*\r?$)");
@@ -33,14 +36,17 @@ std::unique_ptr<Node> MacroDown::parse(const std::string& input)
}
else
{
+ // Reconstruct the input without the definition lines.
if(!first) processed_input += "\n";
processed_input += line;
first = false;
}
}
+ // Substitute reference-style links [text][id] with inline-style [text](url).
for(const auto& pair : link_refs)
{
+ // Escape the ID for use in regex.
std::string id_escaped = std::regex_replace(pair.first, std::regex(R"([-[\]{}()*+?.,\^$|#\s])"), R"(\$&)");
std::regex ref_link_re(R"(\[([^\]]+)\]\[)" + id_escaped + R"(\])");
processed_input = std::regex_replace(processed_input, ref_link_re, "[$1](" + pair.second + ")");
diff --git a/src/parser.cpp b/src/parser.cpp
index ad8a47a..33084db 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -489,8 +489,8 @@ private:
nodes_.push_back(std::make_unique<Node>(std::move(macro)));
}
- // Handles emphasis (*em*) and strong emphasis (**strong**).
- // Recursively parses the content.
+ // Handles emphasis (*em* or _em_) and strong emphasis (**strong** or
+ // __strong__). Recursively parses the content.
bool handleEmphasis()
{
if(input32_[pos_] == '*' || input32_[pos_] == '_')
diff --git a/tests/test_macrodown.cpp b/tests/test_macrodown.cpp
index 653371f..383ecdb 100644
--- a/tests/test_macrodown.cpp
+++ b/tests/test_macrodown.cpp
@@ -111,6 +111,24 @@ TEST(MacroDownTest, CommonMarkCode)
EXPECT_EQ(md.render(*md.parse(indented_input)), indented_expected);
}
+// Verify that a paragraph following a list correctly closes the list block.
+TEST(MacroDownTest, ListFollowedByParagraph)
+{
+ MacroDown md;
+ std::string input = "First paragraph\n\n- aaa\n- bbb\n\nSecond paragraph";
+ std::string expected = "<p>First paragraph</p>\n"
+ "<ul>\n"
+ "<li>\n"
+ "<p>aaa</p>\n"
+ "</li>\n"
+ "<li>\n"
+ "<p>bbb</p>\n"
+ "</li>\n"
+ "</ul>\n"
+ "<p>Second paragraph</p>\n";
+ EXPECT_EQ(md.render(*md.parse(input)), expected);
+}
+
TEST(MacroDownTest, MixedContent)
{
MacroDown md;