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 "katetextline.h"
11 #include "katetextrange.h"
12 
13 
14 namespace Kate
15 {
16 TextBlock::TextBlock(TextBuffer *buffer, int startLine)
17  : m_buffer(buffer)
18  , m_startLine(startLine)
19 {
20  // reserve the block size
21  m_lines.reserve(m_buffer->m_blockSize);
22 }
23 
25 {
26  // blocks should be empty before they are deleted!
27  Q_ASSERT(m_lines.empty());
28  Q_ASSERT(m_cursors.empty());
29 
30  // it only is a hint for ranges for this block, not the storage of them
31 }
32 
34 {
35  // allow only valid lines
36  Q_ASSERT(startLine >= 0);
37  Q_ASSERT(startLine < m_buffer->lines());
38 
39  m_startLine = startLine;
40 }
41 
43 {
44  // right input
45  Q_ASSERT(line >= startLine());
46 
47  // get text line, at will bail out on out-of-range
48  return m_lines.at(line - startLine());
49 }
50 
51 void TextBlock::appendLine(const QString &textOfLine)
52 {
53  m_lines.push_back(TextLine::create(textOfLine));
54 }
55 
57 {
58  m_lines.clear();
59 }
60 
62 {
63  // combine all lines
64  for (size_t i = 0; i < m_lines.size(); ++i) {
65  // not first line, insert \n
66  if (i > 0 || startLine() > 0) {
67  text.append(QLatin1Char('\n'));
68  }
69 
70  text.append(m_lines.at(i)->text());
71  }
72 }
73 
74 void TextBlock::wrapLine(const KTextEditor::Cursor &position, int fixStartLinesStartIndex)
75 {
76  // calc internal line
77  int line = position.line() - startLine();
78 
79  // get text
80  QString &text = m_lines.at(line)->textReadWrite();
81 
82  // check if valid column
83  Q_ASSERT(position.column() >= 0);
84  Q_ASSERT(position.column() <= text.size());
85 
86  // create new line and insert it
87  m_lines.insert(m_lines.begin() + line + 1, TextLine(new TextLineData()));
88 
89  // cases for modification:
90  // 1. line is wrapped in the middle
91  // 2. if empty line is wrapped, mark new line as modified
92  // 3. line-to-be-wrapped is already modified
93  if (position.column() > 0 || text.size() == 0 || m_lines.at(line)->markedAsModified()) {
94  m_lines.at(line + 1)->markAsModified(true);
95  } else if (m_lines.at(line)->markedAsSavedOnDisk()) {
96  m_lines.at(line + 1)->markAsSavedOnDisk(true);
97  }
98 
99  // perhaps remove some text from previous line and append it
100  if (position.column() < text.size()) {
101  // text from old line moved first to new one
102  m_lines.at(line + 1)->textReadWrite() = text.right(text.size() - position.column());
103 
104  // now remove wrapped text from old line
105  text.chop(text.size() - position.column());
106 
107  // mark line as modified
108  m_lines.at(line)->markAsModified(true);
109  }
110 
111  // fix all start lines
112  // we need to do this NOW, else the range update will FAIL!
113  // bug 313759
114  m_buffer->fixStartLines(fixStartLinesStartIndex);
115 
116  // notify the text history
117  m_buffer->history().wrapLine(position);
118 
119  // cursor and range handling below
120 
121  // no cursors will leave or join this block
122 
123  // no cursors in this block, no work to do..
124  if (m_cursors.empty()) {
125  return;
126  }
127 
128  // move all cursors on the line which has the text inserted
129  // remember all ranges modified, optimize for the standard case of a few ranges
130  QVarLengthArray<TextRange *, 32> changedRanges;
131  for (TextCursor *cursor : m_cursors) {
132  // skip cursors on lines in front of the wrapped one!
133  if (cursor->lineInBlock() < line) {
134  continue;
135  }
136 
137  // either this is simple, line behind the wrapped one
138  if (cursor->lineInBlock() > line) {
139  // patch line of cursor
140  cursor->m_line++;
141  }
142 
143  // this is the wrapped line
144  else {
145  // skip cursors with too small column
146  if (cursor->column() <= position.column()) {
147  if (cursor->column() < position.column() || !cursor->m_moveOnInsert) {
148  continue;
149  }
150  }
151 
152  // move cursor
153 
154  // patch line of cursor
155  cursor->m_line++;
156 
157  // patch column
158  cursor->m_column -= position.column();
159  }
160 
161  // remember range, if any, avoid double insert
162  auto range = cursor->kateRange();
163  if (range && !range->isValidityCheckRequired()) {
164  range->setValidityCheckRequired();
165  changedRanges.push_back(range);
166  }
167  }
168 
169  // we might need to invalidate ranges or notify about their changes
170  // checkValidity might trigger delete of the range!
171  for (TextRange *range : qAsConst(changedRanges)) {
172  // we need to do updateRange to ALWAYS ensure the line => range and back cache is updated
173  // see MovingRangeTest::testLineWrapOrUnwrapUpdateRangeForLineCache
174  updateRange(range);
175 
176  // in addition: ensure that we really invalidate bad ranges!
177  range->checkValidity(range->toLineRange());
178  }
179 }
180 
181 void TextBlock::unwrapLine(int line, TextBlock *previousBlock, int fixStartLinesStartIndex)
182 {
183  // calc internal line
184  line = line - startLine();
185 
186  // two possiblities: either first line of this block or later line
187  if (line == 0) {
188  // we need previous block with at least one line
189  Q_ASSERT(previousBlock);
190  Q_ASSERT(previousBlock->lines() > 0);
191 
192  // move last line of previous block to this one, might result in empty block
193  TextLine oldFirst = m_lines.at(0);
194  int lastLineOfPreviousBlock = previousBlock->lines() - 1;
195  TextLine newFirst = previousBlock->m_lines.back();
196  m_lines[0] = newFirst;
197  previousBlock->m_lines.erase(previousBlock->m_lines.begin() + (previousBlock->lines() - 1));
198 
199  const int oldSizeOfPreviousLine = newFirst->text().size();
200  if (oldFirst->length() > 0) {
201  // append text
202  newFirst->textReadWrite().append(oldFirst->text());
203 
204  // mark line as modified, since text was appended
205  newFirst->markAsModified(true);
206  }
207 
208  // patch startLine of this block
209  --m_startLine;
210 
211  // fix all start lines
212  // we need to do this NOW, else the range update will FAIL!
213  // bug 313759
214  m_buffer->fixStartLines(fixStartLinesStartIndex);
215 
216  // notify the text history in advance
217  m_buffer->history().unwrapLine(startLine() + line, oldSizeOfPreviousLine);
218 
219  // cursor and range handling below
220 
221  // no cursors in this block and the previous one, no work to do..
222  if (m_cursors.empty() && previousBlock->m_cursors.empty()) {
223  return;
224  }
225 
226  // move all cursors because of the unwrapped line
227  // remember all ranges modified, optimize for the standard case of a few ranges
228  QVarLengthArray<TextRange *, 32> changedRanges;
229  for (TextCursor *cursor : m_cursors) {
230  // this is the unwrapped line
231  if (cursor->lineInBlock() == 0) {
232  // patch column
233  cursor->m_column += oldSizeOfPreviousLine;
234 
235  // remember range, if any, avoid double insert
236  auto range = cursor->kateRange();
237  if (range && !range->isValidityCheckRequired()) {
238  range->setValidityCheckRequired();
239  changedRanges.push_back(range);
240  }
241  }
242  }
243 
244  // move cursors of the moved line from previous block to this block now
245  for (auto it = previousBlock->m_cursors.begin(); it != previousBlock->m_cursors.end();) {
246  auto cursor = *it;
247  if (cursor->lineInBlock() == lastLineOfPreviousBlock) {
248  cursor->m_line = 0;
249  cursor->m_block = this;
250  m_cursors.insert(cursor);
251 
252  // remember range, if any, avoid double insert
253  auto range = cursor->kateRange();
254  if (range && !range->isValidityCheckRequired()) {
255  range->setValidityCheckRequired();
256  changedRanges.push_back(range);
257  }
258 
259  // remove from previous block
260  it = previousBlock->m_cursors.erase(it);
261  } else {
262  // keep in previous block
263  ++it;
264  }
265  }
266 
267  // fixup the ranges that might be effected, because they moved from last line to this block
268  // we might need to invalidate ranges or notify about their changes
269  // checkValidity might trigger delete of the range!
270  for (TextRange *range : qAsConst(changedRanges)) {
271  // update both blocks
272  updateRange(range);
273  previousBlock->updateRange(range);
274 
275  // afterwards check validity, might delete this range!
276  range->checkValidity(range->toLineRange());
277  }
278 
279  // be done
280  return;
281  }
282 
283  // easy: just move text to previous line and remove current one
284  const int oldSizeOfPreviousLine = m_lines.at(line - 1)->length();
285  const int sizeOfCurrentLine = m_lines.at(line)->length();
286  if (sizeOfCurrentLine > 0) {
287  m_lines.at(line - 1)->textReadWrite().append(m_lines.at(line)->text());
288  }
289 
290  const bool lineChanged = (oldSizeOfPreviousLine > 0 && m_lines.at(line - 1)->markedAsModified())
291  || (sizeOfCurrentLine > 0 && (oldSizeOfPreviousLine > 0 || m_lines.at(line)->markedAsModified()));
292  m_lines.at(line - 1)->markAsModified(lineChanged);
293  if (oldSizeOfPreviousLine == 0 && m_lines.at(line)->markedAsSavedOnDisk()) {
294  m_lines.at(line - 1)->markAsSavedOnDisk(true);
295  }
296 
297  m_lines.erase(m_lines.begin() + line);
298 
299  // fix all start lines
300  // we need to do this NOW, else the range update will FAIL!
301  // bug 313759
302  m_buffer->fixStartLines(fixStartLinesStartIndex);
303 
304  // notify the text history in advance
305  m_buffer->history().unwrapLine(startLine() + line, oldSizeOfPreviousLine);
306 
307  // cursor and range handling below
308 
309  // no cursors in this block, no work to do..
310  if (m_cursors.empty()) {
311  return;
312  }
313 
314  // move all cursors because of the unwrapped line
315  // remember all ranges modified, optimize for the standard case of a few ranges
316  QVarLengthArray<TextRange *, 32> changedRanges;
317  for (TextCursor *cursor : m_cursors) {
318  // skip cursors in lines in front of removed one
319  if (cursor->lineInBlock() < line) {
320  continue;
321  }
322 
323  // this is the unwrapped line
324  if (cursor->lineInBlock() == line) {
325  // patch column
326  cursor->m_column += oldSizeOfPreviousLine;
327  }
328 
329  // patch line of cursor
330  cursor->m_line--;
331 
332  // remember range, if any, avoid double insert
333  auto range = cursor->kateRange();
334  if (range && !range->isValidityCheckRequired()) {
335  range->setValidityCheckRequired();
336  changedRanges.push_back(range);
337  }
338  }
339 
340  // we might need to invalidate ranges or notify about their changes
341  // checkValidity might trigger delete of the range!
342  for (TextRange *range : qAsConst(changedRanges)) {
343  // we need to do updateRange to ALWAYS ensure the line => range and back cache is updated
344  // see MovingRangeTest::testLineWrapOrUnwrapUpdateRangeForLineCache
345  updateRange(range);
346 
347  // in addition: ensure that we really invalidate bad ranges!
348  range->checkValidity(range->toLineRange());
349  }
350 }
351 
353 {
354  // calc internal line
355  int line = position.line() - startLine();
356 
357  // get text
358  QString &textOfLine = m_lines.at(line)->textReadWrite();
359  int oldLength = textOfLine.size();
360  m_lines.at(line)->markAsModified(true);
361 
362  // check if valid column
363  Q_ASSERT(position.column() >= 0);
364  Q_ASSERT(position.column() <= textOfLine.size());
365 
366  // insert text
367  textOfLine.insert(position.column(), text);
368 
369  // notify the text history
370  m_buffer->history().insertText(position, text.size(), oldLength);
371 
372  // cursor and range handling below
373 
374  // no cursors in this block, no work to do..
375  if (m_cursors.empty()) {
376  return;
377  }
378 
379  // move all cursors on the line which has the text inserted
380  // remember all ranges modified, optimize for the standard case of a few ranges
381  QVarLengthArray<TextRange *, 32> changedRanges;
382  for (TextCursor *cursor : m_cursors) {
383  // skip cursors not on this line!
384  if (cursor->lineInBlock() != line) {
385  continue;
386  }
387 
388  // skip cursors with too small column
389  if (cursor->column() <= position.column()) {
390  if (cursor->column() < position.column() || !cursor->m_moveOnInsert) {
391  continue;
392  }
393  }
394 
395  // patch column of cursor
396  if (cursor->m_column <= oldLength) {
397  cursor->m_column += text.size();
398  }
399 
400  // special handling if cursor behind the real line, e.g. non-wrapping cursor in block selection mode
401  else if (cursor->m_column < textOfLine.size()) {
402  cursor->m_column = textOfLine.size();
403  }
404 
405  // remember range, if any, avoid double insert
406  // we only need to trigger checkValidity later if the range has feedback or might be invalidated
407  auto range = cursor->kateRange();
408  if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) {
409  range->setValidityCheckRequired();
410  changedRanges.push_back(range);
411  }
412  }
413 
414  // we might need to invalidate ranges or notify about their changes
415  // checkValidity might trigger delete of the range!
416  for (TextRange *range : qAsConst(changedRanges)) {
417  range->checkValidity(range->toLineRange());
418  }
419 }
420 
421 void TextBlock::removeText(const KTextEditor::Range &range, QString &removedText)
422 {
423  // calc internal line
424  int line = range.start().line() - startLine();
425 
426  // get text
427  QString &textOfLine = m_lines.at(line)->textReadWrite();
428  int oldLength = textOfLine.size();
429 
430  // check if valid column
431  Q_ASSERT(range.start().column() >= 0);
432  Q_ASSERT(range.start().column() <= textOfLine.size());
433  Q_ASSERT(range.end().column() >= 0);
434  Q_ASSERT(range.end().column() <= textOfLine.size());
435 
436  // get text which will be removed
437  removedText = textOfLine.mid(range.start().column(), range.end().column() - range.start().column());
438 
439  // remove text
440  textOfLine.remove(range.start().column(), range.end().column() - range.start().column());
441  m_lines.at(line)->markAsModified(true);
442 
443  // notify the text history
444  m_buffer->history().removeText(range, oldLength);
445 
446  // cursor and range handling below
447 
448  // no cursors in this block, no work to do..
449  if (m_cursors.empty()) {
450  return;
451  }
452 
453  // move all cursors on the line which has the text removed
454  // remember all ranges modified, optimize for the standard case of a few ranges
455  QVarLengthArray<TextRange *, 32> changedRanges;
456  for (TextCursor *cursor : m_cursors) {
457  // skip cursors not on this line!
458  if (cursor->lineInBlock() != line) {
459  continue;
460  }
461 
462  // skip cursors with too small column
463  if (cursor->column() <= range.start().column()) {
464  continue;
465  }
466 
467  // patch column of cursor
468  if (cursor->column() <= range.end().column()) {
469  cursor->m_column = range.start().column();
470  } else {
471  cursor->m_column -= (range.end().column() - range.start().column());
472  }
473 
474  // remember range, if any, avoid double insert
475  // we only need to trigger checkValidity later if the range has feedback or might be invalidated
476  auto range = cursor->kateRange();
477  if (range && !range->isValidityCheckRequired() && (range->feedback() || range->start().line() == range->end().line())) {
478  range->setValidityCheckRequired();
479  changedRanges.push_back(range);
480  }
481  }
482 
483  // we might need to invalidate ranges or notify about their changes
484  // checkValidity might trigger delete of the range!
485  for (TextRange *range : qAsConst(changedRanges)) {
486  range->checkValidity(range->toLineRange());
487  }
488 }
489 
490 void TextBlock::debugPrint(int blockIndex) const
491 {
492  // print all blocks
493  for (size_t i = 0; i < m_lines.size(); ++i)
494  printf("%4d - %4lld : %4d : '%s'\n", blockIndex, (unsigned long long)startLine() + i, m_lines.at(i)->text().size(), qPrintable(m_lines.at(i)->text()));
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 }
const TextCursor & startInternal() const
Non-virtual version of start(), which is faster.
QString & append(QChar ch)
constexpr LineRange toLineRange() const Q_DECL_NOEXCEPT
Convert this Range to a LineRange.
QSet< TextRange * > cachedRangesForLine(int line) const
Return all ranges in this block which might intersect the given line and only span one line...
void wrapLine(const KTextEditor::Cursor &position, int fixStartLinesStartIndex)
Wrap line at given cursor position.
QSharedPointer< T > create(Args &&...args)
void setStartLine(int startLine)
Set start line of this block.
void removeRange(TextRange *range)
Remove 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.
Definition: katetextblock.h:90
int size() const const
void push_back(const T &t)
QVector< TextRange * > rangesForLine(int line, KTextEditor::View *view, bool rangesWithAttributeOnly) const
Return all ranges in this block which might intersect the given line.
QString & remove(int position, int n)
int lineInternal() const
Non-virtual version of line(), which is faster.
The Cursor represents a position in a Document.
Definition: cursor.h:71
void chop(int n)
int startLine() const
Start line of this block.
Definition: katetextblock.h:57
Class representing a single text line.
Definition: katetextline.h:25
void deleteBlockContent()
Delete the block content, delete all lines and delete all cursors not bound to ranges.
const TextCursor & endInternal() const
Nonvirtual version of end(), which is faster.
void debugPrint(int blockIndex) const
Debug output, print whole block content with line numbers and line length.
TextBlock(TextBuffer *buffer, int startLine)
Construct an empty text block.
~TextBlock()
Destruct the text block.
void updateRange(TextRange *range)
Update a range from this block.
void clearBlockContent(TextBlock *targetBlock)
Clear the block content, delete all lines, move all cursors not bound to range to given block at 0...
QString & insert(int position, QChar ch)
void markModifiedLinesAsSaved()
Flag all modified text lines as saved on disk.
constexpr int column() const Q_DECL_NOEXCEPT
Retrieve the column on which this cursor is situated.
Definition: cursor.h:213
constexpr Cursor start() const Q_DECL_NOEXCEPT
Get the start position of this range.
void removeText(const KTextEditor::Range &range, QString &removedText)
Remove text at given range.
QString right(int n) const const
An object representing a section of text, from one Cursor to another.
void appendLine(const QString &textOfLine)
Append a new line with given text.
void reserve(int size)
void clearLines()
Clear the lines.
QString mid(int position, int n) const const
void mergeBlock(TextBlock *targetBlock)
Merge this block with given one, the given one must be a direct predecessor.
Class representing a &#39;clever&#39; text cursor.
constexpr Cursor end() const Q_DECL_NOEXCEPT
Get the end position of this range.
constexpr int line() const Q_DECL_NOEXCEPT
Retrieve the line on which this cursor is situated.
Definition: cursor.h:195
TextLine line(int line) const
Retrieve a text line.
Class representing a text buffer.
A text widget with KXMLGUIClient that represents a Document.
Definition: view.h:146
TextHistory & history()
TextHistory of this buffer.
void text(QString &text) const
Retrieve text of block.
Class representing a &#39;clever&#39; text range.
Definition: katetextrange.h:36
void insertText(const KTextEditor::Cursor &position, const QString &text)
Insert text at given cursor position.
Class representing a text block.
Definition: katetextblock.h:38
TextBlock * splitBlock(int fromLine)
Split given block.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Jun 20 2021 22:57:21 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.