KTextEditor

katetextrange.cpp
1 /*
2  SPDX-FileCopyrightText: 2010 Christoph Cullmann <[email protected]>
3 
4  Based on code of the SmartCursor/Range by:
5  SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "katetextrange.h"
11 #include "katedocument.h"
12 #include "katetextbuffer.h"
13 
14 namespace Kate
15 {
16 TextRange::TextRange(TextBuffer &buffer, KTextEditor::Range range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior)
17  : m_buffer(buffer)
18  , m_start(buffer, this, range.start(), (insertBehavior & ExpandLeft) ? Kate::TextCursor::StayOnInsert : Kate::TextCursor::MoveOnInsert)
19  , m_end(buffer, this, range.end(), (insertBehavior & ExpandRight) ? Kate::TextCursor::MoveOnInsert : Kate::TextCursor::StayOnInsert)
20  , m_view(nullptr)
21  , m_feedback(nullptr)
22  , m_zDepth(0.0)
23  , m_attributeOnlyForViews(false)
24  , m_invalidateIfEmpty(emptyBehavior == InvalidateIfEmpty)
25 {
26  // remember this range in buffer
27  m_buffer.m_ranges.insert(this);
28 
29  // check if range now invalid, there can happen no feedback, as m_feedback == 0
30  // only place where KTextEditor::LineRange::invalid() for old range makes sense, as we were yet not registered!
31  checkValidity(KTextEditor::LineRange::invalid());
32 }
33 
35 {
36  // reset feedback, don't want feedback during destruction
37  m_feedback = nullptr;
38 
39  // remove range from m_ranges
41 
42  // remove this range from the buffer
43  m_buffer.m_ranges.remove(this);
44 
45  // trigger update, if we have attribute
46  // notify right view
47  // here we can ignore feedback, even with feedback, we want none if the range is deleted!
48  if (m_attribute) {
49  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), true /* we have a attribute */);
50  }
51 }
52 
54 {
55  // nothing to do?
56  if (_insertBehaviors == insertBehaviors()) {
57  return;
58  }
59 
60  // modify cursors
63 
64  // notify world
65  if (m_attribute || m_feedback) {
66  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), true /* we have a attribute */);
67  }
68 }
69 
71 {
72  InsertBehaviors behaviors = DoNotExpand;
73 
75  behaviors |= ExpandLeft;
76  }
77 
79  behaviors |= ExpandRight;
80  }
81 
82  return behaviors;
83 }
84 
86 {
87  // nothing to do?
88  if (m_invalidateIfEmpty == (emptyBehavior == InvalidateIfEmpty)) {
89  return;
90  }
91 
92  // remember value
93  m_invalidateIfEmpty = (emptyBehavior == InvalidateIfEmpty);
94 
95  // invalidate range?
96  if (endInternal() <= startInternal()) {
98  }
99 }
100 
102 {
103  // avoid work if nothing changed!
104  if (range == toRange()) {
105  return;
106  }
107 
108  // remember old line range
109  const auto oldLineRange = toLineRange();
110 
111  // change start and end cursor
112  m_start.setPosition(range.start());
113  m_end.setPosition(range.end());
114 
115  // check if range now invalid, don't emit feedback here, will be handled below
116  // otherwise you can't delete ranges in feedback!
117  checkValidity(oldLineRange, false);
118 
119  // no attribute or feedback set, be done
120  if (!m_attribute && !m_feedback) {
121  return;
122  }
123 
124  // get full range
125  int startLineMin = oldLineRange.start();
126  if (oldLineRange.start() == -1 || (m_start.lineInternal() != -1 && m_start.lineInternal() < oldLineRange.start())) {
127  startLineMin = m_start.line();
128  }
129 
130  int endLineMax = oldLineRange.end();
131  if (oldLineRange.end() == -1 || m_end.lineInternal() > oldLineRange.end()) {
132  endLineMax = m_end.lineInternal();
133  }
134 
135  // notify buffer about attribute change, it will propagate the changes
136  // notify right view
137  m_buffer.notifyAboutRangeChange(m_view, {startLineMin, endLineMax}, m_attribute);
138 
139  // perhaps need to notify stuff!
140  if (m_feedback) {
141  // do this last: may delete this range
142  if (!toRange().isValid()) {
143  m_feedback->rangeInvalid(this);
144  } else if (toRange().isEmpty()) {
145  m_feedback->rangeEmpty(this);
146  }
147  }
148 }
149 
150 void TextRange::checkValidity(KTextEditor::LineRange oldLineRange, bool notifyAboutChange)
151 {
152  // in any case: reset the flag, to avoid multiple runs
153  m_isCheckValidityRequired = false;
154 
155  // check if any cursor is invalid or the range is zero size and it should be invalidated then
156  if (!m_start.isValid() || !m_end.isValid() || (m_invalidateIfEmpty && m_end <= m_start)) {
157  m_start.setPosition(-1, -1);
158  m_end.setPosition(-1, -1);
159  }
160 
161  // for ranges which are allowed to become empty, normalize them, if the end has moved to the front of the start
162  if (!m_invalidateIfEmpty && m_end < m_start) {
163  m_end.setPosition(m_start);
164  }
165 
166  // fix lookup
167  fixLookup(oldLineRange, toLineRange());
168 
169  // perhaps need to notify stuff!
170  if (notifyAboutChange && m_feedback) {
171  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), false /* attribute not interesting here */);
172 
173  // do this last: may delete this range
174  if (!toRange().isValid()) {
175  m_feedback->rangeInvalid(this);
176  } else if (toRange().isEmpty()) {
177  m_feedback->rangeEmpty(this);
178  }
179  }
180 }
181 
182 void TextRange::fixLookup(KTextEditor::LineRange oldLineRange, KTextEditor::LineRange lineRange)
183 {
184  // nothing changed?
185  if (oldLineRange == lineRange) {
186  return;
187  }
188 
189  // now, not both can be invalid
190  Q_ASSERT(oldLineRange.start() >= 0 || lineRange.start() >= 0);
191  Q_ASSERT(oldLineRange.end() >= 0 || lineRange.end() >= 0);
192 
193  // get full range
194  int startLineMin = oldLineRange.start();
195  if (oldLineRange.start() == -1 || (lineRange.start() != -1 && lineRange.start() < oldLineRange.start())) {
196  startLineMin = lineRange.start();
197  }
198 
199  int endLineMax = oldLineRange.end();
200  if (oldLineRange.end() == -1 || lineRange.end() > oldLineRange.end()) {
201  endLineMax = lineRange.end();
202  }
203 
204  // get start block
205  int blockIdx = m_buffer.blockForLine(startLineMin);
206  Q_ASSERT(blockIdx >= 0);
207 
208  // remove this range from m_ranges
209  auto it = m_buffer.m_blocks.begin() + blockIdx;
210  auto end = m_buffer.m_blocks.end();
211  for (; it != end; ++it) {
212  // either insert or remove range
213  TextBlock *block = *it;
214  if ((lineRange.end() < block->startLine()) || (lineRange.start() >= (block->startLine() + block->lines()))) {
215  block->removeRange(this);
216  } else {
217  block->updateRange(this);
218  }
219 
220  // ok, reached end block
221  if (endLineMax < (block->startLine() + block->lines())) {
222  return;
223  }
224  }
225 
226  // we should not be here, really, then endLine is wrong
227  Q_ASSERT(false);
228 }
229 
231 {
232  // nothing changes, nop
233  if (view == m_view) {
234  return;
235  }
236 
237  // remember the new attribute
238  m_view = view;
239 
240  // notify buffer about attribute change, it will propagate the changes
241  // notify all views (can be optimized later)
242  if (m_attribute || m_feedback) {
243  m_buffer.notifyAboutRangeChange(nullptr, toLineRange(), m_attribute);
244  }
245 }
246 
248 {
249  // nothing changes, nop, only pointer compare
250  if (attribute == m_attribute) {
251  return;
252  }
253 
254  // remember the new attribute
255  m_attribute = attribute;
256 
257  // notify buffer about attribute change, it will propagate the changes
258  // notify right view
259  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), true /* even for nullptr attribute, we had before one => repaint */);
260 }
261 
263 {
264  // nothing changes, nop
265  if (feedback == m_feedback) {
266  return;
267  }
268 
269  // remember the new feedback object
270  m_feedback = feedback;
271 
272  // notify buffer about feedback change, it will propagate the changes
273  // notify right view
274  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), m_attribute);
275 }
276 
277 void TextRange::setAttributeOnlyForViews(bool onlyForViews)
278 {
279  // just set the value, no need to trigger updates, printing is not interruptable
280  m_attributeOnlyForViews = onlyForViews;
281 }
282 
283 void TextRange::setZDepth(qreal zDepth)
284 {
285  // nothing changes, nop
286  if (zDepth == m_zDepth) {
287  return;
288  }
289 
290  // remember the new attribute
291  m_zDepth = zDepth;
292 
293  // notify buffer about attribute change, it will propagate the changes
294  if (m_attribute) {
295  m_buffer.notifyAboutRangeChange(m_view, toLineRange(), m_attribute);
296  }
297 }
298 
300 {
301  return m_buffer.document();
302 }
303 
304 }
InsertBehaviors insertBehaviors() const override
Get current insert behaviors.
@ StayOnInsert
stay on insert
Definition: movingcursor.h:65
virtual void rangeInvalid(MovingRange *range)
The range is now invalid (ie.
constexpr static LineRange invalid() Q_DECL_NOEXCEPT
Returns an invalid line range.
Definition: linerange.h:70
Class representing a 'clever' text cursor.
const KTextEditor::MovingCursor & end() const override
Retrieve end cursor of this range, read-only.
void setZDepth(qreal zDepth) override
Set the current Z-depth of this range.
@ ExpandLeft
Expand to encapsulate new characters to the left of the range.
Definition: movingrange.h:156
~TextRange() override
Destruct the text block.
Class representing a text buffer.
const TextCursor & endInternal() const
Nonvirtual version of end(), which is faster.
Q_SCRIPTABLE Q_NOREPLY void start()
void setAttribute(KTextEditor::Attribute::Ptr attribute) override
Sets the currently active attribute for this range.
void setView(KTextEditor::View *view) override
Sets the currently active view for this range.
An object representing lines from a start line to an end line.
Definition: linerange.h:37
EmptyBehavior emptyBehavior() const override
Will this range invalidate itself if it becomes empty?
Definition: katetextrange.h:80
An object representing a section of text, from one Cursor to another.
void setRange(const KTextEditor::Range &range) override
Set the range of this range.
int lineInternal() const
Non-virtual version of line(), which is faster.
KTextEditor::LineRange toLineRange() const
Hides parent's impl of toLineRange() and uses non-virtual functions internally.
KTextEditor::Attribute::Ptr attribute() const override
Gets the active Attribute for this range.
@ InvalidateIfEmpty
invalidate range, if it becomes empty
Definition: movingrange.h:168
constexpr Cursor end() const Q_DECL_NOEXCEPT
Get the end position of this range.
@ ExpandRight
Expand to encapsulate new characters to the right of the range.
Definition: movingrange.h:158
void setFeedback(KTextEditor::MovingRangeFeedback *feedback) override
Sets the currently active MovingRangeFeedback for this range.
int line() const override
Retrieve the line on which this cursor is situated.
void setInsertBehavior(InsertBehavior insertBehavior) override
Set insert behavior.
bool isValid() const
Returns whether the current position of this cursor is a valid position, i.e.
Definition: movingcursor.h:159
void setPosition(const TextCursor &position)
Fast way to set the current cursor position to position.
const KTextEditor::Range toRange() const
Convert this clever range into a dumb one.
void setEmptyBehavior(EmptyBehavior emptyBehavior) override
Set if this range will invalidate itself if it becomes empty.
constexpr int start() const Q_DECL_NOEXCEPT
Get the start line of this line range.
Definition: linerange.h:105
@ DoNotExpand
Don't expand to encapsulate new characters in either direction. This is the default.
Definition: movingrange.h:154
A text widget with KXMLGUIClient that represents a Document.
Definition: view.h:146
virtual void rangeEmpty(MovingRange *range)
The range is now empty (ie.
bool isEmpty() const
Returns true if this range contains no characters, ie.
Definition: movingrange.h:413
void setInsertBehaviors(InsertBehaviors insertBehaviors) override
Set insert behaviors.
EmptyBehavior
Behavior of range if it becomes empty.
Definition: movingrange.h:166
A class which provides notifications of state changes to a MovingRange.
KTextEditor::Document * document() const override
Gets the document to which this range is bound.
constexpr Cursor start() const Q_DECL_NOEXCEPT
Get the start position of this range.
bool isValid(QStringView ifopt)
@ MoveOnInsert
move on insert
Definition: movingcursor.h:66
KTextEditor::MovingRangeFeedback * feedback() const override
Gets the active MovingRangeFeedback for this range.
const TextCursor & startInternal() const
Non-virtual version of start(), which is faster.
constexpr int end() const Q_DECL_NOEXCEPT
Get the end line of this line range.
Definition: linerange.h:115
KTextEditor::View * view() const override
Gets the active view for this range.
void setAttributeOnlyForViews(bool onlyForViews) override
Set if this range's attribute is only visible in views, not for example prints.
constexpr static Range invalid() Q_DECL_NOEXCEPT
Returns an invalid range.
qreal zDepth() const override
Gets the current Z-depth of this range.
TextRange(TextBuffer &buffer, KTextEditor::Range range, InsertBehaviors insertBehavior, EmptyBehavior emptyBehavior=AllowEmpty)
Construct a text range.
InsertBehavior insertBehavior() const override
Get current insert behavior.
A KParts derived class representing a text document.
Definition: document.h:185
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 03:48:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.