diff --git a/src/hgmarkdownhighlighter.cpp b/src/hgmarkdownhighlighter.cpp index 1545d0f2..4e599965 100644 --- a/src/hgmarkdownhighlighter.cpp +++ b/src/hgmarkdownhighlighter.cpp @@ -209,6 +209,10 @@ void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks) { const HighlightingStyle &style = highlightingStyles[i]; pmh_element *elem_cursor = result[style.type]; + + // pmh_H1 to pmh_H6 is continuous. + bool isHeader = style.type >= pmh_H1 && style.type <= pmh_H6; + while (elem_cursor != NULL) { // elem_cursor->pos and elem_cursor->end is the start @@ -217,6 +221,14 @@ void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks) elem_cursor = elem_cursor->next; continue; } + + // Check header. Skip those headers with no spaces after #s. + if (isHeader + && !isValidHeader(elem_cursor->pos, elem_cursor->end)) { + elem_cursor = elem_cursor->next; + continue; + } + initBlockHighlihgtOne(elem_cursor->pos, elem_cursor->end, i); elem_cursor = elem_cursor->next; } @@ -304,7 +316,8 @@ void HGMarkdownHighlighter::initHeaderRegionsFromResult() for (int i = 0; i < 6; ++i) { pmh_element *elem = result[hx[i]]; while (elem != NULL) { - if (elem->end <= elem->pos) { + if (elem->end <= elem->pos + || !isValidHeader(elem->pos, elem->end)) { elem = elem->next; continue; } @@ -336,10 +349,20 @@ void HGMarkdownHighlighter::initHeaderRegionsFromResult() emit headersUpdated(m_headerRegions); } -void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, unsigned long end, int styleIndex) +void HGMarkdownHighlighter::initBlockHighlihgtOne(unsigned long pos, + unsigned long end, + int styleIndex) { + // When the the highlight element is at the end of document, @end will equals + // to the characterCount. + unsigned int nrChar = (unsigned int)document->characterCount(); + if (end >= nrChar && nrChar > 0) { + end = nrChar - 1; + } + int startBlockNum = document->findBlock(pos).blockNumber(); int endBlockNum = document->findBlock(end).blockNumber(); + for (int i = startBlockNum; i <= endBlockNum; ++i) { QTextBlock block = document->findBlockByNumber(i); @@ -688,3 +711,18 @@ void HGMarkdownHighlighter::highlightChanged() m_completeTimer->stop(); m_completeTimer->start(); } + +bool HGMarkdownHighlighter::isValidHeader(unsigned long p_pos, unsigned long p_end) +{ + // There must exist spaces after #s. + for (unsigned long i = p_pos; i < p_end; ++i) { + QChar ch = document->characterAt(i); + if (ch.isSpace()) { + return true; + } else if (ch != QChar('#')) { + return false; + } + } + + return false; +} diff --git a/src/hgmarkdownhighlighter.h b/src/hgmarkdownhighlighter.h index 5e28ecff..f51132f8 100644 --- a/src/hgmarkdownhighlighter.h +++ b/src/hgmarkdownhighlighter.h @@ -206,8 +206,13 @@ private: void parse(); void parseInternal(); + + // Init highlight elements for all the blocks from parse results. void initBlockHighlightFromResult(int nrBlocks); - void initBlockHighlihgtOne(unsigned long pos, unsigned long end, + + // Init highlight elements for blocks from one parse result. + void initBlockHighlihgtOne(unsigned long pos, + unsigned long end, int styleIndex); // Return true if there are fenced code blocks and it will call rehighlight() later. @@ -234,6 +239,9 @@ private: // Highlight color column in code block. void highlightCodeBlockColorColumn(const QString &p_text); + + // Check if [p_pos, p_end) is a valid header. + bool isValidHeader(unsigned long p_pos, unsigned long p_end); }; inline const QMap &HGMarkdownHighlighter::getPotentialPreviewBlocks() const diff --git a/src/vmdeditor.cpp b/src/vmdeditor.cpp index 65ec0536..ac58bf7a 100644 --- a/src/vmdeditor.cpp +++ b/src/vmdeditor.cpp @@ -437,7 +437,9 @@ void VMdEditor::updateHeaders(const QVector &p_headerRegions) } if (!block.contains(reg.m_endPos - 1)) { - continue; + qWarning() << "header accross multiple blocks, starting from block" + << block.blockNumber() + << block.text(); } if ((block.userState() == HighlightBlockState::Normal) diff --git a/src/vtableofcontent.cpp b/src/vtableofcontent.cpp index 54954991..396b54ff 100644 --- a/src/vtableofcontent.cpp +++ b/src/vtableofcontent.cpp @@ -38,20 +38,34 @@ static bool parseTocLi(QXmlStreamReader &p_xml, if (p_xml.name() == "a") { QString anchor = p_xml.attributes().value("href").toString().mid(1); QString name; - if (p_xml.readNext()) { + // Read till . + int nrStart = 1; + while (p_xml.readNext()) { if (p_xml.tokenString() == "Characters") { - name = p_xml.text().toString(); - } else if (!p_xml.isEndElement()) { - qWarning() << "TOC HTML should be ended by " << p_xml.name(); - return false; - } + name += p_xml.text().toString(); + } else if (p_xml.isEndElement()) { + --nrStart; + if (nrStart < 0) { + qWarning() << "end elements more than start elements in " << anchor << p_xml.name(); + return false; + } - VTableOfContentItem header(name, p_level, anchor, p_table.size()); - p_table.append(header); - } else { + if (p_xml.name() == "a") { + break; + } + } else if (p_xml.isStartElement()) { + ++nrStart; + } + } + + if (p_xml.hasError()) { // Error + qWarning() << "fail to parse an entire element" << anchor << name; return false; } + + VTableOfContentItem header(name, p_level, anchor, p_table.size()); + p_table.append(header); } else if (p_xml.name() == "ul") { // Such as header 3 under header 1 directly VTableOfContentItem header(c_emptyHeaderName, p_level, "", p_table.size());