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