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 }