1 | /* This file is part of the Kate project. |
2 | * |
3 | * Copyright (C) 2010 Christoph Cullmann <cullmann@kde.org> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "katetextblock.h" |
22 | #include "katetextbuffer.h" |
23 | |
24 | namespace Kate { |
25 | |
26 | TextBlock::TextBlock (TextBuffer *buffer, int startLine) |
27 | : m_buffer (buffer) |
28 | , m_startLine (startLine) |
29 | { |
30 | // reserve the block size |
31 | m_lines.reserve (m_buffer->m_blockSize); |
32 | } |
33 | |
34 | TextBlock::~TextBlock () |
35 | { |
36 | // blocks should be empty before they are deleted! |
37 | Q_ASSERT (m_lines.empty()); |
38 | Q_ASSERT (m_cursors.empty()); |
39 | |
40 | // it only is a hint for ranges for this block, not the storage of them |
41 | } |
42 | |
43 | void TextBlock::setStartLine (int startLine) |
44 | { |
45 | // allow only valid lines |
46 | Q_ASSERT (startLine >= 0); |
47 | Q_ASSERT (startLine < m_buffer->lines ()); |
48 | |
49 | m_startLine = startLine; |
50 | } |
51 | |
52 | TextLine TextBlock::line (int line) const |
53 | { |
54 | // right input |
55 | Q_ASSERT (line >= startLine ()); |
56 | |
57 | // calc internal line |
58 | line = line - startLine (); |
59 | |
60 | // in range |
61 | Q_ASSERT (line < m_lines.size ()); |
62 | |
63 | // get text line |
64 | return m_lines.at(line); |
65 | } |
66 | |
67 | void TextBlock::appendLine (const QString &textOfLine) |
68 | { |
69 | m_lines.append (TextLine (new TextLineData(textOfLine))); |
70 | } |
71 | |
72 | void TextBlock::clearLines () |
73 | { |
74 | m_lines.clear (); |
75 | } |
76 | |
77 | void TextBlock::text (QString &text) const |
78 | { |
79 | // combine all lines |
80 | for (int i = 0; i < m_lines.size(); ++i) { |
81 | // not first line, insert \n |
82 | if (i > 0 || startLine() > 0) |
83 | text.append ('\n'); |
84 | |
85 | text.append (m_lines.at(i)->text ()); |
86 | } |
87 | } |
88 | |
89 | void TextBlock::wrapLine (const KTextEditor::Cursor &position, int fixStartLinesStartIndex) |
90 | { |
91 | // calc internal line |
92 | int line = position.line () - startLine (); |
93 | |
94 | // get text |
95 | QString &text = m_lines.at(line)->textReadWrite (); |
96 | |
97 | // check if valid column |
98 | Q_ASSERT (position.column() >= 0); |
99 | Q_ASSERT (position.column() <= text.size()); |
100 | |
101 | // create new line and insert it |
102 | m_lines.insert (m_lines.begin() + line + 1, TextLine (new TextLineData())); |
103 | |
104 | // cases for modification: |
105 | // 1. line is wrapped in the middle |
106 | // 2. if empty line is wrapped, mark new line as modified |
107 | // 3. line-to-be-wrapped is already modified |
108 | if (position.column() > 0 || text.size() == 0 || m_lines.at(line)->markedAsModified()) { |
109 | m_lines.at(line + 1)->markAsModified(true); |
110 | } else if (m_lines.at(line)->markedAsSavedOnDisk()) { |
111 | m_lines.at(line + 1)->markAsSavedOnDisk(true); |
112 | } |
113 | |
114 | // perhaps remove some text from previous line and append it |
115 | if (position.column() < text.size ()) { |
116 | // text from old line moved first to new one |
117 | m_lines.at(line+1)->textReadWrite() = text.right (text.size() - position.column()); |
118 | |
119 | // now remove wrapped text from old line |
120 | text.chop (text.size() - position.column()); |
121 | |
122 | // mark line as modified |
123 | m_lines.at(line)->markAsModified(true); |
124 | } |
125 | |
126 | /** |
127 | * fix all start lines |
128 | * we need to do this NOW, else the range update will FAIL! |
129 | * bug 313759 |
130 | */ |
131 | m_buffer->fixStartLines (fixStartLinesStartIndex); |
132 | |
133 | /** |
134 | * notify the text history |
135 | */ |
136 | m_buffer->history().wrapLine (position); |
137 | |
138 | /** |
139 | * cursor and range handling below |
140 | */ |
141 | |
142 | // no cursors will leave or join this block |
143 | |
144 | // no cursors in this block, no work to do.. |
145 | if (m_cursors.empty()) |
146 | return; |
147 | |
148 | // move all cursors on the line which has the text inserted |
149 | // remember all ranges modified |
150 | QSet<TextRange *> changedRanges; |
151 | foreach (TextCursor *cursor, m_cursors) { |
152 | // skip cursors on lines in front of the wrapped one! |
153 | if (cursor->lineInBlock() < line) |
154 | continue; |
155 | |
156 | // either this is simple, line behind the wrapped one |
157 | if (cursor->lineInBlock() > line) { |
158 | // patch line of cursor |
159 | cursor->m_line++; |
160 | } |
161 | |
162 | // this is the wrapped line |
163 | else { |
164 | // skip cursors with too small column |
165 | if (cursor->column() <= position.column()) { |
166 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) |
167 | continue; |
168 | } |
169 | |
170 | // move cursor |
171 | |
172 | // patch line of cursor |
173 | cursor->m_line++; |
174 | |
175 | // patch column |
176 | cursor->m_column -= position.column(); |
177 | } |
178 | |
179 | // remember range, if any |
180 | if (cursor->kateRange()) |
181 | changedRanges.insert (cursor->kateRange()); |
182 | } |
183 | |
184 | // check validity of all ranges, might invalidate them... |
185 | foreach (TextRange *range, changedRanges) |
186 | range->checkValidity (); |
187 | } |
188 | |
189 | void TextBlock::unwrapLine (int line, TextBlock *previousBlock, int fixStartLinesStartIndex) |
190 | { |
191 | // calc internal line |
192 | line = line - startLine (); |
193 | |
194 | // two possiblities: either first line of this block or later line |
195 | if (line == 0) { |
196 | // we need previous block with at least one line |
197 | Q_ASSERT (previousBlock); |
198 | Q_ASSERT (previousBlock->lines () > 0); |
199 | |
200 | // move last line of previous block to this one, might result in empty block |
201 | TextLine oldFirst = m_lines.at(0); |
202 | int lastLineOfPreviousBlock = previousBlock->lines ()-1; |
203 | TextLine newFirst = previousBlock->m_lines.last(); |
204 | m_lines[0] = newFirst; |
205 | previousBlock->m_lines.erase (previousBlock->m_lines.begin() + (previousBlock->lines () - 1)); |
206 | |
207 | const int oldSizeOfPreviousLine = newFirst->text().size(); |
208 | if (oldFirst->length() > 0) { |
209 | // append text |
210 | newFirst->textReadWrite().append (oldFirst->text()); |
211 | |
212 | // mark line as modified, since text was appended |
213 | newFirst->markAsModified(true); |
214 | } |
215 | |
216 | // patch startLine of this block |
217 | --m_startLine; |
218 | |
219 | /** |
220 | * fix all start lines |
221 | * we need to do this NOW, else the range update will FAIL! |
222 | * bug 313759 |
223 | */ |
224 | m_buffer->fixStartLines (fixStartLinesStartIndex); |
225 | |
226 | /** |
227 | * notify the text history in advance |
228 | */ |
229 | m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); |
230 | |
231 | /** |
232 | * cursor and range handling below |
233 | */ |
234 | |
235 | // no cursors in this block and the previous one, no work to do.. |
236 | if (m_cursors.empty() && previousBlock->m_cursors.empty()) |
237 | return; |
238 | |
239 | // move all cursors because of the unwrapped line |
240 | // remember all ranges modified |
241 | QSet<TextRange *> changedRanges; |
242 | foreach (TextCursor *cursor, m_cursors) { |
243 | // this is the unwrapped line |
244 | if (cursor->lineInBlock() == 0) { |
245 | // patch column |
246 | cursor->m_column += oldSizeOfPreviousLine; |
247 | |
248 | // remember range, if any |
249 | if (cursor->kateRange()) |
250 | changedRanges.insert (cursor->kateRange()); |
251 | } |
252 | } |
253 | |
254 | // move cursors of the moved line from previous block to this block now |
255 | QSet<TextCursor *> newPreviousCursors; |
256 | foreach (TextCursor *cursor, previousBlock->m_cursors) { |
257 | if (cursor->lineInBlock() == lastLineOfPreviousBlock) { |
258 | cursor->m_line = 0; |
259 | cursor->m_block = this; |
260 | m_cursors.insert (cursor); |
261 | |
262 | // remember range, if any |
263 | if (cursor->kateRange()) |
264 | changedRanges.insert (cursor->kateRange()); |
265 | } |
266 | else |
267 | newPreviousCursors.insert (cursor); |
268 | } |
269 | previousBlock->m_cursors = newPreviousCursors; |
270 | |
271 | // fixup the ranges that might be effected, because they moved from last line to this block |
272 | foreach (TextRange *range, changedRanges) { |
273 | // update both blocks |
274 | updateRange (range); |
275 | previousBlock->updateRange (range); |
276 | } |
277 | |
278 | // check validity of all ranges, might invalidate them... |
279 | foreach (TextRange *range, changedRanges) |
280 | range->checkValidity (); |
281 | |
282 | // be done |
283 | return; |
284 | } |
285 | |
286 | // easy: just move text to previous line and remove current one |
287 | const int oldSizeOfPreviousLine = m_lines.at(line-1)->length(); |
288 | const int sizeOfCurrentLine = m_lines.at(line)->length(); |
289 | if (sizeOfCurrentLine > 0) |
290 | m_lines.at(line-1)->textReadWrite().append (m_lines.at(line)->text()); |
291 | |
292 | const bool lineChanged = (oldSizeOfPreviousLine > 0 && m_lines.at(line - 1)->markedAsModified()) |
293 | || (sizeOfCurrentLine > 0 && (oldSizeOfPreviousLine > 0 || m_lines.at(line)->markedAsModified())); |
294 | m_lines.at(line-1)->markAsModified(lineChanged); |
295 | if (oldSizeOfPreviousLine == 0 && m_lines.at(line)->markedAsSavedOnDisk()) |
296 | m_lines.at(line-1)->markAsSavedOnDisk(true); |
297 | |
298 | m_lines.erase (m_lines.begin () + line); |
299 | |
300 | /** |
301 | * fix all start lines |
302 | * we need to do this NOW, else the range update will FAIL! |
303 | * bug 313759 |
304 | */ |
305 | m_buffer->fixStartLines (fixStartLinesStartIndex); |
306 | |
307 | /** |
308 | * notify the text history in advance |
309 | */ |
310 | m_buffer->history().unwrapLine (startLine () + line, oldSizeOfPreviousLine); |
311 | |
312 | /** |
313 | * cursor and range handling below |
314 | */ |
315 | |
316 | // no cursors in this block, no work to do.. |
317 | if (m_cursors.empty()) |
318 | return; |
319 | |
320 | // move all cursors because of the unwrapped line |
321 | // remember all ranges modified |
322 | QSet<TextRange *> changedRanges; |
323 | foreach (TextCursor *cursor, m_cursors) { |
324 | // skip cursors in lines in front of removed one |
325 | if (cursor->lineInBlock() < line) |
326 | continue; |
327 | |
328 | // this is the unwrapped line |
329 | if (cursor->lineInBlock() == line) { |
330 | // patch column |
331 | cursor->m_column += oldSizeOfPreviousLine; |
332 | } |
333 | |
334 | // patch line of cursor |
335 | cursor->m_line--; |
336 | |
337 | // remember range, if any |
338 | if (cursor->kateRange()) |
339 | changedRanges.insert (cursor->kateRange()); |
340 | } |
341 | |
342 | // check validity of all ranges, might invalidate them... |
343 | foreach (TextRange *range, changedRanges) |
344 | range->checkValidity (); |
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(line)->textReadWrite (); |
354 | int oldLength = textOfLine.size (); |
355 | m_lines.at(line)->markAsModified(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 (position.column(), text); |
363 | |
364 | /** |
365 | * notify the text history |
366 | */ |
367 | m_buffer->history().insertText (position, text.size(), oldLength); |
368 | |
369 | /** |
370 | * cursor and range handling below |
371 | */ |
372 | |
373 | // no cursors in this block, no work to do.. |
374 | if (m_cursors.empty()) |
375 | return; |
376 | |
377 | // move all cursors on the line which has the text inserted |
378 | // remember all ranges modified |
379 | QSet<TextRange *> changedRanges; |
380 | foreach (TextCursor *cursor, m_cursors) { |
381 | // skip cursors not on this line! |
382 | if (cursor->lineInBlock() != line) |
383 | continue; |
384 | |
385 | // skip cursors with too small column |
386 | if (cursor->column() <= position.column()) { |
387 | if (cursor->column() < position.column() || !cursor->m_moveOnInsert) |
388 | continue; |
389 | } |
390 | |
391 | // patch column of cursor |
392 | if (cursor->m_column <= oldLength) |
393 | cursor->m_column += text.size (); |
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 | // remember range, if any |
400 | if (cursor->kateRange()) |
401 | changedRanges.insert (cursor->kateRange()); |
402 | } |
403 | |
404 | // check validity of all ranges, might invalidate them... |
405 | foreach (TextRange *range, changedRanges) |
406 | range->checkValidity (); |
407 | } |
408 | |
409 | void TextBlock::removeText (const KTextEditor::Range &range, QString &removedText) |
410 | { |
411 | // calc internal line |
412 | int line = range.start().line () - startLine (); |
413 | |
414 | // get text |
415 | QString &textOfLine = m_lines.at(line)->textReadWrite (); |
416 | int oldLength = textOfLine.size (); |
417 | |
418 | // check if valid column |
419 | Q_ASSERT (range.start().column() >= 0); |
420 | Q_ASSERT (range.start().column() <= textOfLine.size()); |
421 | Q_ASSERT (range.end().column() >= 0); |
422 | Q_ASSERT (range.end().column() <= textOfLine.size()); |
423 | |
424 | // get text which will be removed |
425 | removedText = textOfLine.mid (range.start().column(), range.end().column() - range.start().column()); |
426 | |
427 | // remove text |
428 | textOfLine.remove (range.start().column(), range.end().column() - range.start().column()); |
429 | m_lines.at(line)->markAsModified(true); |
430 | |
431 | /** |
432 | * notify the text history |
433 | */ |
434 | m_buffer->history().removeText (range, oldLength); |
435 | |
436 | /** |
437 | * cursor and range handling below |
438 | */ |
439 | |
440 | // no cursors in this block, no work to do.. |
441 | if (m_cursors.empty()) |
442 | return; |
443 | |
444 | // move all cursors on the line which has the text removed |
445 | // remember all ranges modified |
446 | QSet<TextRange *> changedRanges; |
447 | foreach (TextCursor *cursor, m_cursors) { |
448 | // skip cursors not on this line! |
449 | if (cursor->lineInBlock() != line) |
450 | continue; |
451 | |
452 | // skip cursors with too small column |
453 | if (cursor->column() <= range.start().column()) |
454 | continue; |
455 | |
456 | // patch column of cursor |
457 | if (cursor->column() <= range.end().column()) |
458 | cursor->m_column = range.start().column (); |
459 | else |
460 | cursor->m_column -= (range.end().column() - range.start().column()); |
461 | |
462 | // remember range, if any |
463 | if (cursor->kateRange()) |
464 | changedRanges.insert (cursor->kateRange()); |
465 | } |
466 | |
467 | // check validity of all ranges, might invalidate them... |
468 | foreach (TextRange *range, changedRanges) |
469 | range->checkValidity (); |
470 | } |
471 | |
472 | void TextBlock::debugPrint (int blockIndex) const |
473 | { |
474 | // print all blocks |
475 | for (int i = 0; i < m_lines.size(); ++i) |
476 | printf ("%4d - %4d : %4d : '%s'\n" , blockIndex, startLine() + i |
477 | , m_lines.at(i)->text().size(), qPrintable (m_lines.at(i)->text())); |
478 | } |
479 | |
480 | TextBlock *TextBlock::splitBlock (int fromLine) |
481 | { |
482 | // half the block |
483 | int linesOfNewBlock = lines () - fromLine; |
484 | |
485 | // create and insert new block |
486 | TextBlock *newBlock = new TextBlock (m_buffer, startLine() + fromLine); |
487 | |
488 | // move lines |
489 | newBlock->m_lines.reserve (linesOfNewBlock); |
490 | for (int i = fromLine; i < m_lines.size(); ++i) |
491 | newBlock->m_lines.append (m_lines.at(i)); |
492 | m_lines.resize (fromLine); |
493 | |
494 | // move cursors |
495 | QSet<TextCursor*> oldBlockSet; |
496 | foreach (TextCursor *cursor, m_cursors) { |
497 | if (cursor->lineInBlock() >= fromLine) { |
498 | cursor->m_line = cursor->lineInBlock() - fromLine; |
499 | cursor->m_block = newBlock; |
500 | newBlock->m_cursors.insert (cursor); |
501 | } |
502 | else |
503 | oldBlockSet.insert (cursor); |
504 | } |
505 | m_cursors = oldBlockSet; |
506 | |
507 | // fix ALL ranges! |
508 | QList<TextRange*> allRanges = m_uncachedRanges.toList() + m_cachedLineForRanges.keys(); |
509 | foreach (TextRange *range, allRanges) { |
510 | // update both blocks |
511 | updateRange (range); |
512 | newBlock->updateRange (range); |
513 | } |
514 | |
515 | // return the new generated block |
516 | return newBlock; |
517 | } |
518 | |
519 | void TextBlock::mergeBlock (TextBlock *targetBlock) |
520 | { |
521 | // move cursors, do this first, now still lines() count is correct for target |
522 | foreach (TextCursor *cursor, m_cursors) { |
523 | cursor->m_line = cursor->lineInBlock() + targetBlock->lines (); |
524 | cursor->m_block = targetBlock; |
525 | targetBlock->m_cursors.insert (cursor); |
526 | } |
527 | m_cursors.clear (); |
528 | |
529 | // move lines |
530 | targetBlock->m_lines.reserve (targetBlock->lines() + lines ()); |
531 | for (int i = 0; i < m_lines.size(); ++i) |
532 | targetBlock->m_lines.append (m_lines.at(i)); |
533 | m_lines.clear (); |
534 | |
535 | // fix ALL ranges! |
536 | QList<TextRange*> allRanges = m_uncachedRanges.toList() + m_cachedLineForRanges.keys(); |
537 | foreach(TextRange* range, allRanges) { |
538 | // update both blocks |
539 | updateRange (range); |
540 | targetBlock->updateRange (range); |
541 | } |
542 | } |
543 | |
544 | void TextBlock::deleteBlockContent () |
545 | { |
546 | // kill cursors, if not belonging to a range |
547 | QSet<TextCursor *> copy = m_cursors; |
548 | foreach (TextCursor *cursor, copy) |
549 | if (!cursor->kateRange()) |
550 | delete cursor; |
551 | |
552 | // kill lines |
553 | m_lines.clear (); |
554 | } |
555 | |
556 | void TextBlock::clearBlockContent (TextBlock *targetBlock) |
557 | { |
558 | // move cursors, if not belonging to a range |
559 | QSet<TextCursor *> copy = m_cursors; |
560 | foreach (TextCursor *cursor, copy) { |
561 | if (!cursor->kateRange()) { |
562 | cursor->m_column = 0; |
563 | cursor->m_line = 0; |
564 | cursor->m_block = targetBlock; |
565 | targetBlock->m_cursors.insert (cursor); |
566 | m_cursors.remove (cursor); |
567 | } |
568 | } |
569 | |
570 | // kill lines |
571 | m_lines.clear (); |
572 | } |
573 | |
574 | void TextBlock::markModifiedLinesAsSaved () |
575 | { |
576 | // mark all modified lines as saved |
577 | for (int i = 0; i < m_lines.size(); ++i) { |
578 | TextLine textLine = m_lines[i]; |
579 | if (textLine->markedAsModified()) |
580 | textLine->markAsSavedOnDisk(true); |
581 | } |
582 | } |
583 | |
584 | void TextBlock:: (TextRange* range) |
585 | { |
586 | /** |
587 | * get some simple facts about our nice range |
588 | */ |
589 | const int startLine = range->startInternal().lineInternal(); |
590 | const int endLine = range->endInternal().lineInternal(); |
591 | const bool isSingleLine = startLine == endLine; |
592 | |
593 | /** |
594 | * perhaps remove range and be done |
595 | */ |
596 | if ((endLine < m_startLine) || (startLine >= (m_startLine + lines()))) { |
597 | removeRange (range); |
598 | return; |
599 | } |
600 | |
601 | /** |
602 | * The range is still a single-line range, and is still cached to the correct line. |
603 | */ |
604 | if(isSingleLine && m_cachedLineForRanges.contains (range) && (m_cachedLineForRanges.value(range) == startLine - m_startLine)) |
605 | return; |
606 | |
607 | /** |
608 | * The range is still a multi-line range, and is already in the correct set. |
609 | */ |
610 | if(!isSingleLine && m_uncachedRanges.contains (range)) |
611 | return; |
612 | |
613 | /** |
614 | * remove, if already there! |
615 | */ |
616 | removeRange(range); |
617 | |
618 | /** |
619 | * simple case: multi-line range |
620 | */ |
621 | if (!isSingleLine) { |
622 | /** |
623 | * The range cannot be cached per line, as it spans multiple lines |
624 | */ |
625 | m_uncachedRanges.insert(range); |
626 | return; |
627 | } |
628 | |
629 | /** |
630 | * The range is contained by a single line, put it into the line-cache |
631 | */ |
632 | const int lineOffset = startLine - m_startLine; |
633 | |
634 | /** |
635 | * enlarge cache if needed |
636 | */ |
637 | if (m_cachedRangesForLine.size() <= lineOffset) |
638 | m_cachedRangesForLine.resize(lineOffset+1); |
639 | |
640 | /** |
641 | * insert into mapping |
642 | */ |
643 | m_cachedRangesForLine[lineOffset].insert(range); |
644 | m_cachedLineForRanges[range] = lineOffset; |
645 | } |
646 | |
647 | void TextBlock:: (TextRange* range) |
648 | { |
649 | /** |
650 | * uncached range? remove it and be done |
651 | */ |
652 | if(m_uncachedRanges.remove (range)) { |
653 | /** |
654 | * must be only uncached! |
655 | */ |
656 | Q_ASSERT (!m_cachedLineForRanges.contains(range)); |
657 | return; |
658 | } |
659 | |
660 | /** |
661 | * cached range? |
662 | */ |
663 | QHash<TextRange*, int>::iterator it = m_cachedLineForRanges.find(range); |
664 | if (it != m_cachedLineForRanges.end()) { |
665 | /** |
666 | * must be only cached! |
667 | */ |
668 | Q_ASSERT (!m_uncachedRanges.contains(range)); |
669 | |
670 | /** |
671 | * query the range from cache, must be there |
672 | */ |
673 | Q_ASSERT (m_cachedRangesForLine.at(*it).contains(range)); |
674 | |
675 | /** |
676 | * remove it and be done |
677 | */ |
678 | m_cachedRangesForLine[*it].remove(range); |
679 | m_cachedLineForRanges.erase(it); |
680 | return; |
681 | } |
682 | |
683 | /** |
684 | * else: range was not for this block, just do nothing, removeRange should be "safe" to use |
685 | */ |
686 | } |
687 | |
688 | } |
689 | |