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

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