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

source code of ktexteditor/src/include/ktexteditor/range.h