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;
This commit is contained in:
Le Tan 2017-11-24 19:56:58 +08:00
parent cb6338ecf6
commit d943e58f13
4 changed files with 75 additions and 13 deletions

View File

@ -209,6 +209,10 @@ void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks)
{ {
const HighlightingStyle &style = highlightingStyles[i]; const HighlightingStyle &style = highlightingStyles[i];
pmh_element *elem_cursor = result[style.type]; 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) while (elem_cursor != NULL)
{ {
// elem_cursor->pos and elem_cursor->end is the start // elem_cursor->pos and elem_cursor->end is the start
@ -217,6 +221,14 @@ void HGMarkdownHighlighter::initBlockHighlightFromResult(int nrBlocks)
elem_cursor = elem_cursor->next; elem_cursor = elem_cursor->next;
continue; 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); initBlockHighlihgtOne(elem_cursor->pos, elem_cursor->end, i);
elem_cursor = elem_cursor->next; elem_cursor = elem_cursor->next;
} }
@ -304,7 +316,8 @@ void HGMarkdownHighlighter::initHeaderRegionsFromResult()
for (int i = 0; i < 6; ++i) { for (int i = 0; i < 6; ++i) {
pmh_element *elem = result[hx[i]]; pmh_element *elem = result[hx[i]];
while (elem != NULL) { while (elem != NULL) {
if (elem->end <= elem->pos) { if (elem->end <= elem->pos
|| !isValidHeader(elem->pos, elem->end)) {
elem = elem->next; elem = elem->next;
continue; continue;
} }
@ -336,10 +349,20 @@ void HGMarkdownHighlighter::initHeaderRegionsFromResult()
emit headersUpdated(m_headerRegions); 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 startBlockNum = document->findBlock(pos).blockNumber();
int endBlockNum = document->findBlock(end).blockNumber(); int endBlockNum = document->findBlock(end).blockNumber();
for (int i = startBlockNum; i <= endBlockNum; ++i) for (int i = startBlockNum; i <= endBlockNum; ++i)
{ {
QTextBlock block = document->findBlockByNumber(i); QTextBlock block = document->findBlockByNumber(i);
@ -688,3 +711,18 @@ void HGMarkdownHighlighter::highlightChanged()
m_completeTimer->stop(); m_completeTimer->stop();
m_completeTimer->start(); 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;
}

View File

@ -206,8 +206,13 @@ private:
void parse(); void parse();
void parseInternal(); void parseInternal();
// Init highlight elements for all the blocks from parse results.
void initBlockHighlightFromResult(int nrBlocks); 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); int styleIndex);
// Return true if there are fenced code blocks and it will call rehighlight() later. // 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. // Highlight color column in code block.
void highlightCodeBlockColorColumn(const QString &p_text); 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 inline const QMap<int, bool> &HGMarkdownHighlighter::getPotentialPreviewBlocks() const

View File

@ -437,7 +437,9 @@ void VMdEditor::updateHeaders(const QVector<VElementRegion> &p_headerRegions)
} }
if (!block.contains(reg.m_endPos - 1)) { 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) if ((block.userState() == HighlightBlockState::Normal)

View File

@ -38,20 +38,34 @@ static bool parseTocLi(QXmlStreamReader &p_xml,
if (p_xml.name() == "a") { if (p_xml.name() == "a") {
QString anchor = p_xml.attributes().value("href").toString().mid(1); QString anchor = p_xml.attributes().value("href").toString().mid(1);
QString name; QString name;
if (p_xml.readNext()) { // Read till </a>.
int nrStart = 1;
while (p_xml.readNext()) {
if (p_xml.tokenString() == "Characters") { if (p_xml.tokenString() == "Characters") {
name = p_xml.text().toString(); name += p_xml.text().toString();
} else if (!p_xml.isEndElement()) { } else if (p_xml.isEndElement()) {
qWarning() << "TOC HTML <a> should be ended by </a>" << p_xml.name(); --nrStart;
return false; if (nrStart < 0) {
} qWarning() << "end elements more than start elements in <a>" << anchor << p_xml.name();
return false;
}
VTableOfContentItem header(name, p_level, anchor, p_table.size()); if (p_xml.name() == "a") {
p_table.append(header); break;
} else { }
} else if (p_xml.isStartElement()) {
++nrStart;
}
}
if (p_xml.hasError()) {
// Error // Error
qWarning() << "fail to parse an entire <a> element" << anchor << name;
return false; return false;
} }
VTableOfContentItem header(name, p_level, anchor, p_table.size());
p_table.append(header);
} else if (p_xml.name() == "ul") { } else if (p_xml.name() == "ul") {
// Such as header 3 under header 1 directly // Such as header 3 under header 1 directly
VTableOfContentItem header(c_emptyHeaderName, p_level, "", p_table.size()); VTableOfContentItem header(c_emptyHeaderName, p_level, "", p_table.size());