pegmarkdownhighlighter.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. #include "pegmarkdownhighlighter.h"
  2. #include <QTextDocument>
  3. #include <QTimer>
  4. #include "pegparser.h"
  5. #include "vconfigmanager.h"
  6. #include "utils/vutils.h"
  7. #include "utils/veditutils.h"
  8. extern VConfigManager *g_config;
  9. PegMarkdownHighlighter::PegMarkdownHighlighter(QTextDocument *p_doc)
  10. : QSyntaxHighlighter(p_doc),
  11. m_doc(p_doc),
  12. m_timeStamp(0),
  13. m_parser(NULL),
  14. m_parserExts(pmh_EXT_NOTES | pmh_EXT_STRIKE | pmh_EXT_FRONTMATTER | pmh_EXT_MARK)
  15. {
  16. }
  17. void PegMarkdownHighlighter::init(const QVector<HighlightingStyle> &p_styles,
  18. const QHash<QString, QTextCharFormat> &p_codeBlockStyles,
  19. bool p_mathjaxEnabled,
  20. int p_timerInterval)
  21. {
  22. m_styles = p_styles;
  23. m_codeBlockStyles = p_codeBlockStyles;
  24. if (p_mathjaxEnabled) {
  25. m_parserExts |= pmh_EXT_MATH;
  26. }
  27. m_codeBlockFormat.setForeground(QBrush(Qt::darkYellow));
  28. for (int index = 0; index < m_styles.size(); ++index) {
  29. switch (m_styles[index].type) {
  30. case pmh_FENCEDCODEBLOCK:
  31. m_codeBlockFormat = m_styles[index].format;
  32. break;
  33. default:
  34. break;
  35. }
  36. }
  37. m_colorColumnFormat = m_codeBlockFormat;
  38. m_colorColumnFormat.setForeground(QColor(g_config->getEditorColorColumnFg()));
  39. m_colorColumnFormat.setBackground(QColor(g_config->getEditorColorColumnBg()));
  40. m_result.reset(new PegHighlighterResult());
  41. m_fastResult.reset(new PegHighlighterFastResult());
  42. m_parser = new PegParser(this);
  43. connect(m_parser, &PegParser::parseResultReady,
  44. this, &PegMarkdownHighlighter::handleParseResult);
  45. m_timer = new QTimer(this);
  46. m_timer->setSingleShot(true);
  47. m_timer->setInterval(p_timerInterval);
  48. connect(m_timer, &QTimer::timeout,
  49. this, [this]() {
  50. startParse();
  51. });
  52. m_fastParseTimer = new QTimer(this);
  53. m_fastParseTimer->setSingleShot(true);
  54. m_fastParseTimer->setInterval(50);
  55. connect(m_fastParseTimer, &QTimer::timeout,
  56. this, [this]() {
  57. QSharedPointer<PegHighlighterFastResult> result(m_fastResult);
  58. if (!result->matched(m_timeStamp) || m_result->matched(m_timeStamp)) {
  59. return;
  60. }
  61. const QVector<QVector<HLUnit>> &hls = result->m_blocksHighlights;
  62. for (int i = 0; i < hls.size(); ++i) {
  63. if (!hls[i].isEmpty()) {
  64. rehighlightBlock(m_doc->findBlockByNumber(i));
  65. }
  66. }
  67. });
  68. connect(m_doc, &QTextDocument::contentsChange,
  69. this, &PegMarkdownHighlighter::handleContentsChange);
  70. }
  71. void PegMarkdownHighlighter::highlightBlock(const QString &p_text)
  72. {
  73. QSharedPointer<PegHighlighterResult> result(m_result);
  74. QTextBlock block = currentBlock();
  75. int blockNum = block.blockNumber();
  76. if (result->matched(m_timeStamp)) {
  77. preHighlightSingleFormatBlock(result->m_blocksHighlights, blockNum, p_text);
  78. } else {
  79. preHighlightSingleFormatBlock(m_fastResult->m_blocksHighlights, blockNum, p_text);
  80. }
  81. highlightBlockOne(result->m_blocksHighlights, blockNum);
  82. // The complete result is not ready yet. We use fast result for compensation.
  83. if (!result->matched(m_timeStamp)) {
  84. highlightBlockOne(m_fastResult->m_blocksHighlights, blockNum);
  85. }
  86. // Set current block's user data.
  87. updateBlockUserData(blockNum, p_text);
  88. updateBlockUserState(result, blockNum, p_text);
  89. if (currentBlockState() == HighlightBlockState::CodeBlock) {
  90. highlightCodeBlock(result, blockNum, p_text);
  91. highlightCodeBlockColorColumn(p_text);
  92. }
  93. }
  94. void PegMarkdownHighlighter::preHighlightSingleFormatBlock(const QVector<QVector<HLUnit>> &p_highlights,
  95. int p_blockNum,
  96. const QString &p_text)
  97. {
  98. int sz = p_text.size();
  99. if (sz == 0) {
  100. return;
  101. }
  102. if (!m_singleFormatBlocks.contains(p_blockNum)) {
  103. return;
  104. }
  105. if (p_highlights.size() > p_blockNum) {
  106. const QVector<HLUnit> &units = p_highlights[p_blockNum];
  107. if (units.size() == 1) {
  108. const HLUnit &unit = units[0];
  109. if (unit.start == 0 && (int)unit.length < sz) {
  110. setFormat(unit.length, sz - unit.length, m_styles[unit.styleIndex].format);
  111. }
  112. }
  113. }
  114. }
  115. void PegMarkdownHighlighter::highlightBlockOne(const QVector<QVector<HLUnit>> &p_highlights,
  116. int p_blockNum)
  117. {
  118. if (p_highlights.size() > p_blockNum) {
  119. // units are sorted by start position and length.
  120. const QVector<HLUnit> &units = p_highlights[p_blockNum];
  121. if (!units.isEmpty()) {
  122. for (int i = 0; i < units.size(); ++i) {
  123. const HLUnit &unit = units[i];
  124. if (i == 0) {
  125. // No need to merge format.
  126. setFormat(unit.start,
  127. unit.length,
  128. m_styles[unit.styleIndex].format);
  129. } else {
  130. QTextCharFormat newFormat = m_styles[unit.styleIndex].format;
  131. for (int j = i - 1; j >= 0; --j) {
  132. if (units[j].start + units[j].length <= unit.start) {
  133. // It won't affect current unit.
  134. continue;
  135. } else {
  136. // Merge the format.
  137. QTextCharFormat tmpFormat(newFormat);
  138. newFormat = m_styles[units[j].styleIndex].format;
  139. // tmpFormat takes precedence.
  140. newFormat.merge(tmpFormat);
  141. }
  142. }
  143. setFormat(unit.start, unit.length, newFormat);
  144. }
  145. }
  146. }
  147. }
  148. }
  149. // highlightBlock() will be called before this function.
  150. void PegMarkdownHighlighter::handleContentsChange(int p_position, int p_charsRemoved, int p_charsAdded)
  151. {
  152. Q_UNUSED(p_position);
  153. if (p_charsRemoved == 0 && p_charsAdded == 0) {
  154. return;
  155. }
  156. ++m_timeStamp;
  157. startFastParse(p_position, p_charsRemoved, p_charsAdded);
  158. // We still need a timer to start a complete parse.
  159. m_timer->start();
  160. }
  161. void PegMarkdownHighlighter::startParse()
  162. {
  163. QSharedPointer<PegParseConfig> config(new PegParseConfig());
  164. config->m_timeStamp = m_timeStamp;
  165. config->m_data = m_doc->toPlainText().toUtf8();
  166. config->m_numOfBlocks = m_doc->blockCount();
  167. config->m_extensions = m_parserExts;
  168. m_parser->parseAsync(config);
  169. }
  170. void PegMarkdownHighlighter::startFastParse(int p_position, int p_charsRemoved, int p_charsAdded)
  171. {
  172. // Get affected block range.
  173. int firstBlockNum, lastBlockNum;
  174. getFastParseBlockRange(p_position, p_charsRemoved, p_charsAdded, firstBlockNum, lastBlockNum);
  175. if (firstBlockNum == -1) {
  176. // We could not let m_fastResult NULL here.
  177. return;
  178. }
  179. QString text;
  180. QTextBlock block = m_doc->findBlockByNumber(firstBlockNum);
  181. int offset = block.position();
  182. while (block.isValid()) {
  183. int blockNum = block.blockNumber();
  184. if (blockNum > lastBlockNum) {
  185. break;
  186. } else if (blockNum == firstBlockNum) {
  187. text = block.text();
  188. } else {
  189. text = text + "\n" + block.text();
  190. }
  191. block = block.next();
  192. }
  193. QSharedPointer<PegParseConfig> config(new PegParseConfig());
  194. config->m_timeStamp = m_timeStamp;
  195. config->m_data = text.toUtf8();
  196. config->m_numOfBlocks = m_doc->blockCount();
  197. config->m_offset = offset;
  198. config->m_extensions = m_parserExts;
  199. config->m_fast = true;
  200. QSharedPointer<PegParseResult> parseRes = m_parser->parse(config);
  201. processFastParseResult(parseRes);
  202. }
  203. void PegMarkdownHighlighter::processFastParseResult(const QSharedPointer<PegParseResult> &p_result)
  204. {
  205. m_fastParseTimer->stop();
  206. m_fastResult.reset(new PegHighlighterFastResult(this, p_result));
  207. // Add additional single format blocks.
  208. updateSingleFormatBlocks(m_fastResult->m_blocksHighlights);
  209. m_fastParseTimer->start();
  210. }
  211. static bool compHLUnitStyle(const HLUnitStyle &a, const HLUnitStyle &b)
  212. {
  213. if (a.start < b.start) {
  214. return true;
  215. } else if (a.start == b.start) {
  216. return a.length > b.length;
  217. } else {
  218. return false;
  219. }
  220. }
  221. void PegMarkdownHighlighter::setCodeBlockHighlights(TimeStamp p_timeStamp,
  222. const QVector<HLUnitPos> &p_units)
  223. {
  224. QSharedPointer<PegHighlighterResult> result(m_result);
  225. if (!result->matched(p_timeStamp)) {
  226. return;
  227. }
  228. if (p_units.isEmpty()) {
  229. goto exit;
  230. }
  231. {
  232. QVector<QVector<HLUnitStyle>> highlights(result->m_codeBlocksHighlights.size());
  233. for (auto const &unit : p_units) {
  234. int pos = unit.m_position;
  235. int end = unit.m_position + unit.m_length;
  236. QTextBlock block = m_doc->findBlock(pos);
  237. int startBlockNum = block.blockNumber();
  238. int endBlockNum = m_doc->findBlock(end).blockNumber();
  239. // Text has been changed. Abandon the obsolete parsed result.
  240. if (startBlockNum == -1 || endBlockNum >= highlights.size()) {
  241. goto exit;
  242. }
  243. while (block.isValid()) {
  244. int blockNumber = block.blockNumber();
  245. if (blockNumber > endBlockNum) {
  246. break;
  247. }
  248. int blockStartPos = block.position();
  249. HLUnitStyle hl;
  250. hl.style = unit.m_style;
  251. if (blockNumber == startBlockNum) {
  252. hl.start = pos - blockStartPos;
  253. hl.length = (startBlockNum == endBlockNum) ?
  254. (end - pos) : (block.length() - hl.start);
  255. } else if (blockNumber == endBlockNum) {
  256. hl.start = 0;
  257. hl.length = end - blockStartPos;
  258. } else {
  259. hl.start = 0;
  260. hl.length = block.length();
  261. }
  262. highlights[blockNumber].append(hl);
  263. block = block.next();
  264. }
  265. }
  266. // Need to highlight in order.
  267. for (int i = 0; i < highlights.size(); ++i) {
  268. QVector<HLUnitStyle> &units = highlights[i];
  269. if (!units.isEmpty()) {
  270. if (units.size() > 1) {
  271. std::sort(units.begin(), units.end(), compHLUnitStyle);
  272. }
  273. result->m_codeBlocksHighlights[i].append(units);
  274. }
  275. }
  276. }
  277. exit:
  278. if (--result->m_numOfCodeBlockHighlightsToRecv <= 0) {
  279. // Rehighlight specific blocks.
  280. const QVector<QVector<HLUnitStyle>> &hls = result->m_codeBlocksHighlights;
  281. for (int i = 0; i < hls.size(); ++i) {
  282. if (!hls[i].isEmpty()) {
  283. rehighlightBlock(m_doc->findBlockByNumber(i));
  284. }
  285. }
  286. }
  287. }
  288. void PegMarkdownHighlighter::updateHighlightFast()
  289. {
  290. updateHighlight();
  291. }
  292. void PegMarkdownHighlighter::updateHighlight()
  293. {
  294. m_timer->stop();
  295. startParse();
  296. }
  297. void PegMarkdownHighlighter::handleParseResult(const QSharedPointer<PegParseResult> &p_result)
  298. {
  299. if (!m_result.isNull() && m_result->m_timeStamp > p_result->m_timeStamp) {
  300. return;
  301. }
  302. m_result.reset(new PegHighlighterResult(this, p_result));
  303. m_singleFormatBlocks.clear();
  304. updateSingleFormatBlocks(m_result->m_blocksHighlights);
  305. updateCodeBlocks(m_result);
  306. rehighlight();
  307. completeHighlight(m_result);
  308. }
  309. void PegMarkdownHighlighter::updateSingleFormatBlocks(const QVector<QVector<HLUnit>> &p_highlights)
  310. {
  311. for (int i = 0; i < p_highlights.size(); ++i) {
  312. const QVector<HLUnit> &units = p_highlights[i];
  313. if (units.size() == 1) {
  314. const HLUnit &unit = units[0];
  315. if (unit.start == 0 && unit.length > 0) {
  316. QTextBlock block = m_doc->findBlockByNumber(i);
  317. if (block.length() - 1 <= (int)unit.length) {
  318. m_singleFormatBlocks.insert(i);
  319. }
  320. }
  321. }
  322. }
  323. }
  324. void PegMarkdownHighlighter::updateCodeBlocks(QSharedPointer<PegHighlighterResult> p_result)
  325. {
  326. if (!p_result->matched(m_timeStamp)) {
  327. return;
  328. }
  329. if (g_config->getEnableCodeBlockHighlight()) {
  330. p_result->m_codeBlocksHighlights.resize(p_result->m_numOfBlocks);
  331. p_result->m_numOfCodeBlockHighlightsToRecv = p_result->m_codeBlocks.size();
  332. }
  333. emit codeBlocksUpdated(p_result->m_timeStamp, p_result->m_codeBlocks);
  334. }
  335. void PegMarkdownHighlighter::updateBlockUserData(int p_blockNum, const QString &p_text)
  336. {
  337. Q_UNUSED(p_text);
  338. VTextBlockData *blockData = currentBlockData();
  339. if (!blockData) {
  340. blockData = new VTextBlockData();
  341. setCurrentBlockUserData(blockData);
  342. } else {
  343. blockData->setCodeBlockIndentation(-1);
  344. }
  345. if (blockData->getPreviews().isEmpty()) {
  346. m_possiblePreviewBlocks.remove(p_blockNum);
  347. } else {
  348. m_possiblePreviewBlocks.insert(p_blockNum);
  349. }
  350. }
  351. void PegMarkdownHighlighter::updateBlockUserState(const QSharedPointer<PegHighlighterResult> &p_result,
  352. int p_blockNum,
  353. const QString &p_text)
  354. {
  355. // Newly-added block.
  356. if (currentBlockState() == -1) {
  357. setCurrentBlockState(HighlightBlockState::Normal);
  358. }
  359. if (!p_result->matched(m_timeStamp)) {
  360. return;
  361. }
  362. HighlightBlockState state = HighlightBlockState::Normal;
  363. auto it = p_result->m_codeBlocksState.find(p_blockNum);
  364. if (it != p_result->m_codeBlocksState.end()) {
  365. VTextBlockData *blockData = currentBlockData();
  366. Q_ASSERT(blockData);
  367. state = it.value();
  368. // Set code block indentation.
  369. switch (state) {
  370. case HighlightBlockState::CodeBlockStart:
  371. {
  372. int startLeadingSpaces = 0;
  373. QRegExp reg(VUtils::c_fencedCodeBlockStartRegExp);
  374. int idx = reg.indexIn(p_text);
  375. if (idx >= 0) {
  376. startLeadingSpaces = reg.capturedTexts()[1].size();
  377. }
  378. blockData->setCodeBlockIndentation(startLeadingSpaces);
  379. break;
  380. }
  381. case HighlightBlockState::CodeBlock:
  382. V_FALLTHROUGH;
  383. case HighlightBlockState::CodeBlockEnd:
  384. {
  385. int startLeadingSpaces = 0;
  386. VTextBlockData *preBlockData = previousBlockData();
  387. if (preBlockData) {
  388. startLeadingSpaces = preBlockData->getCodeBlockIndentation();
  389. }
  390. blockData->setCodeBlockIndentation(startLeadingSpaces);
  391. break;
  392. }
  393. default:
  394. Q_ASSERT(false);
  395. break;
  396. }
  397. } else if (p_result->m_hruleBlocks.contains(p_blockNum)) {
  398. state = HighlightBlockState::HRule;
  399. }
  400. // Set code block state.
  401. setCurrentBlockState(state);
  402. }
  403. void PegMarkdownHighlighter::highlightCodeBlock(const QSharedPointer<PegHighlighterResult> &p_result,
  404. int p_blockNum,
  405. const QString &p_text)
  406. {
  407. // Brush the indentation spaces.
  408. if (currentBlockState() == HighlightBlockState::CodeBlock) {
  409. int spaces = VEditUtils::fetchIndentation(p_text);
  410. if (spaces > 0) {
  411. setFormat(0, spaces, m_codeBlockFormat);
  412. }
  413. }
  414. if (p_result->m_codeBlocksHighlights.size() > p_blockNum) {
  415. const QVector<HLUnitStyle> &units = p_result->m_codeBlocksHighlights[p_blockNum];
  416. if (!units.isEmpty()) {
  417. QVector<QTextCharFormat *> formats(units.size(), NULL);
  418. for (int i = 0; i < units.size(); ++i) {
  419. const HLUnitStyle &unit = units[i];
  420. auto it = m_codeBlockStyles.find(unit.style);
  421. if (it == m_codeBlockStyles.end()) {
  422. continue;
  423. }
  424. formats[i] = &(*it);
  425. QTextCharFormat newFormat = m_codeBlockFormat;
  426. newFormat.merge(*it);
  427. for (int j = i - 1; j >= 0; --j) {
  428. if (units[j].start + units[j].length <= unit.start) {
  429. // It won't affect current unit.
  430. continue;
  431. } else {
  432. // Merge the format.
  433. if (formats[j]) {
  434. QTextCharFormat tmpFormat(newFormat);
  435. newFormat = *(formats[j]);
  436. // tmpFormat takes precedence.
  437. newFormat.merge(tmpFormat);
  438. }
  439. }
  440. }
  441. setFormat(unit.start, unit.length, newFormat);
  442. }
  443. }
  444. }
  445. }
  446. void PegMarkdownHighlighter::highlightCodeBlockColorColumn(const QString &p_text)
  447. {
  448. int cc = g_config->getColorColumn();
  449. if (cc <= 0) {
  450. return;
  451. }
  452. VTextBlockData *blockData = currentBlockData();
  453. Q_ASSERT(blockData);
  454. int indent = blockData->getCodeBlockIndentation();
  455. if (indent == -1) {
  456. return;
  457. }
  458. cc += indent;
  459. if (p_text.size() < cc) {
  460. return;
  461. }
  462. setFormat(cc - 1, 1, m_colorColumnFormat);
  463. }
  464. void PegMarkdownHighlighter::completeHighlight(QSharedPointer<PegHighlighterResult> p_result)
  465. {
  466. if (!p_result->matched(m_timeStamp)) {
  467. return;
  468. }
  469. if (isMathJaxEnabled()) {
  470. emit mathjaxBlocksUpdated(p_result->m_mathjaxBlocks);
  471. }
  472. emit imageLinksUpdated(p_result->m_imageRegions);
  473. emit headersUpdated(p_result->m_headerRegions);
  474. emit highlightCompleted();
  475. }
  476. void PegMarkdownHighlighter::getFastParseBlockRange(int p_position,
  477. int p_charsRemoved,
  478. int p_charsAdded,
  479. int &p_firstBlock,
  480. int &p_lastBlock) const
  481. {
  482. const int maxNumOfBlocks = 500;
  483. int charsChanged = p_charsRemoved + p_charsAdded;
  484. QTextBlock firstBlock = m_doc->findBlock(p_position);
  485. // May be an invalid block.
  486. QTextBlock lastBlock = m_doc->findBlock(qMax(0, p_position + charsChanged));
  487. if (!lastBlock.isValid()) {
  488. lastBlock = m_doc->lastBlock();
  489. }
  490. int num = lastBlock.blockNumber() - firstBlock.blockNumber() + 1;
  491. if (num >= maxNumOfBlocks) {
  492. p_firstBlock = p_lastBlock = -1;
  493. return;
  494. }
  495. // Look up.
  496. // Find empty block.
  497. // When firstBlock is an empty block at first, we should always skip it.
  498. while (firstBlock.isValid() && num < maxNumOfBlocks) {
  499. QTextBlock block = firstBlock.previous();
  500. if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
  501. firstBlock = block;
  502. ++num;
  503. } else {
  504. break;
  505. }
  506. }
  507. // Cross code block.
  508. while (firstBlock.isValid() && num < maxNumOfBlocks) {
  509. int state = firstBlock.userState();
  510. if (state == HighlightBlockState::CodeBlock
  511. || state == HighlightBlockState::CodeBlockEnd) {
  512. QTextBlock block = firstBlock.previous();
  513. if (block.isValid()) {
  514. firstBlock = block;
  515. ++num;
  516. } else {
  517. break;
  518. }
  519. } else {
  520. break;
  521. }
  522. }
  523. // Till the block with 0 indentation to handle contents in list.
  524. while (firstBlock.isValid() && num < maxNumOfBlocks) {
  525. if (VEditUtils::fetchIndentation(firstBlock) == 0
  526. && !VEditUtils::isEmptyBlock(firstBlock)) {
  527. break;
  528. } else {
  529. QTextBlock block = firstBlock.previous();
  530. if (block.isValid()) {
  531. firstBlock = block;
  532. ++num;
  533. } else {
  534. break;
  535. }
  536. }
  537. }
  538. // Look down.
  539. // Find empty block.
  540. // If lastBlock is an empty block at first, we should always skip it.
  541. while (lastBlock.isValid() && num < maxNumOfBlocks) {
  542. QTextBlock block = lastBlock.next();
  543. if (block.isValid() && !VEditUtils::isEmptyBlock(block)) {
  544. lastBlock = block;
  545. ++num;
  546. } else {
  547. break;
  548. }
  549. }
  550. // Cross code block.
  551. while (lastBlock.isValid() && num < maxNumOfBlocks) {
  552. int state = lastBlock.userState();
  553. if (state == HighlightBlockState::CodeBlock
  554. || state == HighlightBlockState::CodeBlockStart) {
  555. QTextBlock block = lastBlock.next();
  556. if (block.isValid()) {
  557. lastBlock = block;
  558. ++num;
  559. } else {
  560. break;
  561. }
  562. } else {
  563. break;
  564. }
  565. }
  566. p_firstBlock = firstBlock.blockNumber();
  567. p_lastBlock = lastBlock.blockNumber();
  568. if (p_lastBlock < p_firstBlock) {
  569. p_lastBlock = p_firstBlock;
  570. } else if (p_lastBlock - p_firstBlock + 1 > maxNumOfBlocks) {
  571. p_firstBlock = p_lastBlock = -1;
  572. }
  573. }