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
15class QDebug;
16class QString;
17class QStringView;
18
19namespace 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 */
41class KTEXTEDITOR_EXPORT LineRange
42{
43public:
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
499private:
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
515KTEXTEDITOR_EXPORT size_t qHash(KTextEditor::LineRange range, size_t seed = 0) noexcept;
516}
517
518Q_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 */
525KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, KTextEditor::LineRange range);
526
527namespace QTest
528{
529// forward declaration of template in qtestcase.h
530template<typename T>
531char *toString(const T &);
532
533/*
534 * QTestLib integration to have nice output in e.g. QCOMPARE failures.
535 */
536template<>
537KTEXTEDITOR_EXPORT char *toString(const KTextEditor::LineRange &range);
538}
539
540#endif
541

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