File indexing completed on 2024-05-12 05:40:38

0001 /***************************************************************************
0002  * Copyright (C) 2014 by Renaud Guezennec                                   *
0003  * https://rolisteam.org/                                                *
0004  * Copyright (c) 2014-2018 Patrizio Bekerle -- http://www.bekerle.com       *
0005  *                                                                          *
0006  *  This file is part of rolisteam                                          *
0007  *                                                                          *
0008  * rolisteam is free software; you can redistribute it and/or modify        *
0009  * it under the terms of the GNU General Public License as published by     *
0010  * the Free Software Foundation; either version 2 of the License, or        *
0011  * (at your option) any later version.                                      *
0012  *                                                                          *
0013  * rcse is distributed in the hope that it will be useful,                  *
0014  * but WITHOUT ANY WARRANTY; without even the implied warranty of           *
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the             *
0016  * GNU General Public License for more details.                             *
0017  *                                                                          *
0018  * You should have received a copy of the GNU General Public License        *
0019  * along with this program; if not, write to the                            *
0020  * Free Software Foundation, Inc.,                                          *
0021  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.                 *
0022  ***************************************************************************/
0023 #include "markdownhighlighter.h"
0024 #include <QDebug>
0025 #include <QTimer>
0026 
0027 MarkDownHighlighter::MarkDownHighlighter(QTextDocument* parent, HighlightingOptions highlightingOptions)
0028     : QSyntaxHighlighter(parent)
0029 {
0030     m_highlightingOptions= highlightingOptions;
0031     m_timer= new QTimer(this);
0032     connect(m_timer, &QTimer::timeout, this, &MarkDownHighlighter::timerTick);
0033     m_timer->start(1000);
0034 
0035     // initialize the highlighting rules
0036     initHighlightingRules();
0037 
0038     // initialize the text formats
0039     initTextFormats();
0040 }
0041 
0042 void MarkDownHighlighter::timerTick()
0043 {
0044     // re-highlight all dirty blocks
0045     reHighlightDirtyBlocks();
0046 
0047     // emit a signal every second if there was some highlighting done
0048     if(m_highlightingFinished)
0049     {
0050         m_highlightingFinished= false;
0051         emit highlightingFinished();
0052     }
0053 }
0054 
0055 void MarkDownHighlighter::reHighlightDirtyBlocks()
0056 {
0057     while(m_dirtyTextBlocks.count() > 0)
0058     {
0059         QTextBlock block= m_dirtyTextBlocks.at(0);
0060         rehighlightBlock(block);
0061         m_dirtyTextBlocks.removeFirst();
0062     }
0063 }
0064 
0065 void MarkDownHighlighter::clearDirtyBlocks()
0066 {
0067     m_dirtyTextBlocks.clear();
0068 }
0069 
0070 void MarkDownHighlighter::addDirtyBlock(QTextBlock block)
0071 {
0072     if(!m_dirtyTextBlocks.contains(block))
0073     {
0074         m_dirtyTextBlocks.append(block);
0075     }
0076 }
0077 void MarkDownHighlighter::initHighlightingRules()
0078 {
0079     HighlightingRule rule;
0080 
0081     // highlight the reference of reference links
0082     rule= HighlightingRule();
0083     rule.pattern= QRegularExpression("^\\[.+?\\]: \\w+://.+$");
0084     rule.state= HighlighterState::MaskedSyntax;
0085     m_highlightingRulesPre.append(rule);
0086 
0087     // highlight unordered lists
0088     rule= HighlightingRule();
0089     rule.pattern= QRegularExpression("^\\s*[-*+]\\s");
0090     rule.state= HighlighterState::List;
0091     rule.useStateAsCurrentBlockState= true;
0092     m_highlightingRulesPre.append(rule);
0093 
0094     // highlight ordered lists
0095     rule.pattern= QRegularExpression("^\\s*\\d+\\.\\s");
0096     m_highlightingRulesPre.append(rule);
0097 
0098     // highlight block quotes
0099     rule= HighlightingRule();
0100     rule.pattern= QRegularExpression(m_highlightingOptions.testFlag(HighlightingOption::FullyHighlightedBlockQuote) ?
0101                                          "^\\s*(>\\s*.+)" :
0102                                          "^\\s*(>\\s*)+");
0103     rule.state= HighlighterState::BlockQuote;
0104     m_highlightingRulesPre.append(rule);
0105 
0106     // highlight horizontal rulers
0107     rule= HighlightingRule();
0108     rule.pattern= QRegularExpression("^([*\\-_]\\s?){3,}$");
0109     rule.state= HighlighterState::HorizontalRuler;
0110     m_highlightingRulesPre.append(rule);
0111 
0112     // highlight tables without starting |
0113     // we drop that for now, it's far too messy to deal with
0114     //    rule = HighlightingRule();
0115     //    rule.pattern = QRegularExpression("^.+? \\| .+? \\| .+$");
0116     //    rule.state = HighlighterState::Table;
0117     //    _highlightingRulesPre.append(rule);
0118 
0119     /*
0120      * highlight italic
0121      * this goes before bold so that bold can overwrite italic
0122      *
0123      * text to test:
0124      * **bold** normal **bold**
0125      * *start of line* normal
0126      * normal *end of line*
0127      * * list item *italic*
0128      */
0129     rule= HighlightingRule();
0130     // we don't allow a space after the starting * to prevent problems with
0131     // unordered lists starting with a *
0132     rule.pattern= QRegularExpression("(^|[^\\*\\b])(\\*([^\\* ][^\\*]*?)\\*)([^\\*\\b]|$)");
0133     rule.state= HighlighterState::Italic;
0134     rule.maskedGroup= 2;
0135     rule.capturingGroup= 3;
0136     m_highlightingRulesAfter.append(rule);
0137 
0138     rule.maskedGroup= 0;
0139     rule.capturingGroup= 1;
0140     rule.pattern= QRegularExpression("\\b_([^_]+)_\\b");
0141     m_highlightingRulesAfter.append(rule);
0142 
0143     // highlight bold
0144     rule.pattern= QRegularExpression("\\B\\*{2}(.+?)\\*{2}\\B");
0145     rule.state= HighlighterState::Bold;
0146     rule.capturingGroup= 1;
0147     m_highlightingRulesAfter.append(rule);
0148     rule.pattern= QRegularExpression("\\b__(.+?)__\\b");
0149     m_highlightingRulesAfter.append(rule);
0150 
0151     // highlight urls
0152     rule= HighlightingRule();
0153     rule.state= HighlighterState::Link;
0154 
0155     // highlight urls without any other markup
0156     rule.pattern= QRegularExpression("\\b\\w+?:\\/\\/[^\\s]+");
0157     rule.capturingGroup= 0;
0158     m_highlightingRulesAfter.append(rule);
0159 
0160     //    rule.pattern = QRegularExpression("<(.+?:\\/\\/.+?)>");
0161     rule.pattern= QRegularExpression("<([^\\s`][^`]*?[^\\s`])>");
0162     rule.capturingGroup= 1;
0163     m_highlightingRulesAfter.append(rule);
0164 
0165     // highlight urls with title
0166     rule.pattern= QRegularExpression("\\[([^\\[\\]]+)\\]\\((\\S+|.+?)\\)\\B");
0167     m_highlightingRulesAfter.append(rule);
0168 
0169     // highlight urls with empty title
0170     rule.pattern= QRegularExpression("\\[\\]\\((.+?)\\)");
0171     m_highlightingRulesAfter.append(rule);
0172 
0173     // highlight email links
0174     rule.pattern= QRegularExpression("<(.+?@.+?)>");
0175     m_highlightingRulesAfter.append(rule);
0176 
0177     // highlight reference links
0178     rule.pattern= QRegularExpression("\\[(.+?)\\]\\s?\\[.+?\\]");
0179     m_highlightingRulesAfter.append(rule);
0180 
0181     // highlight images with text
0182     rule= HighlightingRule();
0183     rule.pattern= QRegularExpression("!\\[(.+?)\\]\\(.+?\\)");
0184     rule.state= HighlighterState::Image;
0185     rule.capturingGroup= 1;
0186     m_highlightingRulesAfter.append(rule);
0187 
0188     // highlight images without text
0189     rule.pattern= QRegularExpression("!\\[\\]\\((.+?)\\)");
0190     m_highlightingRulesAfter.append(rule);
0191 
0192     // highlight images links
0193     rule= HighlightingRule();
0194     rule.state= HighlighterState::Link;
0195     rule.pattern= QRegularExpression("\\[!\\[(.+?)\\]\\(.+?\\)\\]\\(.+?\\)");
0196     rule.capturingGroup= 1;
0197     m_highlightingRulesAfter.append(rule);
0198 
0199     // highlight images links without text
0200     rule.pattern= QRegularExpression("\\[!\\[\\]\\(.+?\\)\\]\\((.+?)\\)");
0201     m_highlightingRulesAfter.append(rule);
0202 
0203     // highlight inline code
0204     rule= HighlightingRule();
0205     rule.pattern= QRegularExpression("`(.+?)`");
0206     rule.state= HighlighterState::InlineCodeBlock;
0207     rule.capturingGroup= 1;
0208     m_highlightingRulesAfter.append(rule);
0209 
0210     // highlight code blocks with four spaces or tabs in front of them
0211     // and no list character after that
0212     rule= HighlightingRule();
0213     rule.pattern= QRegularExpression("^((\\t)|( {4,})).+$");
0214     rule.state= HighlighterState::CodeBlock;
0215     rule.disableIfCurrentStateIsSet= true;
0216     m_highlightingRulesAfter.append(rule);
0217 
0218     // highlight inline comments
0219     rule= HighlightingRule();
0220     rule.pattern= QRegularExpression("<!\\-\\-(.+?)\\-\\->");
0221     rule.state= HighlighterState::Comment;
0222     rule.capturingGroup= 1;
0223     m_highlightingRulesAfter.append(rule);
0224 
0225     // highlight comments for Rmarkdown for academic papers
0226     rule.pattern= QRegularExpression("^\\[.+?\\]: # \\(.+?\\)$");
0227     m_highlightingRulesAfter.append(rule);
0228 
0229     // highlight tables with starting |
0230     rule= HighlightingRule();
0231     rule.pattern= QRegularExpression("^\\|.+?\\|$");
0232     rule.state= HighlighterState::Table;
0233     m_highlightingRulesAfter.append(rule);
0234 }
0235 
0236 void MarkDownHighlighter::initTextFormats(int defaultFontSize)
0237 {
0238     QTextCharFormat format;
0239 
0240     // set character formats for headlines
0241     format= QTextCharFormat();
0242     format.setForeground(QBrush(QColor(0, 49, 110)));
0243     format.setBackground(QBrush(QColor(230, 230, 240)));
0244     format.setFontWeight(QFont::Bold);
0245     format.setFontPointSize(defaultFontSize * 1.0);
0246     m_formats[H1]= format;
0247     format.setFontPointSize(defaultFontSize * 1.0);
0248     m_formats[H2]= format;
0249     format.setFontPointSize(defaultFontSize * 1.0);
0250     m_formats[H3]= format;
0251     format.setFontPointSize(defaultFontSize * 1.0);
0252     m_formats[H4]= format;
0253     format.setFontPointSize(defaultFontSize * 1.0);
0254     m_formats[H5]= format;
0255     format.setFontPointSize(defaultFontSize * 1.0);
0256     m_formats[H6]= format;
0257     format.setFontPointSize(defaultFontSize);
0258 
0259     // set character format for horizontal rulers
0260     format= QTextCharFormat();
0261     format.setForeground(QBrush(Qt::darkGray));
0262     format.setBackground(QBrush(Qt::lightGray));
0263     m_formats[HorizontalRuler]= format;
0264 
0265     // set character format for lists
0266     format= QTextCharFormat();
0267     format.setForeground(QBrush(QColor(163, 0, 123)));
0268     m_formats[List]= format;
0269 
0270     // set character format for links
0271     format= QTextCharFormat();
0272     format.setForeground(QBrush(QColor(255, 128, 0)));
0273     format.setBackground(QBrush(QColor(255, 233, 211)));
0274     m_formats[Link]= format;
0275 
0276     // set character format for images
0277     format= QTextCharFormat();
0278     format.setForeground(QBrush(QColor(0, 191, 0)));
0279     format.setBackground(QBrush(QColor(228, 255, 228)));
0280     m_formats[Image]= format;
0281 
0282     // set character format for code blocks
0283     format= QTextCharFormat();
0284     format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
0285     format.setForeground(QBrush(Qt::darkGreen));
0286     format.setBackground(QBrush(QColor(217, 231, 217)));
0287     m_formats[CodeBlock]= format;
0288     m_formats[InlineCodeBlock]= format;
0289 
0290     // set character format for italic
0291     format= QTextCharFormat();
0292     format.setFontWeight(QFont::StyleItalic);
0293     format.setForeground(QBrush(QColor(0, 87, 174)));
0294     m_formats[Italic]= format;
0295 
0296     // set character format for bold
0297     format= QTextCharFormat();
0298     format.setFontWeight(QFont::Bold);
0299     format.setForeground(QBrush(QColor(0, 66, 138)));
0300     m_formats[Bold]= format;
0301 
0302     // set character format for comments
0303     format= QTextCharFormat();
0304     format.setForeground(QBrush(Qt::gray));
0305     m_formats[Comment]= format;
0306 
0307     // set character format for masked syntax
0308     format= QTextCharFormat();
0309     format.setForeground(QBrush(QColor(204, 204, 204)));
0310     m_formats[MaskedSyntax]= format;
0311 
0312     // set character format for tables
0313     format= QTextCharFormat();
0314     format.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
0315     format.setForeground(QBrush(QColor(100, 148, 73)));
0316     m_formats[Table]= format;
0317 
0318     // set character format for block quotes
0319     format= QTextCharFormat();
0320     format.setForeground(QBrush(QColor(Qt::darkRed)));
0321     m_formats[BlockQuote]= format;
0322 }
0323 
0324 void MarkDownHighlighter::setTextFormats(QHash<HighlighterState, QTextCharFormat> formats)
0325 {
0326     m_formats= formats;
0327 }
0328 
0329 void MarkDownHighlighter::setTextFormat(HighlighterState state, QTextCharFormat format)
0330 {
0331     m_formats[state]= format;
0332 }
0333 
0334 void MarkDownHighlighter::highlightBlock(const QString& text)
0335 {
0336     setCurrentBlockState(HighlighterState::NoState);
0337     currentBlock().setUserState(HighlighterState::NoState);
0338     highlightMarkdown(text);
0339     m_highlightingFinished= true;
0340 }
0341 
0342 void MarkDownHighlighter::highlightMarkdown(QString text)
0343 {
0344     if(!text.isEmpty())
0345     {
0346         highlightAdditionalRules(m_highlightingRulesPre, text);
0347 
0348         // needs to be called after the horizontal ruler highlighting
0349         highlightHeadline(text);
0350 
0351         highlightAdditionalRules(m_highlightingRulesAfter, text);
0352     }
0353 
0354     highlightCommentBlock(text);
0355     highlightCodeBlock(text);
0356 }
0357 
0358 void MarkDownHighlighter::highlightHeadline(QString text)
0359 {
0360     QRegularExpression regex("^(#+)\\s+(.+?)$");
0361     QRegularExpressionMatch match= regex.match(text);
0362     QTextCharFormat& maskedFormat= m_formats[HighlighterState::MaskedSyntax];
0363 
0364     // check for headline blocks with # in front of them
0365     if(match.hasMatch())
0366     {
0367         int count= match.captured(1).count();
0368 
0369         // we just have H1 to H6
0370         count= qMin(count, 6);
0371 
0372         HighlighterState state= HighlighterState(HighlighterState::H1 + count - 1);
0373 
0374         QTextCharFormat& format= m_formats[state];
0375         QTextCharFormat& currentMaskedFormat= maskedFormat;
0376 
0377         // set the font size from the current rule's font format
0378         maskedFormat.setFontPointSize(format.fontPointSize());
0379 
0380         // first highlight everything as MaskedSyntax
0381         setFormat(match.capturedStart(), match.capturedLength(), currentMaskedFormat);
0382 
0383         // then highlight with the real format
0384         setFormat(0, text.size(), m_formats[state]);
0385 
0386         // set a margin for the current block
0387         setCurrentBlockMargin(state);
0388 
0389         setCurrentBlockState(state);
0390         currentBlock().setUserState(state);
0391         return;
0392     }
0393 
0394     // take care of ==== and ---- headlines
0395     QRegularExpression patternH1= QRegularExpression("^=+$");
0396     QRegularExpression patternH2= QRegularExpression("^-+$");
0397     QTextBlock previousBlock= currentBlock().previous();
0398     QString previousText= previousBlock.text();
0399     previousText.trimmed().remove(QRegularExpression("[=-]"));
0400 
0401     // check for ===== after a headline text and highlight as H1
0402     if(patternH1.match(text).hasMatch())
0403     {
0404         if(((previousBlockState() == HighlighterState::H1) || (previousBlockState() == HighlighterState::NoState))
0405            && (previousText.length() > 0))
0406         {
0407             // set the font size from the current rule's font format
0408             maskedFormat.setFontPointSize(m_formats[HighlighterState::H1].fontPointSize());
0409 
0410             setFormat(0, text.length(), maskedFormat);
0411             setCurrentBlockState(HighlighterState::HeadlineEnd);
0412             previousBlock.setUserState(HighlighterState::H1);
0413 
0414             // set a margin for the current block
0415             setCurrentBlockMargin(HighlighterState::H1);
0416 
0417             // we want to re-highlight the previous block
0418             // this must not done directly, but with a queue, otherwise it
0419             // will crash
0420             // setting the character format of the previous text, because this
0421             // causes text to be formatted the same way when writing after
0422             // the text
0423             addDirtyBlock(previousBlock);
0424         }
0425 
0426         return;
0427     }
0428 
0429     // check for ----- after a headline text and highlight as H2
0430     if(patternH2.match(text).hasMatch())
0431     {
0432         if(((previousBlockState() == HighlighterState::H2) || (previousBlockState() == HighlighterState::NoState))
0433            && (previousText.length() > 0))
0434         {
0435             // set the font size from the current rule's font format
0436             maskedFormat.setFontPointSize(m_formats[HighlighterState::H2].fontPointSize());
0437 
0438             setFormat(0, text.length(), maskedFormat);
0439             setCurrentBlockState(HighlighterState::HeadlineEnd);
0440             previousBlock.setUserState(HighlighterState::H2);
0441 
0442             // set a margin for the current block
0443             setCurrentBlockMargin(HighlighterState::H2);
0444 
0445             // we want to re-highlight the previous block
0446             addDirtyBlock(previousBlock);
0447         }
0448 
0449         return;
0450     }
0451 
0452     QTextBlock nextBlock= currentBlock().next();
0453     QString nextBlockText= nextBlock.text();
0454 
0455     // highlight as H1 if next block is =====
0456     if(patternH1.match(nextBlockText).hasMatch() || patternH2.match(nextBlockText).hasMatch())
0457     {
0458         setFormat(0, text.length(), m_formats[HighlighterState::H1]);
0459         setCurrentBlockState(HighlighterState::H1);
0460         currentBlock().setUserState(HighlighterState::H1);
0461     }
0462 
0463     // highlight as H2 if next block is -----
0464     if(patternH2.match(nextBlockText).hasMatch())
0465     {
0466         setFormat(0, text.length(), m_formats[HighlighterState::H2]);
0467         setCurrentBlockState(HighlighterState::H2);
0468         currentBlock().setUserState(HighlighterState::H2);
0469     }
0470 }
0471 
0472 void MarkDownHighlighter::setCurrentBlockMargin(MarkDownHighlighter::HighlighterState state)
0473 {
0474     // this is currently disabled because it causes multiple problems:
0475     // - it prevents "undo" in headlines
0476     //   https://github.com/pbek/QOwnNotes/issues/520
0477     // - invisible lines at the end of a note
0478     //   https://github.com/pbek/QOwnNotes/issues/667
0479     // - a crash when reaching the invisible lines when the current line is
0480     //   highlighted
0481     //   https://github.com/pbek/QOwnNotes/issues/701
0482     return;
0483 
0484     qreal margin;
0485 
0486     switch(state)
0487     {
0488     case HighlighterState::H1:
0489         margin= 5;
0490         break;
0491     case HighlighterState::H2:
0492     case HighlighterState::H3:
0493     case HighlighterState::H4:
0494     case HighlighterState::H5:
0495     case HighlighterState::H6:
0496         margin= 3;
0497         break;
0498     default:
0499         return;
0500     }
0501 
0502     QTextBlockFormat blockFormat= currentBlock().blockFormat();
0503     blockFormat.setTopMargin(2);
0504     blockFormat.setBottomMargin(margin);
0505 
0506     // this prevents "undo" in headlines!
0507     QTextCursor* myCursor= new QTextCursor(currentBlock());
0508     myCursor->setBlockFormat(blockFormat);
0509 }
0510 
0511 void MarkDownHighlighter::highlightCodeBlock(QString text)
0512 {
0513     QRegularExpression regex("^```\\w*?$");
0514     QRegularExpressionMatch match= regex.match(text);
0515 
0516     if(match.hasMatch())
0517     {
0518         setCurrentBlockState(previousBlockState() == HighlighterState::CodeBlock ? HighlighterState::CodeBlockEnd :
0519                                                                                    HighlighterState::CodeBlock);
0520         // set the font size from the current rule's font format
0521         QTextCharFormat& maskedFormat= m_formats[HighlighterState::MaskedSyntax];
0522         maskedFormat.setFontPointSize(m_formats[HighlighterState::CodeBlock].fontPointSize());
0523 
0524         setFormat(0, text.length(), maskedFormat);
0525     }
0526     else if(previousBlockState() == HighlighterState::CodeBlock)
0527     {
0528         setCurrentBlockState(HighlighterState::CodeBlock);
0529         setFormat(0, text.length(), m_formats[HighlighterState::CodeBlock]);
0530     }
0531 }
0532 
0533 void MarkDownHighlighter::highlightCommentBlock(QString text)
0534 {
0535     bool highlight= false;
0536     text= text.trimmed();
0537     QString startText= "<!--";
0538     QString endText= "-->";
0539 
0540     // we will skip this case because that is an inline comment and causes
0541     // troubles here
0542     if(text.startsWith(startText) && text.contains(endText))
0543     {
0544         return;
0545     }
0546 
0547     if(text.startsWith(startText) || (!text.endsWith(endText) && (previousBlockState() == HighlighterState::Comment)))
0548     {
0549         setCurrentBlockState(HighlighterState::Comment);
0550         highlight= true;
0551     }
0552     else if(text.endsWith(endText))
0553     {
0554         highlight= true;
0555     }
0556 
0557     if(highlight)
0558     {
0559         setFormat(0, text.length(), m_formats[HighlighterState::Comment]);
0560     }
0561 }
0562 
0563 void MarkDownHighlighter::highlightAdditionalRules(QVector<HighlightingRule>& rules, QString text)
0564 {
0565     QTextCharFormat& maskedFormat= m_formats[HighlighterState::MaskedSyntax];
0566 
0567     for(const auto& rule : rules)
0568     {
0569         // continue if an other current block state was already set if
0570         // disableIfCurrentStateIsSet is set
0571         if(rule.disableIfCurrentStateIsSet && (currentBlockState() != HighlighterState::NoState))
0572         {
0573             continue;
0574         }
0575 
0576         QRegularExpression expression(rule.pattern);
0577         QRegularExpressionMatchIterator iterator= expression.globalMatch(text);
0578         int capturingGroup= rule.capturingGroup;
0579         int maskedGroup= rule.maskedGroup;
0580         QTextCharFormat& format= m_formats[rule.state];
0581 
0582         // store the current block state if useStateAsCurrentBlockState
0583         // is set
0584         if(iterator.hasNext() && rule.useStateAsCurrentBlockState)
0585         {
0586             setCurrentBlockState(rule.state);
0587         }
0588 
0589         // find and format all occurrences
0590         while(iterator.hasNext())
0591         {
0592             QRegularExpressionMatch match= iterator.next();
0593 
0594             // if there is a capturingGroup set then first highlight
0595             // everything as MaskedSyntax and highlight capturingGroup
0596             // with the real format
0597             if(capturingGroup > 0)
0598             {
0599                 QTextCharFormat& currentMaskedFormat= maskedFormat;
0600                 // set the font size from the current rule's font format
0601                 maskedFormat.setFontPointSize(format.fontPointSize());
0602 
0603                 setFormat(match.capturedStart(maskedGroup), match.capturedLength(maskedGroup), currentMaskedFormat);
0604             }
0605 
0606             setFormat(match.capturedStart(capturingGroup), match.capturedLength(capturingGroup), format);
0607         }
0608     }
0609 }
0610 
0611 void MarkDownHighlighter::setHighlightingOptions(HighlightingOptions options)
0612 {
0613     m_highlightingOptions= options;
0614 }