Browse Source

bug-fix: headings

- Missing headings with special characters;
- Display only the starting block of headers across multiple blocks;
- HGMarkdownHighlighter:
    - Skip headers without spaces after #s;
    - Fix last-block-header issue;
Le Tan 8 years ago
parent
commit
d943e58f13
4 changed files with 74 additions and 12 deletions
  1. 40 2
      src/hgmarkdownhighlighter.cpp
  2. 9 1
      src/hgmarkdownhighlighter.h
  3. 3 1
      src/vmdeditor.cpp
  4. 22 8
      src/vtableofcontent.cpp

+ 40 - 2
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;
+}

+ 9 - 1
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<int, bool> &HGMarkdownHighlighter::getPotentialPreviewBlocks() const

+ 3 - 1
src/vmdeditor.cpp

@@ -437,7 +437,9 @@ void VMdEditor::updateHeaders(const QVector<VElementRegion> &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)

+ 22 - 8
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 </a>.
+            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 <a> should be ended by </a>" << 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 <a>" << anchor << p_xml.name();
+                        return false;
+                    }
+
+                    if (p_xml.name() == "a") {
+                        break;
+                    }
+                } else if (p_xml.isStartElement()) {
+                    ++nrStart;
                 }
+            }
 
-                VTableOfContentItem header(name, p_level, anchor, p_table.size());
-                p_table.append(header);
-            } else {
+            if (p_xml.hasError()) {
                 // Error
+                qWarning() << "fail to parse an entire <a> 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());