1 | /* |
2 | SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> |
3 | |
4 | SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #include "katetextblock.h" |
8 | #include "katetextbuffer.h" |
9 | #include "katetextcursor.h" |
10 | #include "katetextrange.h" |
11 | |
12 | namespace Kate |
13 | { |
14 | TextBlock::TextBlock(TextBuffer *buffer, int index) |
15 | : m_buffer(buffer) |
16 | , m_blockIndex(index) |
17 | { |
18 | // reserve the block size |
19 | m_lines.reserve(n: BufferBlockSize); |
20 | } |
21 | |
22 | TextBlock::~TextBlock() |
23 | { |
24 | // blocks should be empty before they are deleted! |
25 | Q_ASSERT(m_lines.empty()); |
26 | Q_ASSERT(m_cursors.empty()); |
27 | |
28 | // it only is a hint for ranges for this block, not the storage of them |
29 | } |
30 | |
31 | int TextBlock::startLine() const |
32 | { |
33 | return m_buffer->m_startLines[m_blockIndex]; |
34 | } |
35 | |
36 | TextLine TextBlock::line(int line) const |
37 | { |
38 | // right input |
39 | Q_ASSERT(size_t(line) < m_lines.size()); |
40 | // get text line, at will bail out on out-of-range |
41 | return m_lines.at(n: line); |
42 | } |
43 | |
44 | void TextBlock::setLineMetaData(int line, const TextLine &textLine) |
45 | { |
46 | // right input |
47 | Q_ASSERT(size_t(line) < m_lines.size()); |
48 | |
49 | // set stuff, at will bail out on out-of-range |
50 | const QString originalText = m_lines.at(n: line).text(); |
51 | m_lines.at(n: line) = textLine; |
52 | m_lines.at(n: line).text() = originalText; |
53 | } |
54 | |
55 | void TextBlock::appendLine(const QString &textOfLine) |
56 | { |
57 | m_lines.emplace_back(args: textOfLine); |
58 | } |
59 | |
60 | void TextBlock::clearLines() |
61 | { |
62 | m_lines.clear(); |
63 | } |
64 | |
65 | void TextBlock::text(QString &text) const |
66 | { |
67 | // combine all lines |
68 | for (const auto &line : m_lines) { |
69 | text.append(s: line.text()); |
70 | text.append(c: QLatin1Char('\n')); |
71 | } |
72 | } |
73 | |
74 | void TextBlock::wrapLine(const KTextEditor::Cursor position, int fixStartLinesStartIndex) |
75 | { |
76 | // calc internal line |
77 | const int line = position.line() - startLine(); |
78 | |
79 | // get text, copy, we might invalidate the reference |
80 | const QString text = m_lines.at(n: line).text(); |
81 | |
82 | // check if valid column |
83 | Q_ASSERT(position.column() >= 0); |
84 | Q_ASSERT(position.column() <= text.size()); |
85 | Q_ASSERT(fixStartLinesStartIndex == m_blockIndex); |
86 | |
87 | // create new line and insert it |
88 | m_lines.insert(position: m_lines.begin() + line + 1, x: TextLine()); |
89 | |
90 | // cases for modification: |
91 | // 1. line is wrapped in the middle |
92 | // 2. if empty line is wrapped, mark new line as modified |
93 | // 3. line-to-be-wrapped is already modified |
94 | if (position.column() > 0 || text.size() == 0 || m_lines.at(n: line).markedAsModified()) { |
95 | m_lines.at(n: line + 1).markAsModified(modified: true); |
96 | } else if (m_lines.at(n: line).markedAsSavedOnDisk()) { |
97 | m_lines.at(n: line + 1).markAsSavedOnDisk(savedOnDisk: true); |
98 | } |
99 | |
100 | // perhaps remove some text from previous line and append it |
101 | if (position.column() < text.size()) { |
102 | // text from old line moved first to new one |
103 | m_lines.at(n: line + 1).text() = text.right(n: text.size() - position.column()); |
104 | |
105 | // now remove wrapped text from old line |
106 | m_lines.at(n: line).text().chop(n: text.size() - position.column()); |
107 | |
108 | // mark line as modified |
109 | m_lines.at(n: line).markAsModified(modified: true); |
110 | } |
111 | |
112 | // fix all start lines |
113 | // we need to do this NOW, else the range update will FAIL! |
114 | // bug 313759 |
115 | m_buffer->fixStartLines(startBlock: fixStartLinesStartIndex + 1, value: 1); |
116 | |
117 | // notify the text history |
118 | m_buffer->history().wrapLine(position); |
119 | |
120 | // cursor and range handling below |
121 | |
122 | // no cursors will leave or join this block |
123 | |
124 | // no cursors in this block, no work to do.. |
125 | if (m_cursors.empty()) { |
126 | return; |
127 | } |
128 | |
129 | // move all cursors on the line which has the text inserted |
130 | // remember all ranges modified, optimize for the standard case of a few ranges |
131 | QVarLengthArray<TextRange *, 32> changedRanges; |
132 | for (TextCursor *cursor : m_cursors) { |
133 | // skip cursors on lines in front of the wrapped one! |
134 | if (cursor->lineInBlock() < line) { |
135 | continue; |
136 | } |
137 | |
138 | // either this is simple, line behind the wrapped one |
139 | if (cursor->lineInBlock() > line) { |
140 | // patch line of cursor |
141 | cursor->m_line++; |
142 | } |
143 | |
144 | // this is the wrapped line |
145 | else { |
146 | // skip cursors with too small column |
147 | if (cursor->column() <= position.column()) { |
148 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { |
149 | continue; |
150 | } |
151 | } |
152 | |
153 | // move cursor |
154 | |
155 | // patch line of cursor |
156 | cursor->m_line++; |
157 | |
158 | // patch column |
159 | cursor->m_column -= position.column(); |
160 | } |
161 | |
162 | // remember range, if any, avoid double insert |
163 | auto range = cursor->kateRange(); |
164 | if (range && !range->isValidityCheckRequired()) { |
165 | range->setValidityCheckRequired(); |
166 | changedRanges.push_back(t: range); |
167 | } |
168 | } |
169 | |
170 | // we might need to invalidate ranges or notify about their changes |
171 | // checkValidity might trigger delete of the range! |
172 | for (TextRange *range : std::as_const(t&: changedRanges)) { |
173 | // ensure that we really invalidate bad ranges! |
174 | range->checkValidity(); |
175 | } |
176 | } |
177 | |
178 | void TextBlock::unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex) |
179 | { |
180 | // two possiblities: either first line of this block or later line |
181 | if (line == 0) { |
182 | // we need previous block with at least one line |
183 | Q_ASSERT(previousBlock); |
184 | Q_ASSERT(previousBlock->lines() > 0); |
185 | |
186 | // move last line of previous block to this one, might result in empty block |
187 | const TextLine oldFirst = m_lines.at(n: 0); |
188 | const int lastLineOfPreviousBlock = previousBlock->lines() - 1; |
189 | m_lines[0] = previousBlock->m_lines.back(); |
190 | previousBlock->m_lines.erase(position: previousBlock->m_lines.begin() + (previousBlock->lines() - 1)); |
191 | |
192 | m_buffer->m_blockSizes[m_blockIndex - 1] -= m_lines[0].length() + 1; |
193 | m_buffer->m_blockSizes[m_blockIndex] += m_lines[0].length(); |
194 | |
195 | const int oldSizeOfPreviousLine = m_lines[0].text().size(); |
196 | if (oldFirst.length() > 0) { |
197 | // append text |
198 | m_lines[0].text().append(s: oldFirst.text()); |
199 | |
200 | // mark line as modified, since text was appended |
201 | m_lines[0].markAsModified(modified: true); |
202 | } |
203 | |
204 | // fix all start lines |
205 | // we need to do this NOW, else the range update will FAIL! |
206 | // bug 313759 |
207 | Q_ASSERT(fixStartLinesStartIndex + 1 == m_blockIndex); |
208 | m_buffer->fixStartLines(startBlock: fixStartLinesStartIndex + 1, value: -1); |
209 | |
210 | // notify the text history in advance |
211 | m_buffer->history().unwrapLine(line: startLine() + line, oldLineLength: oldSizeOfPreviousLine); |
212 | |
213 | // cursor and range handling below |
214 | |
215 | // no cursors in this block and the previous one, no work to do.. |
216 | if (m_cursors.empty() && previousBlock->m_cursors.empty()) { |
217 | return; |
218 | } |
219 | |
220 | // move all cursors because of the unwrapped line |
221 | // remember all ranges modified, optimize for the standard case of a few ranges |
222 | QVarLengthArray<QPair<TextRange *, bool>, 32> changedRanges; |
223 | for (TextCursor *cursor : m_cursors) { |
224 | // this is the unwrapped line |
225 | if (cursor->lineInBlock() == 0) { |
226 | // patch column |
227 | cursor->m_column += oldSizeOfPreviousLine; |
228 | |
229 | // remember range, if any, avoid double insert |
230 | auto range = cursor->kateRange(); |
231 | if (range && !range->isValidityCheckRequired()) { |
232 | range->setValidityCheckRequired(); |
233 | changedRanges.push_back(t: {range, range->spansMultipleBlocks()}); |
234 | } |
235 | } |
236 | } |
237 | |
238 | // move cursors of the moved line from previous block to this block now |
239 | for (auto it = previousBlock->m_cursors.begin(); it != previousBlock->m_cursors.end();) { |
240 | auto cursor = *it; |
241 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { |
242 | Kate::TextRange *range = cursor->kateRange(); |
243 | // get the value before changing the block |
244 | const bool spansMultipleBlocks = range && range->spansMultipleBlocks(); |
245 | cursor->m_line = 0; |
246 | cursor->m_block = this; |
247 | insertCursor(cursor); |
248 | |
249 | // remember range, if any, avoid double insert |
250 | if (range && !range->isValidityCheckRequired()) { |
251 | range->setValidityCheckRequired(); |
252 | // the range might not span multiple blocks anymore |
253 | changedRanges.push_back(t: {range, spansMultipleBlocks}); |
254 | } |
255 | |
256 | // remove from previous block |
257 | it = previousBlock->m_cursors.erase(position: it); |
258 | } else { |
259 | // keep in previous block |
260 | ++it; |
261 | } |
262 | } |
263 | |
264 | // fixup the ranges that might be effected, because they moved from last line to this block |
265 | // we might need to invalidate ranges or notify about their changes |
266 | // checkValidity might trigger delete of the range! |
267 | for (auto [range, wasMultiblock] : changedRanges) { |
268 | // if the range doesn't span multiple blocks anymore remove it from buffer multiline range cache |
269 | if (!range->spansMultipleBlocks() && wasMultiblock) { |
270 | m_buffer->removeMultilineRange(range); |
271 | } |
272 | // afterwards check validity, might delete this range! |
273 | range->checkValidity(); |
274 | } |
275 | |
276 | // be done |
277 | return; |
278 | } |
279 | |
280 | m_buffer->m_blockSizes[m_blockIndex] -= 1; |
281 | |
282 | // easy: just move text to previous line and remove current one |
283 | const int oldSizeOfPreviousLine = m_lines.at(n: line - 1).length(); |
284 | const int sizeOfCurrentLine = m_lines.at(n: line).length(); |
285 | if (sizeOfCurrentLine > 0) { |
286 | m_lines.at(n: line - 1).text().append(s: m_lines.at(n: line).text()); |
287 | } |
288 | |
289 | const bool lineChanged = (oldSizeOfPreviousLine > 0 && m_lines.at(n: line - 1).markedAsModified()) |
290 | || (sizeOfCurrentLine > 0 && (oldSizeOfPreviousLine > 0 || m_lines.at(n: line).markedAsModified())); |
291 | m_lines.at(n: line - 1).markAsModified(modified: lineChanged); |
292 | if (oldSizeOfPreviousLine == 0 && m_lines.at(n: line).markedAsSavedOnDisk()) { |
293 | m_lines.at(n: line - 1).markAsSavedOnDisk(savedOnDisk: true); |
294 | } |
295 | |
296 | m_lines.erase(position: m_lines.begin() + line); |
297 | |
298 | // fix all start lines |
299 | // we need to do this NOW, else the range update will FAIL! |
300 | // bug 313759 |
301 | m_buffer->fixStartLines(startBlock: fixStartLinesStartIndex + 1, value: -1); |
302 | |
303 | // notify the text history in advance |
304 | m_buffer->history().unwrapLine(line: startLine() + line, oldLineLength: oldSizeOfPreviousLine); |
305 | |
306 | // cursor and range handling below |
307 | |
308 | // no cursors in this block, no work to do.. |
309 | if (m_cursors.empty()) { |
310 | return; |
311 | } |
312 | |
313 | // move all cursors because of the unwrapped line |
314 | // remember all ranges modified, optimize for the standard case of a few ranges |
315 | QVarLengthArray<TextRange *, 32> changedRanges; |
316 | for (TextCursor *cursor : m_cursors) { |
317 | // skip cursors in lines in front of removed one |
318 | if (cursor->lineInBlock() < line) { |
319 | continue; |
320 | } |
321 | |
322 | // this is the unwrapped line |
323 | if (cursor->lineInBlock() == line) { |
324 | // patch column |
325 | cursor->m_column += oldSizeOfPreviousLine; |
326 | } |
327 | |
328 | // patch line of cursor |
329 | cursor->m_line--; |
330 | |
331 | // remember range, if any, avoid double insert |
332 | auto range = cursor->kateRange(); |
333 | if (range && !range->isValidityCheckRequired()) { |
334 | range->setValidityCheckRequired(); |
335 | changedRanges.push_back(t: range); |
336 | } |
337 | } |
338 | |
339 | // we might need to invalidate ranges or notify about their changes |
340 | // checkValidity might trigger delete of the range! |
341 | for (TextRange *range : std::as_const(t&: changedRanges)) { |
342 | // ensure that we really invalidate bad ranges! |
343 | range->checkValidity(); |
344 | } |
345 | } |
346 | |
347 | void TextBlock::insertText(const KTextEditor::Cursor position, const QString &text) |
348 | { |
349 | // calc internal line |
350 | int line = position.line() - startLine(); |
351 | |
352 | // get text |
353 | QString &textOfLine = m_lines.at(n: line).text(); |
354 | int oldLength = textOfLine.size(); |
355 | m_lines.at(n: line).markAsModified(modified: true); |
356 | |
357 | // check if valid column |
358 | Q_ASSERT(position.column() >= 0); |
359 | Q_ASSERT(position.column() <= textOfLine.size()); |
360 | |
361 | // insert text |
362 | textOfLine.insert(i: position.column(), s: text); |
363 | |
364 | // notify the text history |
365 | m_buffer->history().insertText(position, length: text.size(), oldLineLength: oldLength); |
366 | |
367 | // cursor and range handling below |
368 | |
369 | // no cursors in this block, no work to do.. |
370 | if (m_cursors.empty()) { |
371 | return; |
372 | } |
373 | |
374 | // move all cursors on the line which has the text inserted |
375 | // remember all ranges modified, optimize for the standard case of a few ranges |
376 | QVarLengthArray<TextRange *, 32> changedRanges; |
377 | for (TextCursor *cursor : m_cursors) { |
378 | // skip cursors not on this line! |
379 | if (cursor->lineInBlock() != line) { |
380 | continue; |
381 | } |
382 | |
383 | // skip cursors with too small column |
384 | if (cursor->column() <= position.column()) { |
385 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) { |
386 | continue; |
387 | } |
388 | } |
389 | |
390 | // patch column of cursor |
391 | if (cursor->m_column <= oldLength) { |
392 | cursor->m_column += text.size(); |
393 | } |
394 | |
395 | // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode |
396 | else if (cursor->m_column < textOfLine.size()) { |
397 | cursor->m_column = textOfLine.size(); |
398 | } |
399 | |
400 | // remember range, if any, avoid double insert |
401 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated |
402 | auto range = cursor->kateRange(); |
403 | if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) { |
404 | range->setValidityCheckRequired(); |
405 | changedRanges.push_back(t: range); |
406 | } |
407 | } |
408 | |
409 | // we might need to invalidate ranges or notify about their changes |
410 | // checkValidity might trigger delete of the range! |
411 | for (TextRange *range : std::as_const(t&: changedRanges)) { |
412 | range->checkValidity(); |
413 | } |
414 | } |
415 | |
416 | void TextBlock::removeText(KTextEditor::Range range, QString &removedText) |
417 | { |
418 | // calc internal line |
419 | int line = range.start().line() - startLine(); |
420 | |
421 | // get text |
422 | QString &textOfLine = m_lines.at(n: line).text(); |
423 | int oldLength = textOfLine.size(); |
424 | |
425 | // check if valid column |
426 | Q_ASSERT(range.start().column() >= 0); |
427 | Q_ASSERT(range.start().column() <= textOfLine.size()); |
428 | Q_ASSERT(range.end().column() >= 0); |
429 | Q_ASSERT(range.end().column() <= textOfLine.size()); |
430 | |
431 | // get text which will be removed |
432 | removedText = textOfLine.mid(position: range.start().column(), n: range.end().column() - range.start().column()); |
433 | |
434 | // remove text |
435 | textOfLine.remove(i: range.start().column(), len: range.end().column() - range.start().column()); |
436 | m_lines.at(n: line).markAsModified(modified: true); |
437 | |
438 | // notify the text history |
439 | m_buffer->history().removeText(range, oldLineLength: oldLength); |
440 | |
441 | // cursor and range handling below |
442 | |
443 | // no cursors in this block, no work to do.. |
444 | if (m_cursors.empty()) { |
445 | return; |
446 | } |
447 | |
448 | // move all cursors on the line which has the text removed |
449 | // remember all ranges modified, optimize for the standard case of a few ranges |
450 | QVarLengthArray<TextRange *, 32> changedRanges; |
451 | for (TextCursor *cursor : m_cursors) { |
452 | // skip cursors not on this line! |
453 | if (cursor->lineInBlock() != line) { |
454 | continue; |
455 | } |
456 | |
457 | // skip cursors with too small column |
458 | if (cursor->column() <= range.start().column()) { |
459 | continue; |
460 | } |
461 | |
462 | // patch column of cursor |
463 | if (cursor->column() <= range.end().column()) { |
464 | cursor->m_column = range.start().column(); |
465 | } else { |
466 | cursor->m_column -= (range.end().column() - range.start().column()); |
467 | } |
468 | |
469 | // remember range, if any, avoid double insert |
470 | // we only need to trigger checkValidity later if the range has feedback or might be invalidated |
471 | auto range = cursor->kateRange(); |
472 | if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) { |
473 | range->setValidityCheckRequired(); |
474 | changedRanges.push_back(t: range); |
475 | } |
476 | } |
477 | |
478 | // we might need to invalidate ranges or notify about their changes |
479 | // checkValidity might trigger delete of the range! |
480 | for (TextRange *range : std::as_const(t&: changedRanges)) { |
481 | range->checkValidity(); |
482 | } |
483 | } |
484 | |
485 | void TextBlock::debugPrint(int blockIndex) const |
486 | { |
487 | // print all blocks |
488 | for (size_t i = 0; i < m_lines.size(); ++i) { |
489 | printf(format: "%4d - %4llu : %4llu : '%s'\n" , |
490 | blockIndex, |
491 | (unsigned long long)startLine() + i, |
492 | (unsigned long long)m_lines.at(n: i).text().size(), |
493 | qPrintable(m_lines.at(i).text())); |
494 | } |
495 | } |
496 | |
497 | void TextBlock::splitBlock(int fromLine, TextBlock *newBlock) |
498 | { |
499 | Q_ASSERT(newBlock->m_cursors.empty()); |
500 | // move lines |
501 | auto myLinesToMoveBegin = m_lines.begin() + fromLine; |
502 | auto myLinesToMoveEnd = m_lines.end(); |
503 | int blockSizeChange = myLinesToMoveEnd - myLinesToMoveBegin;// how many newlines |
504 | std::for_each(first: myLinesToMoveBegin, last: myLinesToMoveEnd, f: [&blockSizeChange] (const TextLine &line) -> void { |
505 | blockSizeChange += line.length();// how many non-newlines |
506 | }); |
507 | m_buffer->m_blockSizes[m_blockIndex] -= blockSizeChange; |
508 | m_buffer->m_blockSizes[newBlock->m_blockIndex] += blockSizeChange; |
509 | newBlock->m_lines.insert(position: newBlock->m_lines.cend(), first: std::make_move_iterator(i: myLinesToMoveBegin), last: std::make_move_iterator(i: myLinesToMoveEnd)); |
510 | m_lines.resize(new_size: fromLine); |
511 | |
512 | // move cursors |
513 | QSet<Kate::TextRange *> ranges; |
514 | for (auto it = m_cursors.begin(); it != m_cursors.end();) { |
515 | auto cursor = *it; |
516 | if (cursor->lineInBlock() >= fromLine) { |
517 | cursor->m_line = cursor->lineInBlock() - fromLine; |
518 | cursor->m_block = newBlock; |
519 | |
520 | // add to new, remove from current |
521 | newBlock->m_cursors.push_back(x: cursor); |
522 | it = m_cursors.erase(position: it); |
523 | if (cursor->kateRange()) { |
524 | ranges.insert(value: cursor->kateRange()); |
525 | } |
526 | } else { |
527 | // keep in current |
528 | ++it; |
529 | } |
530 | } |
531 | Q_ASSERT(std::is_sorted(newBlock->m_cursors.cbegin(), newBlock->m_cursors.cend())); |
532 | |
533 | for (auto range : std::as_const(t&: ranges)) { |
534 | if (range->spansMultipleBlocks()) { |
535 | m_buffer->addMultilineRange(range); |
536 | } |
537 | } |
538 | } |
539 | |
540 | void TextBlock::mergeBlock(TextBlock *targetBlock) |
541 | { |
542 | // This function moves everything from *this into *targetBlock. |
543 | // *targetBlock exists before *this with no blocks between. |
544 | // Both this->m_cursors and targetBlock->m_cursors are sorted. |
545 | |
546 | // Iterating m_cursors backwards to modify TextRange's m_end before m_start. |
547 | std::for_each(first: m_cursors.crbegin(), last: m_cursors.crend(), f: [targetBlockLines = targetBlock->lines(), targetBlock, m_buffer = m_buffer] (TextCursor *cursor) -> void { |
548 | cursor->m_line += targetBlockLines; |
549 | cursor->m_block = targetBlock; |
550 | TextRange *range = cursor->m_range; |
551 | if (range && cursor == &range->m_end && targetBlock == range->m_start.m_block) { |
552 | m_buffer->removeMultilineRange(range); |
553 | } |
554 | }); |
555 | // move cursors |
556 | auto first_insertion_pos = targetBlock->m_cursors.insert(position: targetBlock->m_cursors.cend(), first: m_cursors.cbegin(), last: m_cursors.cend()); |
557 | m_cursors.clear(); |
558 | // keep targetBlock->m_cursors sorted |
559 | std::inplace_merge(first: targetBlock->m_cursors.begin(), middle: first_insertion_pos, last: targetBlock->m_cursors.end()); |
560 | Q_ASSERT(std::is_sorted(targetBlock->m_cursors.cbegin(), targetBlock->m_cursors.cend())); |
561 | // move lines |
562 | targetBlock->m_lines.insert(position: targetBlock->m_lines.cend(), first: std::make_move_iterator(i: m_lines.begin()), last: std::make_move_iterator(i: m_lines.end())); |
563 | m_lines.clear(); |
564 | } |
565 | |
566 | void TextBlock::(const int line, KTextEditor::View *view, bool rangesWithAttributeOnly, QList<TextRange *> &outRanges) const |
567 | { |
568 | const int lineInBlock = line - startLine(); // line number in block |
569 | for (TextCursor *cursor : std::as_const(t: m_cursors)) { |
570 | if (!cursor->kateRange()) { |
571 | continue; |
572 | } |
573 | TextRange *range = cursor->kateRange(); |
574 | if (rangesWithAttributeOnly && !range->hasAttribute()) { |
575 | continue; |
576 | } |
577 | |
578 | // we want ranges for no view, but this one's attribute is only valid for views |
579 | if (!view && range->attributeOnlyForViews()) { |
580 | continue; |
581 | } |
582 | |
583 | // the range's attribute is not valid for this view |
584 | if (range->view() && range->view() != view) { |
585 | continue; |
586 | } |
587 | |
588 | if ( |
589 | // simple case |
590 | (cursor->lineInBlock() == lineInBlock) || |
591 | // if line is in the range, ok |
592 | (range->startInternal().lineInternal() <= line && line <= range->endInternal().lineInternal()) |
593 | ) { |
594 | outRanges.append(t: range); |
595 | } |
596 | } |
597 | } |
598 | |
599 | void TextBlock::markModifiedLinesAsSaved() |
600 | { |
601 | // mark all modified lines as saved |
602 | for (auto &textLine : m_lines) { |
603 | if (textLine.markedAsModified()) { |
604 | textLine.markAsSavedOnDisk(savedOnDisk: true); |
605 | } |
606 | } |
607 | } |
608 | } |
609 | |