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 | |
33 | namespace 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 | */ |
55 | class KTEXTEDITOR_EXPORT Range |
56 | { |
57 | public: |
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 | |
580 | private: |
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 | |
598 | Q_DECLARE_TYPEINFO(KTextEditor::Range, Q_MOVABLE_TYPE); |
599 | Q_DECLARE_METATYPE(KTextEditor::Range) |
600 | |
601 | /** |
602 | * QHash function for KTextEditor::Range. |
603 | * Returns the hash value for @p range. |
604 | */ |
605 | inline 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 | |
610 | namespace 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 | |