KTextEditor

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

KDE's Doxygen guidelines are available online.