1 | /* |
2 | SPDX-FileCopyrightText: 2001-2005 Christoph Cullmann <cullmann@kde.org> |
3 | SPDX-FileCopyrightText: 2021 Dominik Haumann <dhaumann@kde.org> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #ifndef KTEXTEDITOR_LINERANGE_H |
9 | #define KTEXTEDITOR_LINERANGE_H |
10 | |
11 | #include <ktexteditor_export.h> |
12 | |
13 | #include <QtGlobal> |
14 | |
15 | class QDebug; |
16 | class QString; |
17 | class QStringView; |
18 | |
19 | namespace KTextEditor |
20 | { |
21 | /*! |
22 | * \class KTextEditor::LineRange |
23 | * \inmodule KTextEditor |
24 | * \inheaderfile KTextEditor/LineRange |
25 | * |
26 | * \brief An object representing lines from a start line to an end line. |
27 | * |
28 | * A LineRange is a basic class which represents a range of lines, from a start() |
29 | * line to an end() line. |
30 | * |
31 | * For simplicity and convenience, ranges always maintain their start() line to |
32 | * be before or equal to their end() line. 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 line. In the constructor, the |
35 | * start and end will be swapped if necessary. |
36 | * |
37 | * \sa Range |
38 | * |
39 | * \since 5.79 |
40 | */ |
41 | class KTEXTEDITOR_EXPORT LineRange |
42 | { |
43 | public: |
44 | /*! |
45 | * Default constructor. Creates a valid line range with both start and end |
46 | * line set to 0. |
47 | */ |
48 | constexpr LineRange() noexcept = default; |
49 | |
50 | /*! |
51 | * Constructor which creates a range from \e start to \e end. |
52 | * If start is after end, they will be swapped. |
53 | * |
54 | * \a start is the start line |
55 | * |
56 | * \a end is the end line |
57 | * |
58 | */ |
59 | constexpr LineRange(int start, int end) noexcept |
60 | : m_start(qMin(a: start, b: end)) |
61 | , m_end(qMax(a: start, b: end)) |
62 | { |
63 | } |
64 | |
65 | /*! |
66 | * Validity check. In the base class, returns true unless the line range starts before (0,0). |
67 | */ |
68 | constexpr bool isValid() const noexcept |
69 | { |
70 | return m_start >= 0 && m_end >= 0; |
71 | } |
72 | |
73 | /*! |
74 | * Returns an invalid line range. |
75 | */ |
76 | constexpr static LineRange invalid() noexcept |
77 | { |
78 | return LineRange(-1, -1); |
79 | } |
80 | |
81 | /*! |
82 | * Returns the line range as string in the format |
83 | * "[start line, end line]". |
84 | * \sa fromString() |
85 | */ |
86 | QString toString() const; |
87 | |
88 | /*! |
89 | * Returns a LineRange created from the string \a str containing the format |
90 | * "[start line, end line]". |
91 | * In case the string cannot be parsed, an LineRange::invalid() is returned. |
92 | * \sa toString() |
93 | */ |
94 | static LineRange fromString(QStringView str) noexcept; |
95 | |
96 | /* |
97 | * Position |
98 | * |
99 | * The following functions provide access to, and manipulation of, the range's position. |
100 | * @{ |
101 | */ |
102 | |
103 | /*! |
104 | * Get the start line of this line range. This will always be <= end(). |
105 | * |
106 | * Returns the start line of this line range. |
107 | */ |
108 | constexpr int start() const noexcept |
109 | { |
110 | return m_start; |
111 | } |
112 | |
113 | /*! |
114 | * Get the end line of this line range. This will always be >= start(). |
115 | * |
116 | * Returns the end line of this line range. |
117 | */ |
118 | constexpr int end() const noexcept |
119 | { |
120 | return m_end; |
121 | } |
122 | |
123 | /*! |
124 | * Set the start and end lines of the range. |
125 | * |
126 | * \note If \e start is after \e end, they will be reversed. |
127 | * |
128 | * \a range is the new range |
129 | * |
130 | */ |
131 | void setRange(LineRange range) noexcept |
132 | { |
133 | setRange(start: range.start(), end: range.end()); |
134 | } |
135 | |
136 | /*! |
137 | * Set the start and end lines of the range. |
138 | * |
139 | * \note If \e start is after \e end, they will be reversed. |
140 | * |
141 | * \a start is the start line of the range |
142 | * |
143 | * \a end is the end line of the range |
144 | * |
145 | */ |
146 | void setRange(int start, int end) noexcept |
147 | { |
148 | m_start = qMin(a: start, b: end); |
149 | m_end = qMax(a: start, b: end); |
150 | } |
151 | |
152 | /*! |
153 | * Convenience function. Set the start and end lines to \a line. |
154 | * |
155 | * \a line the line number to assign to start() and end() |
156 | */ |
157 | void setBothLines(int line) noexcept |
158 | { |
159 | m_start = line; |
160 | m_end = line; |
161 | } |
162 | |
163 | /*! |
164 | * Set the start line to \e start. |
165 | * |
166 | * \note If \e start is after current end, start and end will be set to new start value. |
167 | * |
168 | * \a start new start line |
169 | */ |
170 | void setStart(int start) noexcept |
171 | { |
172 | if (start > end()) { |
173 | setRange(start, end: start); |
174 | } else { |
175 | setRange(start, end: end()); |
176 | } |
177 | } |
178 | |
179 | /*! |
180 | * Set the end line to \e end. |
181 | * |
182 | * \note If \e end is in front of current start, start and end will be set to new end value. |
183 | * |
184 | * \a end new end line |
185 | */ |
186 | void setEnd(int end) noexcept |
187 | { |
188 | if (end < start()) { |
189 | setRange(start: end, end); |
190 | } else { |
191 | setRange(start: start(), end); |
192 | } |
193 | } |
194 | |
195 | /*! |
196 | * Expand this line range if necessary to contain \a range. |
197 | * |
198 | * \a range range which this range should contain |
199 | * |
200 | * Returns \e true if expansion occurred, \e false otherwise |
201 | */ |
202 | bool expandToRange(LineRange range) noexcept |
203 | { |
204 | if (start() > range.start()) |
205 | if (end() < range.end()) { |
206 | setRange(range); |
207 | } else { |
208 | setStart(range.start()); |
209 | } |
210 | else if (end() < range.end()) { |
211 | setEnd(range.end()); |
212 | } else { |
213 | return false; |
214 | } |
215 | |
216 | return true; |
217 | } |
218 | |
219 | /*! |
220 | * Confine this range if necessary to fit within \a range. |
221 | * |
222 | * \a range range which should contain this range |
223 | * |
224 | * Returns \e true if confinement occurred, \e false otherwise |
225 | */ |
226 | bool confineToRange(LineRange range) noexcept |
227 | { |
228 | if (start() < range.start()) |
229 | if (end() > range.end()) { |
230 | setRange(range); |
231 | } else { |
232 | setStart(range.start()); |
233 | } |
234 | else if (end() > range.end()) { |
235 | setEnd(range.end()); |
236 | } else { |
237 | return false; |
238 | } |
239 | |
240 | return true; |
241 | } |
242 | |
243 | /*! |
244 | * Check whether this line range is on one line. |
245 | * |
246 | * Returns \e true if both the start and end line are equal, otherwise \e false |
247 | */ |
248 | constexpr bool onSingleLine() const noexcept |
249 | { |
250 | return start() == end(); |
251 | } |
252 | |
253 | /*! |
254 | * Returns the number of lines separating the start() and end() line. |
255 | * |
256 | * Returns the number of lines separating the start() and end() line; |
257 | * 0 if the start and end lines are the same. |
258 | */ |
259 | constexpr int numberOfLines() const noexcept |
260 | { |
261 | return end() - start(); |
262 | } |
263 | |
264 | // BEGIN comparison functions |
265 | /* |
266 | * @} |
267 | * |
268 | * Comparison |
269 | * |
270 | * The following functions perform checks against this range in comparison |
271 | * to other lines and ranges. |
272 | * @{ |
273 | */ |
274 | /*! |
275 | * Check whether the this range wholly encompasses \e range. |
276 | * |
277 | * \a range range to check |
278 | * |
279 | * Returns \e true, if this range contains \e range, otherwise \e false |
280 | */ |
281 | constexpr bool contains(LineRange range) const noexcept |
282 | { |
283 | return range.start() >= start() && range.end() <= end(); |
284 | } |
285 | |
286 | /*! |
287 | * Returns true if this range wholly encompasses \a line. |
288 | * |
289 | * \a line line to check |
290 | * |
291 | * Returns \e true if the line is wholly encompassed by this range, otherwise \e false. |
292 | */ |
293 | constexpr bool containsLine(int line) const noexcept |
294 | { |
295 | return line >= start() && line < end(); |
296 | } |
297 | |
298 | /*! |
299 | * Check whether the this range overlaps with \e range. |
300 | * |
301 | * \a range range to check against |
302 | * |
303 | * Returns \e true, if this range overlaps with \e range, otherwise \e false |
304 | */ |
305 | constexpr bool overlaps(LineRange range) const noexcept |
306 | { |
307 | return (range.start() <= start()) ? (range.end() > start()) : (range.end() >= end()) ? (range.start() < end()) : contains(range); |
308 | } |
309 | |
310 | /*! |
311 | * Check whether the range overlaps at least part of \e line. |
312 | * |
313 | * \a line line to check |
314 | * |
315 | * Returns \e true, if the range overlaps at least part of \e line, otherwise \e false |
316 | */ |
317 | constexpr bool overlapsLine(int line) const noexcept |
318 | { |
319 | return line >= start() && line <= end(); |
320 | } |
321 | //!\} |
322 | // END |
323 | |
324 | /*! |
325 | * Intersects this line range with another, returning the shared lines of |
326 | * the two line ranges. |
327 | * |
328 | * \a range other line range to intersect with this |
329 | * |
330 | * Returns the intersection of this range and the supplied \a range. |
331 | */ |
332 | constexpr LineRange intersect(LineRange range) const noexcept |
333 | { |
334 | return ((!isValid() || !range.isValid() || *this > range || *this < range)) ? invalid() |
335 | : LineRange(qMax(a: start(), b: range.start()), qMin(a: end(), b: range.end())); |
336 | } |
337 | |
338 | /*! |
339 | * Returns the smallest range which encompasses this line range and the |
340 | * supplied \a range. |
341 | * |
342 | * \a range other range to encompass |
343 | * |
344 | * Returns the smallest range which contains this range and the supplied \a range. |
345 | */ |
346 | constexpr LineRange encompass(LineRange range) const noexcept |
347 | { |
348 | return (!isValid()) ? (range.isValid() ? range : invalid()) |
349 | : (!range.isValid()) ? (*this) |
350 | : LineRange(qMin(a: start(), b: range.start()), qMax(a: end(), b: range.end())); |
351 | } |
352 | |
353 | /*! |
354 | * Addition operator. Takes two ranges and returns their summation. |
355 | * |
356 | * \a r1 is the first range |
357 | * |
358 | * \a r2 is the second range |
359 | * |
360 | * Returns a the summation of the two input ranges |
361 | */ |
362 | constexpr friend LineRange operator+(LineRange r1, LineRange r2) noexcept |
363 | { |
364 | return LineRange(r1.start() + r2.start(), r1.end() + r2.end()); |
365 | } |
366 | |
367 | /*! |
368 | * Addition assignment operator. Adds \a r2 to this range. |
369 | * |
370 | * \a r1 is the first range |
371 | * |
372 | * \a r2 is the second range |
373 | * |
374 | * Returns a reference to the line range which has just been added to |
375 | */ |
376 | friend LineRange &operator+=(LineRange &r1, LineRange r2) noexcept |
377 | { |
378 | r1.setRange(start: r1.start() + r2.start(), end: r1.end() + r2.end()); |
379 | return r1; |
380 | } |
381 | |
382 | /*! |
383 | * Subtraction operator. Takes two ranges and returns the subtraction |
384 | * of \a r2 from \a r1. |
385 | * |
386 | * \a r1 is the first range |
387 | * |
388 | * \a r2 is the second range |
389 | * |
390 | * Returns a range representing the subtraction of \a r2 from \a r1 |
391 | */ |
392 | constexpr friend LineRange operator-(LineRange r1, LineRange r2) noexcept |
393 | { |
394 | return LineRange(r1.start() - r2.start(), r1.end() - r2.end()); |
395 | } |
396 | |
397 | /*! |
398 | * Subtraction assignment operator. Subtracts \a r2 from \a r1. |
399 | * |
400 | * \a r1 is the first range |
401 | * |
402 | * \a r2 is the second range |
403 | * |
404 | * Returns a reference to the range which has just been subtracted from |
405 | */ |
406 | friend LineRange &operator-=(LineRange &r1, LineRange r2) noexcept |
407 | { |
408 | r1.setRange(start: r1.start() - r2.start(), end: r1.end() - r2.end()); |
409 | return r1; |
410 | } |
411 | |
412 | /*! |
413 | * Intersects \a r1 and \a r2. |
414 | * |
415 | * \a r1 is the first range |
416 | * |
417 | * \a r2 is the second range |
418 | * |
419 | * Returns the intersected range, invalid() if there is no overlap |
420 | */ |
421 | constexpr friend LineRange operator&(LineRange r1, LineRange r2) noexcept |
422 | { |
423 | return r1.intersect(range: r2); |
424 | } |
425 | |
426 | /*! |
427 | * Intersects \a r1 with \a r2 and assigns the result to \a r1. |
428 | * |
429 | * \a r1 is the range to assign the intersection to |
430 | * |
431 | * \a r2 is the range to intersect \a r1 with |
432 | * |
433 | * Returns a reference to this range, after the intersection has taken place |
434 | */ |
435 | friend LineRange &operator&=(LineRange &r1, LineRange r2) noexcept |
436 | { |
437 | r1.setRange(r1.intersect(range: r2)); |
438 | return r1; |
439 | } |
440 | |
441 | /*! |
442 | * Equality operator. |
443 | * |
444 | * \a r1 is the first range to compare |
445 | * |
446 | * \a r2 is the second range to compare |
447 | * |
448 | * Returns \e true if \e r1 and \e r2 equal, otherwise \e false |
449 | */ |
450 | constexpr friend bool operator==(LineRange r1, LineRange r2) noexcept |
451 | { |
452 | return r1.start() == r2.start() && r1.end() == r2.end(); |
453 | } |
454 | |
455 | /*! |
456 | * Inequality operator. |
457 | * |
458 | * \a r1 is the first range to compare |
459 | * |
460 | * \a r2 is the second range to compare |
461 | * |
462 | * Returns \e true if \e r1 and \e r2 do \e not equal, otherwise \e false |
463 | */ |
464 | constexpr friend bool operator!=(LineRange r1, LineRange r2) noexcept |
465 | { |
466 | return r1.start() != r2.start() || r1.end() != r2.end(); |
467 | } |
468 | |
469 | /*! |
470 | * Greater than operator. Looks only at the lines of the two ranges, |
471 | * does not consider their size. |
472 | * |
473 | * \a r1 is the first range to compare |
474 | * |
475 | * \a r2 is the second range to compare |
476 | * |
477 | * Returns \e true if \e r1 starts after where \e r2 ends, otherwise \e false |
478 | */ |
479 | constexpr friend bool operator>(LineRange r1, LineRange r2) noexcept |
480 | { |
481 | return r1.start() > r2.end(); |
482 | } |
483 | |
484 | /*! |
485 | * Less than operator. Looks only at the lines of the two ranges, |
486 | * does not consider their size. |
487 | * |
488 | * \a r1 is the first range to compare |
489 | * |
490 | * \a r2 is the second range to compare |
491 | * |
492 | * Returns \e true if \e r1 ends before \e r2 begins, otherwise \e false |
493 | */ |
494 | constexpr friend bool operator<(LineRange r1, LineRange r2) noexcept |
495 | { |
496 | return r1.end() < r2.start(); |
497 | } |
498 | |
499 | private: |
500 | /*! |
501 | * This range's start line. |
502 | * |
503 | * \internal |
504 | */ |
505 | int m_start = 0; |
506 | |
507 | /*! |
508 | * This range's end line. |
509 | * |
510 | * \internal |
511 | */ |
512 | int m_end = 0; |
513 | }; |
514 | |
515 | KTEXTEDITOR_EXPORT size_t qHash(KTextEditor::LineRange range, size_t seed = 0) noexcept; |
516 | } |
517 | |
518 | Q_DECLARE_TYPEINFO(KTextEditor::LineRange, Q_PRIMITIVE_TYPE); |
519 | |
520 | /*! |
521 | * qDebug() stream operator. Writes this range to the debug output in a nicely formatted way. |
522 | * |
523 | * \relates KTextEditor::LineRange |
524 | */ |
525 | KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, KTextEditor::LineRange range); |
526 | |
527 | namespace QTest |
528 | { |
529 | // forward declaration of template in qtestcase.h |
530 | template<typename T> |
531 | char *toString(const T &); |
532 | |
533 | /* |
534 | * QTestLib integration to have nice output in e.g. QCOMPARE failures. |
535 | */ |
536 | template<> |
537 | KTEXTEDITOR_EXPORT char *toString(const KTextEditor::LineRange &range); |
538 | } |
539 | |
540 | #endif |
541 | |