1 /*
2 SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <rodda@kde.org>
3 SPDX-FileCopyrightText: 2001-2005 Christoph Cullmann <cullmann@kde.org>
4 SPDX-FileCopyrightText: 2002 Christian Couder <christian@kdevelop.org>
5 SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
6 SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10
11 #ifndef KTEXTEDITOR_RANGE_H
12 #define KTEXTEDITOR_RANGE_H
13
14 #include <ktexteditor/cursor.h>
15 #include <ktexteditor/linerange.h>
16 #include <ktexteditor_export.h>
17
18 #include <QDebug>
19 #include <QtGlobal>
20
21 namespace KTextEditor
22 {
23 /**
24 * \class Range range.h <KTextEditor/Range>
25 *
26 * \short An object representing a section of text, from one Cursor to another.
27 *
28 * A Range is a basic class which represents a range of text with two Cursors,
29 * from a start() position to an end() position.
30 *
31 * For simplicity and convenience, ranges always maintain their start position to
32 * be before or equal to their end position. Attempting to set either the
33 * start or end of the range beyond the respective end or start will result in
34 * both values being set to the specified position. In the constructor, the
35 * start and end will be swapped if necessary.
36 *
37 * If you want additional functionality such as the ability to maintain position
38 * in a document, see MovingRange.
39 *
40 * \sa MovingRange
41 *
42 * \author Hamish Rodda \<rodda@kde.org\>
43 */
44 class KTEXTEDITOR_EXPORT Range
45 {
46 public:
47 /**
48 * Default constructor. Creates a valid range from position (0, 0) to
49 * position (0, 0).
50 */
Range()51 Q_DECL_CONSTEXPR Range() Q_DECL_NOEXCEPT
52 {
53 }
54
55 /**
56 * Constructor which creates a range from \e start to \e end.
57 * If start is after end, they will be swapped.
58 *
59 * \param start start position
60 * \param end end position
61 */
Range(const Cursor & start,const Cursor & end)62 Q_DECL_CONSTEXPR Range(const Cursor &start, const Cursor &end) Q_DECL_NOEXCEPT : m_start(qMin(start, end)), m_end(qMax(start, end))
63 {
64 }
65
66 /**
67 * Constructor which creates a single-line range from \p start,
68 * extending \p width characters along the same line.
69 *
70 * \param start start position
71 * \param width width of this range in columns along the same line
72 */
Range(const Cursor & start,int width)73 Q_DECL_CONSTEXPR Range(const Cursor &start, int width) Q_DECL_NOEXCEPT : m_start(qMin(start, Cursor(start.line(), start.column() + width))),
74 m_end(qMax(start, Cursor(start.line(), start.column() + width)))
75 {
76 }
77
78 /**
79 * Constructor which creates a range from \p start, to \p endLine, \p endColumn.
80 *
81 * \param start start position
82 * \param endLine end line
83 * \param endColumn end column
84 */
Range(const Cursor & start,int endLine,int endColumn)85 Q_DECL_CONSTEXPR Range(const Cursor &start, int endLine, int endColumn) Q_DECL_NOEXCEPT : m_start(qMin(start, Cursor(endLine, endColumn))),
86 m_end(qMax(start, Cursor(endLine, endColumn)))
87 {
88 }
89
90 /**
91 * Constructor which creates a range from \e startLine, \e startColumn to \e endLine, \e endColumn.
92 *
93 * \param startLine start line
94 * \param startColumn start column
95 * \param endLine end line
96 * \param endColumn end column
97 */
Range(int startLine,int startColumn,int endLine,int endColumn)98 Q_DECL_CONSTEXPR Range(int startLine, int startColumn, int endLine, int endColumn) Q_DECL_NOEXCEPT
99 : m_start(qMin(Cursor(startLine, startColumn), Cursor(endLine, endColumn))),
100 m_end(qMax(Cursor(startLine, startColumn), Cursor(endLine, endColumn)))
101 {
102 }
103
104 /**
105 * Validity check. In the base class, returns true unless the range starts before (0,0).
106 */
isValid()107 Q_DECL_CONSTEXPR inline bool isValid() const Q_DECL_NOEXCEPT
108 {
109 return start().isValid() && end().isValid();
110 }
111
112 /**
113 * Returns an invalid range.
114 */
invalid()115 Q_DECL_CONSTEXPR static Range invalid() Q_DECL_NOEXCEPT
116 {
117 return Range(Cursor::invalid(), Cursor::invalid());
118 }
119
120 /**
121 * Returns the cursor position as string in the format
122 * "start-line:start-column,endl-line:end-column".
123 * \see fromString()
124 */
toString()125 QString toString() const
126 {
127 return QLatin1Char('[') + m_start.toString() + QLatin1String(", ") + m_end.toString() + QLatin1Char(']');
128 }
129
130 /**
131 * Returns a Range created from the string \p str containing the format
132 * "[(start-line, start-column), (endl-line:end-column)]".
133 * In case the string cannot be parsed, an Range::invalid() is returned.
134 * \see toString()
135 */
136 // TODO KF6: Remove this overload in favor of fromString(QStringView).
fromString(const QString & str)137 static Range fromString(const QString &str) Q_DECL_NOEXCEPT
138 {
139 return fromString(str.leftRef(-1));
140 }
141
142 /**
143 * Returns a Range created from the string \p str containing the format
144 * "[(start-line, start-column), (endl-line:end-column)]".
145 * In case the string cannot be parsed, an Range::invalid() is returned.
146 * \see toString()
147 */
148 // TODO KF6: Remove this overload in favor of fromString(QStringView).
149 static Range fromString(const QStringRef &str) Q_DECL_NOEXCEPT;
150
151 /**
152 * Returns a Range created from the string \p str containing the format
153 * "[(start-line, start-column), (endl-line:end-column)]".
154 * In case the string cannot be parsed, an Range::invalid() is returned.
155 * \see toString()
156 */
157 static Range fromString(QStringView str) Q_DECL_NOEXCEPT;
158
159 /**
160 * \name Position
161 *
162 * The following functions provide access to, and manipulation of, the range's position.
163 * \{
164 */
165
166 /**
167 * Get the start position of this range. This will always be <= end().
168 *
169 * \returns const reference to the start position of this range.
170 */
start()171 Q_DECL_CONSTEXPR inline Cursor start() const Q_DECL_NOEXCEPT
172 {
173 return m_start;
174 }
175
176 /**
177 * Get the end position of this range. This will always be >= start().
178 *
179 * \returns const reference to the end position of this range.
180 */
end()181 Q_DECL_CONSTEXPR inline Cursor end() const Q_DECL_NOEXCEPT
182 {
183 return m_end;
184 }
185
186 /**
187 * Convert this Range to a LineRange
188 *
189 * @return LineRange from the start line to the end line of this range.
190 */
toLineRange()191 Q_DECL_CONSTEXPR inline LineRange toLineRange() const Q_DECL_NOEXCEPT
192 {
193 return {start().line(), end().line()};
194 }
195
196 /**
197 * Convenience function. Set the start and end lines to \p line.
198 *
199 * \param line the line number to assign to start() and end()
200 */
201 void setBothLines(int line) Q_DECL_NOEXCEPT;
202
203 /**
204 * Convenience function. Set the start and end columns to \p column.
205 *
206 * \param column the column number to assign to start() and end()
207 */
208 void setBothColumns(int column) Q_DECL_NOEXCEPT;
209
210 /**
211 * Set the start and end cursors to \e range.start() and \e range.end() respectively.
212 *
213 * \param range range to assign to this range
214 */
215 void setRange(const Range &range) Q_DECL_NOEXCEPT;
216
217 /**
218 * \overload
219 * \n \n
220 * Set the start and end cursors to \e start and \e end respectively.
221 *
222 * \note If \e start is after \e end, they will be reversed.
223 *
224 * \param start start cursor
225 * \param end end cursor
226 */
227 void setRange(const Cursor &start, const Cursor &end) Q_DECL_NOEXCEPT;
228
229 /**
230 * Set the start cursor to \e start.
231 *
232 * \note If \e start is after current end, start and end will be set to new start value.
233 *
234 * \param start new start cursor
235 */
setStart(const Cursor & start)236 inline void setStart(const Cursor &start) Q_DECL_NOEXCEPT
237 {
238 if (start > end()) {
239 setRange(start, start);
240 } else {
241 setRange(start, end());
242 }
243 }
244
245 /**
246 * Set the end cursor to \e end.
247 *
248 * \note If \e end is in front of current start, start and end will be set to new end value.
249 *
250 * \param end new end cursor
251 */
setEnd(const Cursor & end)252 inline void setEnd(const Cursor &end) Q_DECL_NOEXCEPT
253 {
254 if (end < start()) {
255 setRange(end, end);
256 } else {
257 setRange(start(), end);
258 }
259 }
260
261 /**
262 * Expand this range if necessary to contain \p range.
263 *
264 * \param range range which this range should contain
265 *
266 * \return \e true if expansion occurred, \e false otherwise
267 */
268 bool expandToRange(const Range &range) Q_DECL_NOEXCEPT;
269
270 /**
271 * Confine this range if necessary to fit within \p range.
272 *
273 * \param range range which should contain this range
274 *
275 * \return \e true if confinement occurred, \e false otherwise
276 */
277 bool confineToRange(const Range &range) Q_DECL_NOEXCEPT;
278
279 /**
280 * Check whether this range is wholly contained within one line, ie. if
281 * the start() and end() positions are on the same line.
282 *
283 * \return \e true if both the start and end positions are on the same
284 * line, otherwise \e false
285 */
onSingleLine()286 Q_DECL_CONSTEXPR inline bool onSingleLine() const Q_DECL_NOEXCEPT
287 {
288 return start().line() == end().line();
289 }
290
291 /**
292 * Returns the number of lines separating the start() and end() positions.
293 *
294 * \return the number of lines separating the start() and end() positions;
295 * 0 if the start and end lines are the same.
296 */
numberOfLines()297 Q_DECL_CONSTEXPR inline int numberOfLines() const Q_DECL_NOEXCEPT
298 {
299 return end().line() - start().line();
300 }
301
302 /**
303 * Returns the number of columns separating the start() and end() positions.
304 *
305 * \return the number of columns separating the start() and end() positions;
306 * 0 if the start and end columns are the same.
307 */
columnWidth()308 Q_DECL_CONSTEXPR inline int columnWidth() const Q_DECL_NOEXCEPT
309 {
310 return end().column() - start().column();
311 }
312
313 /**
314 * Returns true if this range contains no characters, ie. the start() and
315 * end() positions are the same.
316 *
317 * \returns \e true if the range contains no characters, otherwise \e false
318 */
isEmpty()319 Q_DECL_CONSTEXPR inline bool isEmpty() const Q_DECL_NOEXCEPT
320 {
321 return start() == end();
322 }
323
324 // BEGIN comparison functions
325 /**
326 * \}
327 *
328 * \name Comparison
329 *
330 * The following functions perform checks against this range in comparison
331 * to other lines, columns, cursors, and ranges.
332 * \{
333 */
334 /**
335 * Check whether the this range wholly encompasses \e range.
336 *
337 * \param range range to check
338 *
339 * \return \e true, if this range contains \e range, otherwise \e false
340 */
contains(const Range & range)341 Q_DECL_CONSTEXPR inline bool contains(const Range &range) const Q_DECL_NOEXCEPT
342 {
343 return range.start() >= start() && range.end() <= end();
344 }
345
346 /**
347 * Check to see if \p cursor is contained within this range, ie >= start() and \< end().
348 *
349 * \param cursor the position to test for containment
350 *
351 * \return \e true if the cursor is contained within this range, otherwise \e false.
352 */
contains(const Cursor & cursor)353 Q_DECL_CONSTEXPR inline bool contains(const Cursor &cursor) const Q_DECL_NOEXCEPT
354 {
355 return cursor >= start() && cursor < end();
356 }
357
358 /**
359 * Returns true if this range wholly encompasses \p line.
360 *
361 * \param line line to check
362 *
363 * \return \e true if the line is wholly encompassed by this range, otherwise \e false.
364 */
containsLine(int line)365 Q_DECL_CONSTEXPR inline bool containsLine(int line) const Q_DECL_NOEXCEPT
366 {
367 return (line > start().line() || (line == start().line() && !start().column())) && line < end().line();
368 }
369
370 /**
371 * Check whether the range contains \e column.
372 *
373 * \param column column to check
374 *
375 * \return \e true if the range contains \e column, otherwise \e false
376 */
containsColumn(int column)377 Q_DECL_CONSTEXPR inline bool containsColumn(int column) const Q_DECL_NOEXCEPT
378 {
379 return column >= start().column() && column < end().column();
380 }
381
382 /**
383 * Check whether the this range overlaps with \e range.
384 *
385 * \param range range to check against
386 *
387 * \return \e true, if this range overlaps with \e range, otherwise \e false
388 */
overlaps(const Range & range)389 Q_DECL_CONSTEXPR inline bool overlaps(const Range &range) const Q_DECL_NOEXCEPT
390 {
391 return (range.start() <= start()) ? (range.end() > start()) : (range.end() >= end()) ? (range.start() < end()) : contains(range);
392 }
393
394 /**
395 * Check whether the range overlaps at least part of \e line.
396 *
397 * \param line line to check
398 *
399 * \return \e true, if the range overlaps at least part of \e line, otherwise \e false
400 */
overlapsLine(int line)401 Q_DECL_CONSTEXPR inline bool overlapsLine(int line) const Q_DECL_NOEXCEPT
402 {
403 return line >= start().line() && line <= end().line();
404 }
405
406 /**
407 * Check to see if this range overlaps \p column; that is, if \p column is
408 * between start().column() and end().column(). This function is most likely
409 * to be useful in relation to block text editing.
410 *
411 * \param column the column to test
412 *
413 * \return \e true if the column is between the range's starting and ending
414 * columns, otherwise \e false.
415 */
overlapsColumn(int column)416 Q_DECL_CONSTEXPR inline bool overlapsColumn(int column) const Q_DECL_NOEXCEPT
417 {
418 return start().column() <= column && end().column() > column;
419 }
420
421 /**
422 * Check whether \p cursor is located at either of the start() or end()
423 * boundaries.
424 *
425 * \param cursor cursor to check
426 *
427 * \return \e true if the cursor is equal to \p start() or \p end(),
428 * otherwise \e false.
429 */
boundaryAtCursor(const Cursor & cursor)430 Q_DECL_CONSTEXPR inline bool boundaryAtCursor(const Cursor &cursor) const Q_DECL_NOEXCEPT
431 {
432 return cursor == start() || cursor == end();
433 }
434 //!\}
435 // END
436
437 /**
438 * Intersects this range with another, returning the shared area of
439 * the two ranges.
440 *
441 * \param range other range to intersect with this
442 *
443 * \return the intersection of this range and the supplied \a range.
444 */
intersect(const Range & range)445 Q_DECL_CONSTEXPR inline Range intersect(const Range &range) const Q_DECL_NOEXCEPT
446 {
447 return ((!isValid() || !range.isValid() || *this > range || *this < range)) ? invalid() : Range(qMax(start(), range.start()), qMin(end(), range.end()));
448 }
449
450 /**
451 * Returns the smallest range which encompasses this range and the
452 * supplied \a range.
453 *
454 * \param range other range to encompass
455 *
456 * \return the smallest range which contains this range and the supplied \a range.
457 */
encompass(const Range & range)458 Q_DECL_CONSTEXPR inline Range encompass(const Range &range) const Q_DECL_NOEXCEPT
459 {
460 return (!isValid()) ? (range.isValid() ? range : invalid())
461 : (!range.isValid()) ? (*this) : Range(qMin(start(), range.start()), qMax(end(), range.end()));
462 }
463
464 /**
465 * Addition operator. Takes two ranges and returns their summation.
466 *
467 * \param r1 the first range
468 * \param r2 the second range
469 *
470 * \return a the summation of the two input ranges
471 */
472 Q_DECL_CONSTEXPR inline friend Range operator+(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
473 {
474 return Range(r1.start() + r2.start(), r1.end() + r2.end());
475 }
476
477 /**
478 * Addition assignment operator. Adds \p r2 to this range.
479 *
480 * \param r1 the first range
481 * \param r2 the second range
482 *
483 * \return a reference to the cursor which has just been added to
484 */
485 inline friend Range &operator+=(Range &r1, const Range &r2) Q_DECL_NOEXCEPT
486 {
487 r1.setRange(r1.start() + r2.start(), r1.end() + r2.end());
488 return r1;
489 }
490
491 /**
492 * Subtraction operator. Takes two ranges and returns the subtraction
493 * of \p r2 from \p r1.
494 *
495 * \param r1 the first range
496 * \param r2 the second range
497 *
498 * \return a range representing the subtraction of \p r2 from \p r1
499 */
500 Q_DECL_CONSTEXPR inline friend Range operator-(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
501 {
502 return Range(r1.start() - r2.start(), r1.end() - r2.end());
503 }
504
505 /**
506 * Subtraction assignment operator. Subtracts \p r2 from \p r1.
507 *
508 * \param r1 the first range
509 * \param r2 the second range
510 *
511 * \return a reference to the range which has just been subtracted from
512 */
513 inline friend Range &operator-=(Range &r1, const Range &r2) Q_DECL_NOEXCEPT
514 {
515 r1.setRange(r1.start() - r2.start(), r1.end() - r2.end());
516 return r1;
517 }
518
519 /**
520 * Intersects \a r1 and \a r2.
521 *
522 * \param r1 the first range
523 * \param r2 the second range
524 *
525 * \return the intersected range, invalid() if there is no overlap
526 */
527 Q_DECL_CONSTEXPR inline friend Range operator&(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
528 {
529 return r1.intersect(r2);
530 }
531
532 /**
533 * Intersects \a r1 with \a r2 and assigns the result to \a r1.
534 *
535 * \param r1 the range to assign the intersection to
536 * \param r2 the range to intersect \a r1 with
537 *
538 * \return a reference to this range, after the intersection has taken place
539 */
540 inline friend Range &operator&=(Range &r1, const Range &r2) Q_DECL_NOEXCEPT
541 {
542 r1.setRange(r1.intersect(r2));
543 return r1;
544 }
545
546 /**
547 * Equality operator.
548 *
549 * \param r1 first range to compare
550 * \param r2 second range to compare
551 *
552 * \return \e true if \e r1 and \e r2 equal, otherwise \e false
553 */
554 Q_DECL_CONSTEXPR inline friend bool operator==(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
555 {
556 return r1.start() == r2.start() && r1.end() == r2.end();
557 }
558
559 /**
560 * Inequality operator.
561 *
562 * \param r1 first range to compare
563 * \param r2 second range to compare
564 *
565 * \return \e true if \e r1 and \e r2 do \e not equal, otherwise \e false
566 */
567 Q_DECL_CONSTEXPR inline friend bool operator!=(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
568 {
569 return r1.start() != r2.start() || r1.end() != r2.end();
570 }
571
572 /**
573 * Greater than operator. Looks only at the position of the two ranges,
574 * does not consider their size.
575 *
576 * \param r1 first range to compare
577 * \param r2 second range to compare
578 *
579 * \return \e true if \e r1 starts after where \e r2 ends, otherwise \e false
580 */
581 Q_DECL_CONSTEXPR inline friend bool operator>(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
582 {
583 return r1.start() > r2.end();
584 }
585
586 /**
587 * Less than operator. Looks only at the position of the two ranges,
588 * does not consider their size.
589 *
590 * \param r1 first range to compare
591 * \param r2 second range to compare
592 *
593 * \return \e true if \e r1 ends before \e r2 begins, otherwise \e false
594 */
595 Q_DECL_CONSTEXPR inline friend bool operator<(const Range &r1, const Range &r2) Q_DECL_NOEXCEPT
596 {
597 return r1.end() < r2.start();
598 }
599
600 /**
601 * qDebug() stream operator. Writes this range to the debug output in a nicely formatted way.
602 */
603 inline friend QDebug operator<<(QDebug s, const Range &range)
604 {
605 s << "[" << range.start() << " -> " << range.end() << "]";
606 return s;
607 }
608
609 private:
610 /**
611 * This range's start cursor pointer.
612 *
613 * \internal
614 */
615 Cursor m_start;
616
617 /**
618 * This range's end cursor pointer.
619 *
620 * \internal
621 */
622 Cursor m_end;
623 };
624
625 }
626
627 Q_DECLARE_TYPEINFO(KTextEditor::Range, Q_MOVABLE_TYPE);
Q_DECLARE_METATYPE(KTextEditor::Range)628 Q_DECLARE_METATYPE(KTextEditor::Range)
629
630 /**
631 * QHash function for KTextEditor::Range.
632 * Returns the hash value for @p range.
633 */
634 inline uint qHash(const KTextEditor::Range &range, uint seed = 0) Q_DECL_NOTHROW
635 {
636 return qHash(qMakePair(qHash(range.start()), qHash(range.end())), seed);
637 }
638
639 namespace QTest
640 {
641 // forward declaration of template in qtestcase.h
642 template<typename T>
643 char *toString(const T &);
644
645 /**
646 * QTestLib integration to have nice output in e.g. QCOMPARE failures.
647 */
648 template<>
649 KTEXTEDITOR_EXPORT char *toString(const KTextEditor::Range &range);
650 }
651
652 #endif
653