+12
-2
@@ -19,6 +19,15 @@ static std::string trimLeft(const std::string& s) {
|
||||
return s.substr(i);
|
||||
}
|
||||
|
||||
static uint8_t parseListIndentLevel(size_t leadingSpaces) {
|
||||
// Top-level list markers may be preceded by up to 3 spaces.
|
||||
// Nested list items require at least 4 spaces before the marker.
|
||||
if (leadingSpaces < TAB_WIDTH) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint8_t>((leadingSpaces - TAB_WIDTH) / TAB_WIDTH + 1);
|
||||
}
|
||||
|
||||
static bool isWordChar(char c) { return std::isalnum(static_cast<unsigned char>(c)) || c == '_'; }
|
||||
|
||||
static bool isUnderscoreEmphasis(const std::string& text, size_t pos, size_t count) {
|
||||
@@ -249,7 +258,8 @@ ParsedLine parseLine(const std::string& rawLine, bool inCodeBlock) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Count leading whitespace for nesting level before trimming
|
||||
// Count leading whitespace for nesting level before trimming.
|
||||
// Up to 3 spaces before a list marker are still top-level in CommonMark.
|
||||
size_t leadingSpaces = 0;
|
||||
for (size_t i = 0; i < rawLine.size(); i++) {
|
||||
if (rawLine[i] == ' ')
|
||||
@@ -259,7 +269,7 @@ ParsedLine parseLine(const std::string& rawLine, bool inCodeBlock) {
|
||||
else
|
||||
break;
|
||||
}
|
||||
result.indentLevel = static_cast<uint8_t>(leadingSpaces / TAB_WIDTH);
|
||||
result.indentLevel = parseListIndentLevel(leadingSpaces);
|
||||
|
||||
std::string trimmed = trimLeft(rawLine);
|
||||
|
||||
|
||||
@@ -335,13 +335,18 @@ bool MdReaderActivity::wordWrapParsedLine(const MdParser::ParsedLine& parsed, in
|
||||
const int availableWidth = viewportWidth - indent;
|
||||
if (availableWidth <= 0) return true;
|
||||
|
||||
// Build a flat list of all spans, prepending the list prefix if present
|
||||
// Build a flat list of all spans, prepending the list prefix if present.
|
||||
std::vector<MdParser::Span> allSpans;
|
||||
if (!parsed.listPrefix.empty()) {
|
||||
allSpans.push_back({parsed.listPrefix, EpdFontFamily::REGULAR});
|
||||
}
|
||||
allSpans.insert(allSpans.end(), parsed.spans.begin(), parsed.spans.end());
|
||||
|
||||
const int listPrefixIndent =
|
||||
!parsed.listPrefix.empty()
|
||||
? renderer.getTextAdvanceX(cachedFontId, parsed.listPrefix.c_str(), EpdFontFamily::REGULAR)
|
||||
: 0;
|
||||
|
||||
// Check if everything fits on one line
|
||||
int totalWidth = measureSpans(allSpans);
|
||||
if (totalWidth <= availableWidth) {
|
||||
@@ -359,6 +364,7 @@ bool MdReaderActivity::wordWrapParsedLine(const MdParser::ParsedLine& parsed, in
|
||||
currentLine.isCodeBlock = isCodeBlock;
|
||||
int currentWidth = 0;
|
||||
bool fullyConsumed = true;
|
||||
bool firstLine = true;
|
||||
|
||||
for (size_t si = 0; si < allSpans.size(); si++) {
|
||||
const auto& span = allSpans[si];
|
||||
@@ -415,8 +421,10 @@ bool MdReaderActivity::wordWrapParsedLine(const MdParser::ParsedLine& parsed, in
|
||||
}
|
||||
} else {
|
||||
outLines.push_back(std::move(currentLine));
|
||||
firstLine = false;
|
||||
currentLine = RenderedLine();
|
||||
currentLine.indent = indent;
|
||||
currentLine.indent = indent + (firstLine ? 0 : listPrefixIndent);
|
||||
currentLine.isCodeBlock = isCodeBlock;
|
||||
currentWidth = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -424,8 +432,9 @@ bool MdReaderActivity::wordWrapParsedLine(const MdParser::ParsedLine& parsed, in
|
||||
|
||||
currentLine.spans.push_back({remaining.substr(0, breakPos), style});
|
||||
outLines.push_back(std::move(currentLine));
|
||||
firstLine = false;
|
||||
currentLine = RenderedLine();
|
||||
currentLine.indent = indent;
|
||||
currentLine.indent = indent + (firstLine ? 0 : listPrefixIndent);
|
||||
currentLine.isCodeBlock = isCodeBlock;
|
||||
currentWidth = 0;
|
||||
|
||||
|
||||
@@ -78,6 +78,26 @@ void testUnderscoreBoldWorks() {
|
||||
PASS();
|
||||
}
|
||||
|
||||
void testNestedUnorderedListIndentLevel() {
|
||||
printf("testNestedUnorderedListIndentLevel...\n");
|
||||
auto parsed = MdParser::parseLine(" - nested item", false);
|
||||
ASSERT_EQ(parsed.blockType, MdParser::BlockType::UnorderedList);
|
||||
ASSERT_EQ(parsed.listPrefix, "\xe2\x80\xa2 ");
|
||||
ASSERT_EQ(parsed.indentLevel, 1);
|
||||
ASSERT_EQ(flattenText(parsed.spans), "nested item");
|
||||
PASS();
|
||||
}
|
||||
|
||||
void testNestedOrderedListIndentLevel() {
|
||||
printf("testNestedOrderedListIndentLevel...\n");
|
||||
auto parsed = MdParser::parseLine(" 1. nested ordered", false);
|
||||
ASSERT_EQ(parsed.blockType, MdParser::BlockType::OrderedList);
|
||||
ASSERT_EQ(parsed.listPrefix, "1. ");
|
||||
ASSERT_EQ(parsed.indentLevel, 2);
|
||||
ASSERT_EQ(flattenText(parsed.spans), "nested ordered");
|
||||
PASS();
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("=== Markdown Parser Tests ===\n\n");
|
||||
|
||||
@@ -86,6 +106,8 @@ int main() {
|
||||
testUnderscoreEmphasisStillWorks();
|
||||
testAsteriskEmphasisStillWorks();
|
||||
testUnderscoreBoldWorks();
|
||||
testNestedUnorderedListIndentLevel();
|
||||
testNestedOrderedListIndentLevel();
|
||||
|
||||
printf("\n=== Results: %d passed, %d failed ===\n", testsPassed, testsFailed);
|
||||
return testsFailed > 0 ? 1 : 0;
|
||||
|
||||
Reference in New Issue
Block a user