1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "textdocumentlayout.h"
27 #include "textdocument.h"
28 #include <utils/qtcassert.h>
29 #include <QDebug>
30
31 namespace TextEditor {
32
33 CodeFormatterData::~CodeFormatterData() = default;
34
~TextBlockUserData()35 TextBlockUserData::~TextBlockUserData()
36 {
37 for (TextMark *mrk : qAsConst(m_marks)) {
38 mrk->baseTextDocument()->removeMarkFromMarksCache(mrk);
39 mrk->setBaseTextDocument(nullptr);
40 mrk->removedFromEditor();
41 }
42
43 delete m_codeFormatterData;
44 }
45
braceDepthDelta() const46 int TextBlockUserData::braceDepthDelta() const
47 {
48 int delta = 0;
49 for (auto &parenthesis : m_parentheses) {
50 switch (parenthesis.chr.unicode()) {
51 case '{': case '+': case '[': ++delta; break;
52 case '}': case '-': case ']': --delta; break;
53 default: break;
54 }
55 }
56 return delta;
57 }
58
checkOpenParenthesis(QTextCursor * cursor,QChar c)59 TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c)
60 {
61 QTextBlock block = cursor->block();
62 if (!TextDocumentLayout::hasParentheses(block) || TextDocumentLayout::ifdefedOut(block))
63 return NoMatch;
64
65 Parentheses parenList = TextDocumentLayout::parentheses(block);
66 Parenthesis openParen, closedParen;
67 QTextBlock closedParenParag = block;
68
69 const int cursorPos = cursor->position() - closedParenParag.position();
70 int i = 0;
71 int ignore = 0;
72 bool foundOpen = false;
73 for (;;) {
74 if (!foundOpen) {
75 if (i >= parenList.count())
76 return NoMatch;
77 openParen = parenList.at(i);
78 if (openParen.pos != cursorPos) {
79 ++i;
80 continue;
81 } else {
82 foundOpen = true;
83 ++i;
84 }
85 }
86
87 if (i >= parenList.count()) {
88 for (;;) {
89 closedParenParag = closedParenParag.next();
90 if (!closedParenParag.isValid())
91 return NoMatch;
92 if (TextDocumentLayout::hasParentheses(closedParenParag)
93 && !TextDocumentLayout::ifdefedOut(closedParenParag)) {
94 parenList = TextDocumentLayout::parentheses(closedParenParag);
95 break;
96 }
97 }
98 i = 0;
99 }
100
101 closedParen = parenList.at(i);
102 if (closedParen.type == Parenthesis::Opened) {
103 ignore++;
104 ++i;
105 continue;
106 } else {
107 if (ignore > 0) {
108 ignore--;
109 ++i;
110 continue;
111 }
112
113 cursor->clearSelection();
114 cursor->setPosition(closedParenParag.position() + closedParen.pos + 1, QTextCursor::KeepAnchor);
115
116 if ((c == QLatin1Char('{') && closedParen.chr != QLatin1Char('}'))
117 || (c == QLatin1Char('(') && closedParen.chr != QLatin1Char(')'))
118 || (c == QLatin1Char('[') && closedParen.chr != QLatin1Char(']'))
119 || (c == QLatin1Char('+') && closedParen.chr != QLatin1Char('-'))
120 )
121 return Mismatch;
122
123 return Match;
124 }
125 }
126 }
127
checkClosedParenthesis(QTextCursor * cursor,QChar c)128 TextBlockUserData::MatchType TextBlockUserData::checkClosedParenthesis(QTextCursor *cursor, QChar c)
129 {
130 QTextBlock block = cursor->block();
131 if (!TextDocumentLayout::hasParentheses(block) || TextDocumentLayout::ifdefedOut(block))
132 return NoMatch;
133
134 Parentheses parenList = TextDocumentLayout::parentheses(block);
135 Parenthesis openParen, closedParen;
136 QTextBlock openParenParag = block;
137
138 const int cursorPos = cursor->position() - openParenParag.position();
139 int i = parenList.count() - 1;
140 int ignore = 0;
141 bool foundClosed = false;
142 for (;;) {
143 if (!foundClosed) {
144 if (i < 0)
145 return NoMatch;
146 closedParen = parenList.at(i);
147 if (closedParen.pos != cursorPos - 1) {
148 --i;
149 continue;
150 } else {
151 foundClosed = true;
152 --i;
153 }
154 }
155
156 if (i < 0) {
157 for (;;) {
158 openParenParag = openParenParag.previous();
159 if (!openParenParag.isValid())
160 return NoMatch;
161
162 if (TextDocumentLayout::hasParentheses(openParenParag)
163 && !TextDocumentLayout::ifdefedOut(openParenParag)) {
164 parenList = TextDocumentLayout::parentheses(openParenParag);
165 break;
166 }
167 }
168 i = parenList.count() - 1;
169 }
170
171 openParen = parenList.at(i);
172 if (openParen.type == Parenthesis::Closed) {
173 ignore++;
174 --i;
175 continue;
176 } else {
177 if (ignore > 0) {
178 ignore--;
179 --i;
180 continue;
181 }
182
183 cursor->clearSelection();
184 cursor->setPosition(openParenParag.position() + openParen.pos, QTextCursor::KeepAnchor);
185
186 if ((c == QLatin1Char('}') && openParen.chr != QLatin1Char('{')) ||
187 (c == QLatin1Char(')') && openParen.chr != QLatin1Char('(')) ||
188 (c == QLatin1Char(']') && openParen.chr != QLatin1Char('[')) ||
189 (c == QLatin1Char('-') && openParen.chr != QLatin1Char('+')))
190 return Mismatch;
191
192 return Match;
193 }
194 }
195 }
196
findPreviousOpenParenthesis(QTextCursor * cursor,bool select,bool onlyInCurrentBlock)197 bool TextBlockUserData::findPreviousOpenParenthesis(QTextCursor *cursor, bool select, bool onlyInCurrentBlock)
198 {
199 QTextBlock block = cursor->block();
200 int position = cursor->position();
201 int ignore = 0;
202 while (block.isValid()) {
203 Parentheses parenList = TextDocumentLayout::parentheses(block);
204 if (!parenList.isEmpty() && !TextDocumentLayout::ifdefedOut(block)) {
205 for (int i = parenList.count()-1; i >= 0; --i) {
206 Parenthesis paren = parenList.at(i);
207 if (block == cursor->block() &&
208 (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0)))
209 continue;
210 if (paren.type == Parenthesis::Closed) {
211 ++ignore;
212 } else if (ignore > 0) {
213 --ignore;
214 } else {
215 cursor->setPosition(block.position() + paren.pos, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
216 return true;
217 }
218 }
219 }
220 if (onlyInCurrentBlock)
221 return false;
222 block = block.previous();
223 }
224 return false;
225 }
226
findPreviousBlockOpenParenthesis(QTextCursor * cursor,bool checkStartPosition)227 bool TextBlockUserData::findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition)
228 {
229 QTextBlock block = cursor->block();
230 int position = cursor->position();
231 int ignore = 0;
232 while (block.isValid()) {
233 Parentheses parenList = TextDocumentLayout::parentheses(block);
234 if (!parenList.isEmpty() && !TextDocumentLayout::ifdefedOut(block)) {
235 for (int i = parenList.count()-1; i >= 0; --i) {
236 Parenthesis paren = parenList.at(i);
237 if (paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-'))
238 continue;
239 if (block == cursor->block()) {
240 if (position - block.position() <= paren.pos + (paren.type == Parenthesis::Closed ? 1 : 0))
241 continue;
242 if (checkStartPosition && paren.type == Parenthesis::Opened && paren.pos== cursor->position())
243 return true;
244 }
245 if (paren.type == Parenthesis::Closed) {
246 ++ignore;
247 } else if (ignore > 0) {
248 --ignore;
249 } else {
250 cursor->setPosition(block.position() + paren.pos);
251 return true;
252 }
253 }
254 }
255 block = block.previous();
256 }
257 return false;
258 }
259
findNextClosingParenthesis(QTextCursor * cursor,bool select)260 bool TextBlockUserData::findNextClosingParenthesis(QTextCursor *cursor, bool select)
261 {
262 QTextBlock block = cursor->block();
263 int position = cursor->position();
264 int ignore = 0;
265 while (block.isValid()) {
266 Parentheses parenList = TextDocumentLayout::parentheses(block);
267 if (!parenList.isEmpty() && !TextDocumentLayout::ifdefedOut(block)) {
268 for (int i = 0; i < parenList.count(); ++i) {
269 Parenthesis paren = parenList.at(i);
270 if (block == cursor->block() &&
271 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
272 continue;
273 if (paren.type == Parenthesis::Opened) {
274 ++ignore;
275 } else if (ignore > 0) {
276 --ignore;
277 } else {
278 cursor->setPosition(block.position() + paren.pos+1, select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor);
279 return true;
280 }
281 }
282 }
283 block = block.next();
284 }
285 return false;
286 }
287
findNextBlockClosingParenthesis(QTextCursor * cursor)288 bool TextBlockUserData::findNextBlockClosingParenthesis(QTextCursor *cursor)
289 {
290 QTextBlock block = cursor->block();
291 int position = cursor->position();
292 int ignore = 0;
293 while (block.isValid()) {
294 Parentheses parenList = TextDocumentLayout::parentheses(block);
295 if (!parenList.isEmpty() && !TextDocumentLayout::ifdefedOut(block)) {
296 for (int i = 0; i < parenList.count(); ++i) {
297 Parenthesis paren = parenList.at(i);
298 if (paren.chr != QLatin1Char('+') && paren.chr != QLatin1Char('-'))
299 continue;
300 if (block == cursor->block() &&
301 (position - block.position() > paren.pos - (paren.type == Parenthesis::Opened ? 1 : 0)))
302 continue;
303 if (paren.type == Parenthesis::Opened) {
304 ++ignore;
305 } else if (ignore > 0) {
306 --ignore;
307 } else {
308 cursor->setPosition(block.position() + paren.pos+1);
309 return true;
310 }
311 }
312 }
313 block = block.next();
314 }
315 return false;
316 }
317
matchCursorBackward(QTextCursor * cursor)318 TextBlockUserData::MatchType TextBlockUserData::matchCursorBackward(QTextCursor *cursor)
319 {
320 cursor->clearSelection();
321 const QTextBlock block = cursor->block();
322
323 if (!TextDocumentLayout::hasParentheses(block) || TextDocumentLayout::ifdefedOut(block))
324 return NoMatch;
325
326 const int relPos = cursor->position() - block.position();
327
328 Parentheses parentheses = TextDocumentLayout::parentheses(block);
329 const Parentheses::const_iterator cend = parentheses.constEnd();
330 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
331 const Parenthesis &paren = *it;
332 if (paren.pos == relPos - 1 && paren.type == Parenthesis::Closed)
333 return checkClosedParenthesis(cursor, paren.chr);
334 }
335 return NoMatch;
336 }
337
matchCursorForward(QTextCursor * cursor)338 TextBlockUserData::MatchType TextBlockUserData::matchCursorForward(QTextCursor *cursor)
339 {
340 cursor->clearSelection();
341 const QTextBlock block = cursor->block();
342
343 if (!TextDocumentLayout::hasParentheses(block) || TextDocumentLayout::ifdefedOut(block))
344 return NoMatch;
345
346 const int relPos = cursor->position() - block.position();
347
348 Parentheses parentheses = TextDocumentLayout::parentheses(block);
349 const Parentheses::const_iterator cend = parentheses.constEnd();
350 for (Parentheses::const_iterator it = parentheses.constBegin();it != cend; ++it) {
351 const Parenthesis &paren = *it;
352 if (paren.pos == relPos
353 && paren.type == Parenthesis::Opened) {
354 return checkOpenParenthesis(cursor, paren.chr);
355 }
356 }
357 return NoMatch;
358 }
359
setCodeFormatterData(CodeFormatterData * data)360 void TextBlockUserData::setCodeFormatterData(CodeFormatterData *data)
361 {
362 if (m_codeFormatterData)
363 delete m_codeFormatterData;
364
365 m_codeFormatterData = data;
366 }
367
addMark(TextMark * mark)368 void TextBlockUserData::addMark(TextMark *mark)
369 {
370 int i = 0;
371 for ( ; i < m_marks.size(); ++i) {
372 if (mark->priority() < m_marks.at(i)->priority())
373 break;
374 }
375 m_marks.insert(i, mark);
376 }
377
378
TextDocumentLayout(QTextDocument * doc)379 TextDocumentLayout::TextDocumentLayout(QTextDocument *doc)
380 : QPlainTextDocumentLayout(doc)
381 {}
382
~TextDocumentLayout()383 TextDocumentLayout::~TextDocumentLayout()
384 {
385 documentClosing();
386 }
387
setParentheses(const QTextBlock & block,const Parentheses & parentheses)388 void TextDocumentLayout::setParentheses(const QTextBlock &block, const Parentheses &parentheses)
389 {
390 if (parentheses.isEmpty()) {
391 if (TextBlockUserData *userData = textUserData(block))
392 userData->clearParentheses();
393 } else {
394 userData(block)->setParentheses(parentheses);
395 }
396 }
397
parentheses(const QTextBlock & block)398 Parentheses TextDocumentLayout::parentheses(const QTextBlock &block)
399 {
400 if (TextBlockUserData *userData = textUserData(block))
401 return userData->parentheses();
402 return Parentheses();
403 }
404
hasParentheses(const QTextBlock & block)405 bool TextDocumentLayout::hasParentheses(const QTextBlock &block)
406 {
407 if (TextBlockUserData *userData = textUserData(block))
408 return userData->hasParentheses();
409 return false;
410 }
411
setIfdefedOut(const QTextBlock & block)412 bool TextDocumentLayout::setIfdefedOut(const QTextBlock &block)
413 {
414 return userData(block)->setIfdefedOut();
415 }
416
clearIfdefedOut(const QTextBlock & block)417 bool TextDocumentLayout::clearIfdefedOut(const QTextBlock &block)
418 {
419 if (TextBlockUserData *userData = textUserData(block))
420 return userData->clearIfdefedOut();
421 return false;
422 }
423
ifdefedOut(const QTextBlock & block)424 bool TextDocumentLayout::ifdefedOut(const QTextBlock &block)
425 {
426 if (TextBlockUserData *userData = textUserData(block))
427 return userData->ifdefedOut();
428 return false;
429 }
430
braceDepthDelta(const QTextBlock & block)431 int TextDocumentLayout::braceDepthDelta(const QTextBlock &block)
432 {
433 if (TextBlockUserData *userData = textUserData(block))
434 return userData->braceDepthDelta();
435 return 0;
436 }
437
braceDepth(const QTextBlock & block)438 int TextDocumentLayout::braceDepth(const QTextBlock &block)
439 {
440 int state = block.userState();
441 if (state == -1)
442 return 0;
443 return state >> 8;
444 }
445
setBraceDepth(QTextBlock & block,int depth)446 void TextDocumentLayout::setBraceDepth(QTextBlock &block, int depth)
447 {
448 int state = block.userState();
449 if (state == -1)
450 state = 0;
451 state = state & 0xff;
452 block.setUserState((depth << 8) | state);
453 }
454
changeBraceDepth(QTextBlock & block,int delta)455 void TextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta)
456 {
457 if (delta)
458 setBraceDepth(block, braceDepth(block) + delta);
459 }
460
setLexerState(const QTextBlock & block,int state)461 void TextDocumentLayout::setLexerState(const QTextBlock &block, int state)
462 {
463 if (state == 0) {
464 if (TextBlockUserData *userData = textUserData(block))
465 userData->setLexerState(0);
466 } else {
467 userData(block)->setLexerState(qMax(0,state));
468 }
469 }
470
lexerState(const QTextBlock & block)471 int TextDocumentLayout::lexerState(const QTextBlock &block)
472 {
473 if (TextBlockUserData *userData = textUserData(block))
474 return userData->lexerState();
475 return 0;
476 }
477
setFoldingIndent(const QTextBlock & block,int indent)478 void TextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
479 {
480 if (indent == 0) {
481 if (TextBlockUserData *userData = textUserData(block))
482 userData->setFoldingIndent(0);
483 } else {
484 userData(block)->setFoldingIndent(indent);
485 }
486 }
487
foldingIndent(const QTextBlock & block)488 int TextDocumentLayout::foldingIndent(const QTextBlock &block)
489 {
490 if (TextBlockUserData *userData = textUserData(block))
491 return userData->foldingIndent();
492 return 0;
493 }
494
changeFoldingIndent(QTextBlock & block,int delta)495 void TextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
496 {
497 if (delta)
498 setFoldingIndent(block, foldingIndent(block) + delta);
499 }
500
canFold(const QTextBlock & block)501 bool TextDocumentLayout::canFold(const QTextBlock &block)
502 {
503 return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
504 }
505
isFolded(const QTextBlock & block)506 bool TextDocumentLayout::isFolded(const QTextBlock &block)
507 {
508 if (TextBlockUserData *userData = textUserData(block))
509 return userData->folded();
510 return false;
511 }
512
setFolded(const QTextBlock & block,bool folded)513 void TextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
514 {
515 if (folded)
516 userData(block)->setFolded(true);
517 else if (TextBlockUserData *userData = textUserData(block))
518 userData->setFolded(false);
519 else
520 return;
521
522 if (auto layout = qobject_cast<TextDocumentLayout *>(block.document()->documentLayout()))
523 emit layout->foldChanged(block.blockNumber(), folded);
524 }
525
requestExtraAreaUpdate()526 void TextDocumentLayout::requestExtraAreaUpdate()
527 {
528 emit updateExtraArea();
529 }
530
doFoldOrUnfold(const QTextBlock & block,bool unfold)531 void TextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
532 {
533 if (!canFold(block))
534 return;
535 QTextBlock b = block.next();
536
537 int indent = foldingIndent(block);
538 while (b.isValid() && foldingIndent(b) > indent && (unfold || b.next().isValid())) {
539 b.setVisible(unfold);
540 b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
541 if (unfold) { // do not unfold folded sub-blocks
542 if (isFolded(b) && b.next().isValid()) {
543 int jndent = foldingIndent(b);
544 b = b.next();
545 while (b.isValid() && foldingIndent(b) > jndent)
546 b = b.next();
547 continue;
548 }
549 }
550 b = b.next();
551 }
552 setFolded(block, !unfold);
553 }
554
setRequiredWidth(int width)555 void TextDocumentLayout::setRequiredWidth(int width)
556 {
557 int oldw = m_requiredWidth;
558 m_requiredWidth = width;
559 int dw = int(QPlainTextDocumentLayout::documentSize().width());
560 if (oldw > dw || width > dw)
561 emitDocumentSizeChanged();
562 }
563
564
documentSize() const565 QSizeF TextDocumentLayout::documentSize() const
566 {
567 QSizeF size = QPlainTextDocumentLayout::documentSize();
568 size.setWidth(qMax(qreal(m_requiredWidth), size.width()));
569 return size;
570 }
571
documentClosing()572 TextMarks TextDocumentLayout::documentClosing()
573 {
574 TextMarks marks;
575 for (QTextBlock block = document()->begin(); block.isValid(); block = block.next()) {
576 if (auto data = static_cast<TextBlockUserData *>(block.userData()))
577 marks.append(data->documentClosing());
578 }
579 return marks;
580 }
581
documentReloaded(TextMarks marks,TextDocument * baseTextDocument)582 void TextDocumentLayout::documentReloaded(TextMarks marks, TextDocument *baseTextDocument)
583 {
584 for (TextMark *mark : qAsConst(marks)) {
585 int blockNumber = mark->lineNumber() - 1;
586 QTextBlock block = document()->findBlockByNumber(blockNumber);
587 if (block.isValid()) {
588 TextBlockUserData *userData = TextDocumentLayout::userData(block);
589 userData->addMark(mark);
590 mark->setBaseTextDocument(baseTextDocument);
591 mark->updateBlock(block);
592 } else {
593 baseTextDocument->removeMarkFromMarksCache(mark);
594 mark->setBaseTextDocument(nullptr);
595 mark->removedFromEditor();
596 }
597 }
598 requestUpdate();
599 }
600
updateMarksLineNumber()601 void TextDocumentLayout::updateMarksLineNumber()
602 {
603 // Note: the breakpointmanger deletes breakpoint marks and readds them
604 // if it doesn't agree with our updating
605 QTextBlock block = document()->begin();
606 int blockNumber = 0;
607 while (block.isValid()) {
608 if (const TextBlockUserData *userData = textUserData(block)) {
609 for (TextMark *mrk : userData->marks())
610 mrk->updateLineNumber(blockNumber + 1);
611 }
612 block = block.next();
613 ++blockNumber;
614 }
615 }
616
updateMarksBlock(const QTextBlock & block)617 void TextDocumentLayout::updateMarksBlock(const QTextBlock &block)
618 {
619 if (const TextBlockUserData *userData = textUserData(block)) {
620 for (TextMark *mrk : userData->marks())
621 mrk->updateBlock(block);
622 }
623 }
624
blockBoundingRect(const QTextBlock & block) const625 QRectF TextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
626 {
627 QRectF boundingRect = QPlainTextDocumentLayout::blockBoundingRect(block);
628 if (TextBlockUserData *userData = textUserData(block))
629 boundingRect.adjust(0, 0, 0, userData->additionalAnnotationHeight());
630 return boundingRect;
631 }
632
setup(TextDocumentLayout * layout)633 void TextDocumentLayout::FoldValidator::setup(TextDocumentLayout *layout)
634 {
635 m_layout = layout;
636 }
637
reset()638 void TextDocumentLayout::FoldValidator::reset()
639 {
640 m_insideFold = 0;
641 m_requestDocUpdate = false;
642 }
643
process(QTextBlock block)644 void TextDocumentLayout::FoldValidator::process(QTextBlock block)
645 {
646 if (!m_layout)
647 return;
648
649 const QTextBlock &previous = block.previous();
650 if (!previous.isValid())
651 return;
652
653 const bool preIsFolded = isFolded(previous);
654 const bool preCanFold = canFold(previous);
655 const bool isVisible = block.isVisible();
656
657 if (preIsFolded && !preCanFold)
658 setFolded(previous, false);
659 else if (!preIsFolded && preCanFold && previous.isVisible() && !isVisible)
660 setFolded(previous, true);
661
662 if (isFolded(previous) && !m_insideFold)
663 m_insideFold = foldingIndent(block);
664
665 bool shouldBeVisible = m_insideFold == 0;
666 if (!shouldBeVisible) {
667 shouldBeVisible = foldingIndent(block) < m_insideFold;
668 if (shouldBeVisible)
669 m_insideFold = 0;
670 }
671
672 if (shouldBeVisible != isVisible) {
673 block.setVisible(shouldBeVisible);
674 block.setLineCount(block.isVisible() ? qMax(1, block.layout()->lineCount()) : 0);
675 m_requestDocUpdate = true;
676 }
677 }
678
finalize()679 void TextDocumentLayout::FoldValidator::finalize()
680 {
681 if (m_requestDocUpdate && m_layout) {
682 m_layout->requestUpdate();
683 m_layout->emitDocumentSizeChanged();
684 }
685 }
686
687 } // namespace TextEditor
688