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