KTextEditor

katetextblock.cpp
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
12namespace Kate
13{
14TextBlock::TextBlock(TextBuffer *buffer, int startLine)
15 : m_buffer(buffer)
16 , m_startLine(startLine)
17{
18 // reserve the block size
19 m_lines.reserve(BufferBlockSize);
20}
21
23{
24 // blocks should be empty before they are deleted!
25 Q_ASSERT(m_blockSize == 0);
26 Q_ASSERT(m_lines.empty());
27 Q_ASSERT(m_cursors.empty());
28
29 // it only is a hint for ranges for this block, not the storage of them
30}
31
32void TextBlock::setStartLine(int startLine)
33{
34 // allow only valid lines
35 Q_ASSERT(startLine >= 0);
37
38 m_startLine = startLine;
39}
40
42{
43 // right input
45
46 // get text line, at will bail out on out-of-range
47 return m_lines.at(line - startLine());
48}
49
50void TextBlock::setLineMetaData(int line, const TextLine &textLine)
51{
52 // right input
54
55 // set stuff, at will bail out on out-of-range
56 const QString originalText = m_lines.at(line - startLine()).text();
57 m_lines.at(line - startLine()) = textLine;
58 m_lines.at(line - startLine()).text() = originalText;
59}
60
61void TextBlock::appendLine(const QString &textOfLine)
62{
63 m_lines.emplace_back(textOfLine);
64 m_blockSize += textOfLine.size();
65}
66
68{
69 m_lines.clear();
70 m_blockSize = 0;
71}
72
73void TextBlock::text(QString &text) const
74{
75 // combine all lines
76 for (size_t i = 0; i < m_lines.size(); ++i) {
77 // not first line, insert \n
78 if (i > 0 || startLine() > 0) {
79 text.append(QLatin1Char('\n'));
80 }
81
82 text.append(m_lines.at(i).text());
83 }
84}
85
86void TextBlock::wrapLine(const KTextEditor::Cursor position, int fixStartLinesStartIndex)
87{
88 // calc internal line
89 int line = position.line() - startLine();
90
91 // get text, copy, we might invalidate the reference
92 const QString text = m_lines.at(line).text();
93
94 // check if valid column
95 Q_ASSERT(position.column() >= 0);
96 Q_ASSERT(position.column() <= text.size());
97
98 // create new line and insert it
99 m_lines.insert(m_lines.begin() + line + 1, TextLine());
100
101 // cases for modification:
102 // 1. line is wrapped in the middle
103 // 2. if empty line is wrapped, mark new line as modified
104 // 3. line-to-be-wrapped is already modified
105 if (position.column() > 0 || text.size() == 0 || m_lines.at(line).markedAsModified()) {
106 m_lines.at(line + 1).markAsModified(true);
107 } else if (m_lines.at(line).markedAsSavedOnDisk()) {
108 m_lines.at(line + 1).markAsSavedOnDisk(true);
109 }
110
111 // perhaps remove some text from previous line and append it
112 if (position.column() < text.size()) {
113 // text from old line moved first to new one
114 m_lines.at(line + 1).text() = text.right(text.size() - position.column());
115
116 // now remove wrapped text from old line
117 m_lines.at(line).text().chop(text.size() - position.column());
118
119 // mark line as modified
120 m_lines.at(line).markAsModified(true);
121 }
122
123 // fix all start lines
124 // we need to do this NOW, else the range update will FAIL!
125 // bug 313759
126 m_buffer->fixStartLines(fixStartLinesStartIndex);
127
128 // notify the text history
129 m_buffer->history().wrapLine(position);
130
131 // cursor and range handling below
132
133 // no cursors will leave or join this block
134
135 // no cursors in this block, no work to do..
136 if (m_cursors.empty()) {
137 return;
138 }
139
140 // move all cursors on the line which has the text inserted
141 // remember all ranges modified, optimize for the standard case of a few ranges
143 for (TextCursor *cursor : m_cursors) {
144 // skip cursors on lines in front of the wrapped one!
145 if (cursor->lineInBlock() < line) {
146 continue;
147 }
148
149 // either this is simple, line behind the wrapped one
150 if (cursor->lineInBlock() > line) {
151 // patch line of cursor
152 cursor->m_line++;
153 }
154
155 // this is the wrapped line
156 else {
157 // skip cursors with too small column
158 if (cursor->column() <= position.column()) {
159 if (cursor->column() < position.column() || !cursor->m_moveOnInsert) {
160 continue;
161 }
162 }
163
164 // move cursor
165
166 // patch line of cursor
167 cursor->m_line++;
168
169 // patch column
170 cursor->m_column -= position.column();
171 }
172
173 // remember range, if any, avoid double insert
174 auto range = cursor->kateRange();
175 if (range && !range->isValidityCheckRequired()) {
176 range->setValidityCheckRequired();
177 changedRanges.push_back(range);
178 }
179 }
180
181 // we might need to invalidate ranges or notify about their changes
182 // checkValidity might trigger delete of the range!
183 for (TextRange *range : std::as_const(changedRanges)) {
184 // we need to do updateRange to ALWAYS ensure the line => range and back cache is updated
185 // see MovingRangeTest::testLineWrapOrUnwrapUpdateRangeForLineCache
186 updateRange(range);
187
188 // in addition: ensure that we really invalidate bad ranges!
189 range->checkValidity(range->toLineRange());
190 }
191}
192
193void TextBlock::unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex)
194{
195 // calc internal line
196 line = line - startLine();
197
198 // two possiblities: either first line of this block or later line
199 if (line == 0) {
200 // we need previous block with at least one line
202 Q_ASSERT(previousBlock->lines() > 0);
203
204 // move last line of previous block to this one, might result in empty block
205 const TextLine oldFirst = m_lines.at(0);
206 int lastLineOfPreviousBlock = previousBlock->lines() - 1;
207 m_lines[0] = previousBlock->m_lines.back();
208 previousBlock->m_lines.erase(previousBlock->m_lines.begin() + (previousBlock->lines() - 1));
209
210 const int oldSizeOfPreviousLine = m_lines[0].text().size();
211 if (oldFirst.length() > 0) {
212 // append text
213 m_lines[0].text().append(oldFirst.text());
214
215 // mark line as modified, since text was appended
216 m_lines[0].markAsModified(true);
217 }
218
219 // patch startLine of this block
220 --m_startLine;
221
222 // fix all start lines
223 // we need to do this NOW, else the range update will FAIL!
224 // bug 313759
225 m_buffer->fixStartLines(fixStartLinesStartIndex);
226
227 // notify the text history in advance
228 m_buffer->history().unwrapLine(startLine() + line, oldSizeOfPreviousLine);
229
230 // cursor and range handling below
231
232 // no cursors in this block and the previous one, no work to do..
233 if (m_cursors.empty() && previousBlock->m_cursors.empty()) {
234 return;
235 }
236
237 // move all cursors because of the unwrapped line
238 // remember all ranges modified, optimize for the standard case of a few ranges
240 for (TextCursor *cursor : m_cursors) {
241 // this is the unwrapped line
242 if (cursor->lineInBlock() == 0) {
243 // patch column
244 cursor->m_column += oldSizeOfPreviousLine;
245
246 // remember range, if any, avoid double insert
247 auto range = cursor->kateRange();
248 if (range && !range->isValidityCheckRequired()) {
249 range->setValidityCheckRequired();
250 changedRanges.push_back(range);
251 }
252 }
253 }
254
255 // move cursors of the moved line from previous block to this block now
256 for (auto it = previousBlock->m_cursors.begin(); it != previousBlock->m_cursors.end();) {
257 auto cursor = *it;
258 if (cursor->lineInBlock() == lastLineOfPreviousBlock) {
259 cursor->m_line = 0;
260 cursor->m_block = this;
261 m_cursors.insert(cursor);
262
263 // remember range, if any, avoid double insert
264 auto range = cursor->kateRange();
265 if (range && !range->isValidityCheckRequired()) {
266 range->setValidityCheckRequired();
267 changedRanges.push_back(range);
268 }
269
270 // remove from previous block
271 it = previousBlock->m_cursors.erase(it);
272 } else {
273 // keep in previous block
274 ++it;
275 }
276 }
277
278 // fixup the ranges that might be effected, because they moved from last line to this block
279 // we might need to invalidate ranges or notify about their changes
280 // checkValidity might trigger delete of the range!
281 for (TextRange *range : std::as_const(changedRanges)) {
282 // update both blocks
283 updateRange(range);
284 previousBlock->updateRange(range);
285
286 // afterwards check validity, might delete this range!
287 range->checkValidity(range->toLineRange());
288 }
289
290 // be done
291 return;
292 }
293
294 // easy: just move text to previous line and remove current one
295 const int oldSizeOfPreviousLine = m_lines.at(line - 1).length();
296 const int sizeOfCurrentLine = m_lines.at(line).length();
297 if (sizeOfCurrentLine > 0) {
298 m_lines.at(line - 1).text().append(m_lines.at(line).text());
299 }
300
301 const bool lineChanged = (oldSizeOfPreviousLine > 0 && m_lines.at(line - 1).markedAsModified())
302 || (sizeOfCurrentLine > 0 && (oldSizeOfPreviousLine > 0 || m_lines.at(line).markedAsModified()));
303 m_lines.at(line - 1).markAsModified(lineChanged);
304 if (oldSizeOfPreviousLine == 0 && m_lines.at(line).markedAsSavedOnDisk()) {
305 m_lines.at(line - 1).markAsSavedOnDisk(true);
306 }
307
308 m_lines.erase(m_lines.begin() + line);
309
310 // fix all start lines
311 // we need to do this NOW, else the range update will FAIL!
312 // bug 313759
313 m_buffer->fixStartLines(fixStartLinesStartIndex);
314
315 // notify the text history in advance
316 m_buffer->history().unwrapLine(startLine() + line, oldSizeOfPreviousLine);
317
318 // cursor and range handling below
319
320 // no cursors in this block, no work to do..
321 if (m_cursors.empty()) {
322 return;
323 }
324
325 // move all cursors because of the unwrapped line
326 // remember all ranges modified, optimize for the standard case of a few ranges
328 for (TextCursor *cursor : m_cursors) {
329 // skip cursors in lines in front of removed one
330 if (cursor->lineInBlock() < line) {
331 continue;
332 }
333
334 // this is the unwrapped line
335 if (cursor->lineInBlock() == line) {
336 // patch column
337 cursor->m_column += oldSizeOfPreviousLine;
338 }
339
340 // patch line of cursor
341 cursor->m_line--;
342
343 // remember range, if any, avoid double insert
344 auto range = cursor->kateRange();
345 if (range && !range->isValidityCheckRequired()) {
346 range->setValidityCheckRequired();
347 changedRanges.push_back(range);
348 }
349 }
350
351 // we might need to invalidate ranges or notify about their changes
352 // checkValidity might trigger delete of the range!
353 for (TextRange *range : std::as_const(changedRanges)) {
354 // we need to do updateRange to ALWAYS ensure the line => range and back cache is updated
355 // see MovingRangeTest::testLineWrapOrUnwrapUpdateRangeForLineCache
356 updateRange(range);
357
358 // in addition: ensure that we really invalidate bad ranges!
359 range->checkValidity(range->toLineRange());
360 }
361}
362
363void TextBlock::insertText(const KTextEditor::Cursor position, const QString &text)
364{
365 // calc internal line
366 int line = position.line() - startLine();
367
368 // get text
369 QString &textOfLine = m_lines.at(line).text();
370 int oldLength = textOfLine.size();
371 m_lines.at(line).markAsModified(true);
372
373 // check if valid column
374 Q_ASSERT(position.column() >= 0);
375 Q_ASSERT(position.column() <= textOfLine.size());
376
377 // insert text
378 textOfLine.insert(position.column(), text);
379
380 // notify the text history
381 m_buffer->history().insertText(position, text.size(), oldLength);
382
383 m_blockSize += text.size();
384
385 // cursor and range handling below
386
387 // no cursors in this block, no work to do..
388 if (m_cursors.empty()) {
389 return;
390 }
391
392 // move all cursors on the line which has the text inserted
393 // remember all ranges modified, optimize for the standard case of a few ranges
395 for (TextCursor *cursor : m_cursors) {
396 // skip cursors not on this line!
397 if (cursor->lineInBlock() != line) {
398 continue;
399 }
400
401 // skip cursors with too small column
402 if (cursor->column() <= position.column()) {
403 if (cursor->column() < position.column() || !cursor->m_moveOnInsert) {
404 continue;
405 }
406 }
407
408 // patch column of cursor
409 if (cursor->m_column <= oldLength) {
410 cursor->m_column += text.size();
411 }
412
413 // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode
414 else if (cursor->m_column < textOfLine.size()) {
415 cursor->m_column = textOfLine.size();
416 }
417
418 // remember range, if any, avoid double insert
419 // we only need to trigger checkValidity later if the range has feedback or might be invalidated
420 auto range = cursor->kateRange();
421 if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) {
422 range->setValidityCheckRequired();
423 changedRanges.push_back(range);
424 }
425 }
426
427 // we might need to invalidate ranges or notify about their changes
428 // checkValidity might trigger delete of the range!
429 for (TextRange *range : std::as_const(changedRanges)) {
430 range->checkValidity(range->toLineRange());
431 }
432}
433
435{
436 // calc internal line
437 int line = range.start().line() - startLine();
438
439 // get text
440 QString &textOfLine = m_lines.at(line).text();
441 int oldLength = textOfLine.size();
442
443 // check if valid column
444 Q_ASSERT(range.start().column() >= 0);
445 Q_ASSERT(range.start().column() <= textOfLine.size());
446 Q_ASSERT(range.end().column() >= 0);
447 Q_ASSERT(range.end().column() <= textOfLine.size());
448
449 // get text which will be removed
450 removedText = textOfLine.mid(range.start().column(), range.end().column() - range.start().column());
451
452 // remove text
453 textOfLine.remove(range.start().column(), range.end().column() - range.start().column());
454 m_lines.at(line).markAsModified(true);
455
456 // notify the text history
457 m_buffer->history().removeText(range, oldLength);
458
459 m_blockSize -= removedText.size();
460
461 // cursor and range handling below
462
463 // no cursors in this block, no work to do..
464 if (m_cursors.empty()) {
465 return;
466 }
467
468 // move all cursors on the line which has the text removed
469 // remember all ranges modified, optimize for the standard case of a few ranges
471 for (TextCursor *cursor : m_cursors) {
472 // skip cursors not on this line!
473 if (cursor->lineInBlock() != line) {
474 continue;
475 }
476
477 // skip cursors with too small column
478 if (cursor->column() <= range.start().column()) {
479 continue;
480 }
481
482 // patch column of cursor
483 if (cursor->column() <= range.end().column()) {
484 cursor->m_column = range.start().column();
485 } else {
486 cursor->m_column -= (range.end().column() - range.start().column());
487 }
488
489 // remember range, if any, avoid double insert
490 // we only need to trigger checkValidity later if the range has feedback or might be invalidated
491 auto range = cursor->kateRange();
492 if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) {
493 range->setValidityCheckRequired();
494 changedRanges.push_back(range);
495 }
496 }
497
498 // we might need to invalidate ranges or notify about their changes
499 // checkValidity might trigger delete of the range!
500 for (TextRange *range : std::as_const(changedRanges)) {
501 range->checkValidity(range->toLineRange());
502 }
503}
504
505void TextBlock::debugPrint(int blockIndex) const
506{
507 // print all blocks
508 for (size_t i = 0; i < m_lines.size(); ++i) {
509 printf("%4d - %4llu : %4llu : '%s'\n",
511 (unsigned long long)startLine() + i,
512 (unsigned long long)m_lines.at(i).text().size(),
513 qPrintable(m_lines.at(i).text()));
514 }
515}
516
518{
519 // half the block
521
522 // create and insert new block
523 TextBlock *newBlock = new TextBlock(m_buffer, startLine() + fromLine);
524
525 // move lines
526 newBlock->m_lines.reserve(linesOfNewBlock);
527 for (size_t i = fromLine; i < m_lines.size(); ++i) {
528 auto line = std::move(m_lines[i]);
529 m_blockSize -= line.length();
530 newBlock->m_blockSize += line.length();
531 newBlock->m_lines.push_back(std::move(line));
532 }
533
534 m_lines.resize(fromLine);
535
536 // move cursors
537 for (auto it = m_cursors.begin(); it != m_cursors.end();) {
538 auto cursor = *it;
539 if (cursor->lineInBlock() >= fromLine) {
540 cursor->m_line = cursor->lineInBlock() - fromLine;
541 cursor->m_block = newBlock;
542
543 // add to new, remove from current
544 newBlock->m_cursors.insert(cursor);
545 it = m_cursors.erase(it);
546 } else {
547 // keep in current
548 ++it;
549 }
550 }
551
552 // fix ALL ranges!
553 // copy is necessary as update range may modify the uncached ranges
554 std::vector<TextRange *> allRanges;
555 allRanges.reserve(m_uncachedRanges.size() + m_cachedLineForRanges.size());
556 std::for_each(m_cachedLineForRanges.keyBegin(), m_cachedLineForRanges.keyEnd(), [&allRanges](TextRange *range) {
557 allRanges.push_back(range);
558 });
559 allRanges.insert(allRanges.end(), m_uncachedRanges.begin(), m_uncachedRanges.end());
560 for (TextRange *range : allRanges) {
561 // update both blocks
562 updateRange(range);
563 newBlock->updateRange(range);
564 }
565
566 // return the new generated block
567 return newBlock;
568}
569
571{
572 // move cursors, do this first, now still lines() count is correct for target
573 for (TextCursor *cursor : m_cursors) {
574 cursor->m_line = cursor->lineInBlock() + targetBlock->lines();
575 cursor->m_block = targetBlock;
576 targetBlock->m_cursors.insert(cursor);
577 }
578 m_cursors.clear();
579
580 // move lines
581 targetBlock->m_lines.reserve(targetBlock->lines() + lines());
582 for (size_t i = 0; i < m_lines.size(); ++i) {
583 targetBlock->m_lines.push_back(m_lines.at(i));
584 }
585 targetBlock->m_blockSize += m_blockSize;
586 clearLines();
587
588 // fix ALL ranges!
589 // copy is necessary as update range may modify the uncached ranges
590 std::vector<TextRange *> allRanges;
591 allRanges.reserve(m_uncachedRanges.size() + m_cachedLineForRanges.size());
592 std::for_each(m_cachedLineForRanges.keyBegin(), m_cachedLineForRanges.keyEnd(), [&allRanges](TextRange *range) {
593 allRanges.push_back(range);
594 });
595 allRanges.insert(allRanges.end(), m_uncachedRanges.begin(), m_uncachedRanges.end());
596 for (TextRange *range : allRanges) {
597 // update both blocks
598 updateRange(range);
599 targetBlock->updateRange(range);
600 }
601}
602
604{
605 // kill cursors, if not belonging to a range
606 // we can do in-place editing of the current set of cursors as
607 // we remove them before deleting
608 for (auto it = m_cursors.begin(); it != m_cursors.end();) {
609 auto cursor = *it;
610 if (!cursor->kateRange()) {
611 // remove it and advance to next element
612 it = m_cursors.erase(it);
613
614 // delete after cursor is gone from the set
615 // else the destructor will modify it!
616 delete cursor;
617 } else {
618 // keep this cursor
619 ++it;
620 }
621 }
622
623 // kill lines
624 clearLines();
625}
626
628{
629 // move cursors, if not belonging to a range
630 // we can do in-place editing of the current set of cursors
631 for (auto it = m_cursors.begin(); it != m_cursors.end();) {
632 auto cursor = *it;
633 if (!cursor->kateRange()) {
634 cursor->m_column = 0;
635 cursor->m_line = 0;
636 cursor->m_block = targetBlock;
637 targetBlock->m_cursors.insert(cursor);
638
639 // remove it and advance to next element
640 it = m_cursors.erase(it);
641 } else {
642 // keep this cursor
643 ++it;
644 }
645 }
646
647 // kill lines
648 clearLines();
649}
650
651QList<TextRange *> TextBlock::rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const
652{
653 const auto cachedRanges = cachedRangesForLine(line);
655 ranges.reserve(m_uncachedRanges.size() + (cachedRanges ? cachedRanges->size() : 0));
657 return ranges;
658}
659
660void TextBlock::rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly, QList<TextRange *> &outRanges) const
661{
662 const auto cachedRanges = cachedRangesForLine(line);
663 outRanges.clear();
664
665 auto predicate = [line, view, rangesWithAttributeOnly](TextRange *range) {
666 if (rangesWithAttributeOnly && !range->hasAttribute()) {
667 return false;
668 }
669
670 // we want ranges for no view, but this one's attribute is only valid for views
671 if (!view && range->attributeOnlyForViews()) {
672 return false;
673 }
674
675 // the range's attribute is not valid for this view
676 if (range->view() && range->view() != view) {
677 return false;
678 }
679
680 // if line is in the range, ok
681 if (range->startInternal().lineInternal() <= line && line <= range->endInternal().lineInternal()) {
682 return true;
683 }
684 return false;
685 };
686
687 if (cachedRanges) {
688 std::copy_if(cachedRanges->begin(), cachedRanges->end(), std::back_inserter(outRanges), predicate);
689 }
690 std::copy_if(m_uncachedRanges.begin(), m_uncachedRanges.end(), std::back_inserter(outRanges), predicate);
691}
692
694{
695 // mark all modified lines as saved
696 for (auto &textLine : m_lines) {
697 if (textLine.markedAsModified()) {
698 textLine.markAsSavedOnDisk(true);
699 }
700 }
701}
702
704{
705 // get some simple facts about our nice range
706 const int startLine = range->startInternal().lineInternal();
707 const int endLine = range->endInternal().lineInternal();
708 const bool isSingleLine = startLine == endLine;
709
710 // perhaps remove range and be done
711 if ((endLine < m_startLine) || (startLine >= (m_startLine + lines()))) {
712 removeRange(range);
713 return;
714 }
715
716 // The range is still a single-line range, and is still cached to the correct line.
717 if (isSingleLine) {
718 auto it = m_cachedLineForRanges.find(range);
719 if (it != m_cachedLineForRanges.end() && it.value() == startLine - m_startLine) {
720 return;
721 }
722 }
723
724 // The range is still a multi-line range, and is already in the correct set.
725 if (!isSingleLine && m_uncachedRanges.contains(range)) {
726 return;
727 }
728
729 // remove, if already there!
730 removeRange(range);
731
732 // simple case: multi-line range
733 if (!isSingleLine) {
734 // The range cannot be cached per line, as it spans multiple lines
735 m_uncachedRanges.append(range);
736 return;
737 }
738
739 // The range is contained by a single line, put it into the line-cache
740 const int lineOffset = startLine - m_startLine;
741
742 // enlarge cache if needed
743 if (m_cachedRangesForLine.size() <= (size_t)lineOffset) {
744 m_cachedRangesForLine.resize(lineOffset + 1);
745 }
746
747 // insert into mapping
748 if (!m_cachedRangesForLine[lineOffset].contains(range)) {
749 m_cachedRangesForLine[lineOffset].push_back(range);
750 }
751 m_cachedLineForRanges[range] = lineOffset;
752}
753
755{
756 // uncached range? remove it and be done
757 int pos = m_uncachedRanges.indexOf(range);
758 if (pos != -1) {
759 m_uncachedRanges.remove(pos);
760 // must be only uncached!
761 Q_ASSERT(m_cachedLineForRanges.find(range) == m_cachedLineForRanges.end());
762 return;
763 }
764
765 // cached range?
766 auto it = m_cachedLineForRanges.find(range);
767 if (it != m_cachedLineForRanges.end()) {
768 // must be only cached!
769 Q_ASSERT(!m_uncachedRanges.contains(range));
770
771 int line = it.value();
772
773 // query the range from cache, must be there
774 int idx = m_cachedRangesForLine[line].indexOf(range);
775 Q_ASSERT(idx != -1);
776
777 // remove it and be done
778 m_cachedRangesForLine[line].remove(idx);
779 m_cachedLineForRanges.erase(it);
780 return;
781 }
782
783 // else: range was not for this block, just do nothing, removeRange should be "safe" to use
784}
785
786}
The Cursor represents a position in a Document.
Definition cursor.h:75
constexpr int column() const noexcept
Retrieve the column on which this cursor is situated.
Definition cursor.h:192
constexpr int line() const noexcept
Retrieve the line on which this cursor is situated.
Definition cursor.h:174
An object representing a section of text, from one Cursor to another.
constexpr LineRange toLineRange() const noexcept
Convert this Range to a LineRange.
constexpr Cursor end() const noexcept
Get the end position of this range.
constexpr Cursor start() const noexcept
Get the start position of this range.
A text widget with KXMLGUIClient that represents a Document.
Definition view.h:244
Class representing a text block.
void clearBlockContent(TextBlock *targetBlock)
Clear the block content, delete all lines, move all cursors not bound to range to given block at 0,...
void setStartLine(int startLine)
Set start line of this block.
int startLine() const
Start line of this block.
void wrapLine(const KTextEditor::Cursor position, int fixStartLinesStartIndex)
Wrap line at given cursor position.
void deleteBlockContent()
Delete the block content, delete all lines and delete all cursors not bound to ranges.
TextBlock(TextBuffer *buffer, int startLine)
Construct an empty text block.
void insertText(const KTextEditor::Cursor position, const QString &text)
Insert text at given cursor position.
void mergeBlock(TextBlock *targetBlock)
Merge this block with given one, the given one must be a direct predecessor.
void removeText(KTextEditor::Range range, QString &removedText)
Remove text at given range.
KTEXTEDITOR_EXPORT QList< TextRange * > rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const
Return all ranges in this block which might intersect the given line.
void text(QString &text) const
Retrieve text of block.
TextBlock * splitBlock(int fromLine)
Split given block.
void appendLine(const QString &textOfLine)
Append a new line with given text.
void setLineMetaData(int line, const TextLine &textLine)
Transfer all non text attributes for the given line from the given text line to the one in the block.
void clearLines()
Clear the lines.
~TextBlock()
Destruct the text block.
void markModifiedLinesAsSaved()
Flag all modified text lines as saved on disk.
TextLine line(int line) const
Retrieve a text line.
void updateRange(TextRange *range)
Update a range from this block.
void unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex)
Unwrap given line.
int lines() const
Number of lines in this block.
void debugPrint(int blockIndex) const
Debug output, print whole block content with line numbers and line length.
void removeRange(TextRange *range)
Remove a range from this block.
Class representing a text buffer.
TextHistory & history()
TextHistory of this buffer.
Class representing a 'clever' text cursor.
int lineInternal() const
Non-virtual version of line(), which is faster.
Class representing a single text line.
int length() const
Returns the line's length.
Class representing a 'clever' text range.
const TextCursor & startInternal() const
Non-virtual version of start(), which is faster.
const TextCursor & endInternal() const
Nonvirtual version of end(), which is faster.
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:15:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.