File indexing completed on 2024-05-19 04:00:02

0001 /*
0002     SPDX-FileCopyrightText: 2001-2005 Christoph Cullmann <cullmann@kde.org>
0003     SPDX-FileCopyrightText: 2021 Dominik Haumann <dhaumann@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KTEXTEDITOR_LINERANGE_H
0009 #define KTEXTEDITOR_LINERANGE_H
0010 
0011 #include <ktexteditor_export.h>
0012 
0013 #include <QtGlobal>
0014 
0015 class QDebug;
0016 class QString;
0017 class QStringView;
0018 
0019 namespace KTextEditor
0020 {
0021 /**
0022  * @class LineRange linerange.h <KTextEditor/LineRange>
0023  *
0024  * @short An object representing lines from a start line to an end line.
0025  *
0026  * A LineRange is a basic class which represents a range of lines, from a start()
0027  * line to an end() line.
0028  *
0029  * For simplicity and convenience, ranges always maintain their start() line to
0030  * be before or equal to their end() line. Attempting to set either the
0031  * start or end of the range beyond the respective end or start will result in
0032  * both values being set to the specified line. In the constructor, the
0033  * start and end will be swapped if necessary.
0034  *
0035  * @sa Range
0036  *
0037  * @since 5.79
0038  * @author Dominik Haumann \<dhaumann@kde.org\>
0039  */
0040 class KTEXTEDITOR_EXPORT LineRange
0041 {
0042 public:
0043     /**
0044      * Default constructor. Creates a valid line range with both start and end
0045      * line set to 0.
0046      */
0047     constexpr LineRange() noexcept = default;
0048 
0049     /**
0050      * Constructor which creates a range from \e start to \e end.
0051      * If start is after end, they will be swapped.
0052      *
0053      * @param start start line
0054      * @param end end line
0055      */
0056     constexpr LineRange(int start, int end) noexcept
0057         : m_start(qMin(start, end))
0058         , m_end(qMax(start, end))
0059     {
0060     }
0061 
0062     /**
0063      * Validity check. In the base class, returns true unless the line range starts before (0,0).
0064      */
0065     constexpr bool isValid() const noexcept
0066     {
0067         return m_start >= 0 && m_end >= 0;
0068     }
0069 
0070     /**
0071      * Returns an invalid line range.
0072      */
0073     constexpr static LineRange invalid() noexcept
0074     {
0075         return LineRange(-1, -1);
0076     }
0077 
0078     /**
0079      * Returns the line range as string in the format
0080      * "[start line, end line]".
0081      * @see fromString()
0082      */
0083     QString toString() const;
0084 
0085     /**
0086      * Returns a LineRange created from the string \p str containing the format
0087      * "[start line, end line]".
0088      * In case the string cannot be parsed, an LineRange::invalid() is returned.
0089      * @see toString()
0090      */
0091     static LineRange fromString(QStringView str) noexcept;
0092 
0093     /**
0094      * @name Position
0095      *
0096      * The following functions provide access to, and manipulation of, the range's position.
0097      * @{
0098      */
0099 
0100     /**
0101      * Get the start line of this line range. This will always be <= end().
0102      *
0103      * @returns the start line of this line range.
0104      */
0105     constexpr int start() const noexcept
0106     {
0107         return m_start;
0108     }
0109 
0110     /**
0111      * Get the end line of this line range. This will always be >= start().
0112      *
0113      * @returns the end line of this line range.
0114      */
0115     constexpr int end() const noexcept
0116     {
0117         return m_end;
0118     }
0119 
0120     /**
0121      * Set the start and end lines to \e start and \e end respectively.
0122      *
0123      * \note If \e start is after \e end, they will be reversed.
0124      *
0125      * \param start start line
0126      * \param end end line
0127      */
0128     void setRange(LineRange range) noexcept
0129     {
0130         setRange(range.start(), range.end());
0131     }
0132 
0133     /**
0134      * Set the start and end lines to \e start and \e end respectively.
0135      *
0136      * \note If \e start is after \e end, they will be reversed.
0137      *
0138      * \param start start line
0139      * \param end end line
0140      */
0141     void setRange(int start, int end) noexcept
0142     {
0143         m_start = qMin(start, end);
0144         m_end = qMax(start, end);
0145     }
0146 
0147     /**
0148      * Convenience function.  Set the start and end lines to \p line.
0149      *
0150      * @param line the line number to assign to start() and end()
0151      */
0152     void setBothLines(int line) noexcept
0153     {
0154         m_start = line;
0155         m_end = line;
0156     }
0157 
0158     /**
0159      * Set the start line to \e start.
0160      *
0161      * @note If \e start is after current end, start and end will be set to new start value.
0162      *
0163      * @param start new start line
0164      */
0165     void setStart(int start) noexcept
0166     {
0167         if (start > end()) {
0168             setRange(start, start);
0169         } else {
0170             setRange(start, end());
0171         }
0172     }
0173 
0174     /**
0175      * Set the end line to \e end.
0176      *
0177      * @note If \e end is in front of current start, start and end will be set to new end value.
0178      *
0179      * @param end new end line
0180      */
0181     void setEnd(int end) noexcept
0182     {
0183         if (end < start()) {
0184             setRange(end, end);
0185         } else {
0186             setRange(start(), end);
0187         }
0188     }
0189 
0190     /**
0191      * Expand this line range if necessary to contain \p range.
0192      *
0193      * @param range range which this range should contain
0194      *
0195      * @return \e true if expansion occurred, \e false otherwise
0196      */
0197     bool expandToRange(LineRange range) noexcept
0198     {
0199         if (start() > range.start())
0200             if (end() < range.end()) {
0201                 setRange(range);
0202             } else {
0203                 setStart(range.start());
0204             }
0205         else if (end() < range.end()) {
0206             setEnd(range.end());
0207         } else {
0208             return false;
0209         }
0210 
0211         return true;
0212     }
0213 
0214     /**
0215      * Confine this range if necessary to fit within \p range.
0216      *
0217      * @param range range which should contain this range
0218      *
0219      * @return \e true if confinement occurred, \e false otherwise
0220      */
0221     bool confineToRange(LineRange range) noexcept
0222     {
0223         if (start() < range.start())
0224             if (end() > range.end()) {
0225                 setRange(range);
0226             } else {
0227                 setStart(range.start());
0228             }
0229         else if (end() > range.end()) {
0230             setEnd(range.end());
0231         } else {
0232             return false;
0233         }
0234 
0235         return true;
0236     }
0237 
0238     /**
0239      * Check whether this line range is on one line.
0240      *
0241      * @return \e true if both the start and end line are equal, otherwise \e false
0242      */
0243     constexpr bool onSingleLine() const noexcept
0244     {
0245         return start() == end();
0246     }
0247 
0248     /**
0249      * Returns the number of lines separating the start() and end() line.
0250      *
0251      * @return the number of lines separating the start() and end() line;
0252      *         0 if the start and end lines are the same.
0253      */
0254     constexpr int numberOfLines() const noexcept
0255     {
0256         return end() - start();
0257     }
0258 
0259     // BEGIN comparison functions
0260     /**
0261      * @}
0262      *
0263      * @name Comparison
0264      *
0265      * The following functions perform checks against this range in comparison
0266      * to other lines and ranges.
0267      * @{
0268      */
0269     /**
0270      * Check whether the this range wholly encompasses \e range.
0271      *
0272      * @param range range to check
0273      *
0274      * @return \e true, if this range contains \e range, otherwise \e false
0275      */
0276     constexpr bool contains(LineRange range) const noexcept
0277     {
0278         return range.start() >= start() && range.end() <= end();
0279     }
0280 
0281     /**
0282      * Returns true if this range wholly encompasses \p line.
0283      *
0284      * @param line line to check
0285      *
0286      * @return \e true if the line is wholly encompassed by this range, otherwise \e false.
0287      */
0288     constexpr bool containsLine(int line) const noexcept
0289     {
0290         return line >= start() && line < end();
0291     }
0292 
0293     /**
0294      * Check whether the this range overlaps with \e range.
0295      *
0296      * @param range range to check against
0297      *
0298      * @return \e true, if this range overlaps with \e range, otherwise \e false
0299      */
0300     constexpr bool overlaps(LineRange range) const noexcept
0301     {
0302         return (range.start() <= start()) ? (range.end() > start()) : (range.end() >= end()) ? (range.start() < end()) : contains(range);
0303     }
0304 
0305     /**
0306      * Check whether the range overlaps at least part of \e line.
0307      *
0308      * @param line line to check
0309      *
0310      * @return \e true, if the range overlaps at least part of \e line, otherwise \e false
0311      */
0312     constexpr bool overlapsLine(int line) const noexcept
0313     {
0314         return line >= start() && line <= end();
0315     }
0316     //!\}
0317     // END
0318 
0319     /**
0320      * Intersects this line range with another, returning the shared lines of
0321      * the two line ranges.
0322      *
0323      * @param range other line range to intersect with this
0324      *
0325      * @return the intersection of this range and the supplied \a range.
0326      */
0327     constexpr LineRange intersect(LineRange range) const noexcept
0328     {
0329         return ((!isValid() || !range.isValid() || *this > range || *this < range)) ? invalid()
0330                                                                                     : LineRange(qMax(start(), range.start()), qMin(end(), range.end()));
0331     }
0332 
0333     /**
0334      * Returns the smallest range which encompasses this line range and the
0335      * supplied \a range.
0336      *
0337      * @param range other range to encompass
0338      *
0339      * @return the smallest range which contains this range and the supplied \a range.
0340      */
0341     constexpr LineRange encompass(LineRange range) const noexcept
0342     {
0343         return (!isValid())      ? (range.isValid() ? range : invalid())
0344             : (!range.isValid()) ? (*this)
0345                                  : LineRange(qMin(start(), range.start()), qMax(end(), range.end()));
0346     }
0347 
0348     /**
0349      * Addition operator. Takes two ranges and returns their summation.
0350      *
0351      * @param r1 the first range
0352      * @param r2 the second range
0353      *
0354      * @return a the summation of the two input ranges
0355      */
0356     constexpr friend LineRange operator+(LineRange r1, LineRange r2) noexcept
0357     {
0358         return LineRange(r1.start() + r2.start(), r1.end() + r2.end());
0359     }
0360 
0361     /**
0362      * Addition assignment operator. Adds \p r2 to this range.
0363      *
0364      * @param r1 the first range
0365      * @param r2 the second range
0366      *
0367      * @return a reference to the line range which has just been added to
0368      */
0369     friend LineRange &operator+=(LineRange &r1, LineRange r2) noexcept
0370     {
0371         r1.setRange(r1.start() + r2.start(), r1.end() + r2.end());
0372         return r1;
0373     }
0374 
0375     /**
0376      * Subtraction operator. Takes two ranges and returns the subtraction
0377      * of \p r2 from \p r1.
0378      *
0379      * @param r1 the first range
0380      * @param r2 the second range
0381      *
0382      * @return a range representing the subtraction of \p r2 from \p r1
0383      */
0384     constexpr friend LineRange operator-(LineRange r1, LineRange r2) noexcept
0385     {
0386         return LineRange(r1.start() - r2.start(), r1.end() - r2.end());
0387     }
0388 
0389     /**
0390      * Subtraction assignment operator. Subtracts \p r2 from \p r1.
0391      *
0392      * @param r1 the first range
0393      * @param r2 the second range
0394      *
0395      * @return a reference to the range which has just been subtracted from
0396      */
0397     friend LineRange &operator-=(LineRange &r1, LineRange r2) noexcept
0398     {
0399         r1.setRange(r1.start() - r2.start(), r1.end() - r2.end());
0400         return r1;
0401     }
0402 
0403     /**
0404      * Intersects \a r1 and \a r2.
0405      *
0406      * @param r1 the first range
0407      * @param r2 the second range
0408      *
0409      * @return the intersected range, invalid() if there is no overlap
0410      */
0411     constexpr friend LineRange operator&(LineRange r1, LineRange r2) noexcept
0412     {
0413         return r1.intersect(r2);
0414     }
0415 
0416     /**
0417      * Intersects \a r1 with \a r2 and assigns the result to \a r1.
0418      *
0419      * @param r1 the range to assign the intersection to
0420      * @param r2 the range to intersect \a r1 with
0421      *
0422      * @return a reference to this range, after the intersection has taken place
0423      */
0424     friend LineRange &operator&=(LineRange &r1, LineRange r2) noexcept
0425     {
0426         r1.setRange(r1.intersect(r2));
0427         return r1;
0428     }
0429 
0430     /**
0431      * Equality operator.
0432      *
0433      * @param r1 first range to compare
0434      * @param r2 second range to compare
0435      *
0436      * @return \e true if \e r1 and \e r2 equal, otherwise \e false
0437      */
0438     constexpr friend bool operator==(LineRange r1, LineRange r2) noexcept
0439     {
0440         return r1.start() == r2.start() && r1.end() == r2.end();
0441     }
0442 
0443     /**
0444      * Inequality operator.
0445      *
0446      * @param r1 first range to compare
0447      * @param r2 second range to compare
0448      *
0449      * @return \e true if \e r1 and \e r2 do \e not equal, otherwise \e false
0450      */
0451     constexpr friend bool operator!=(LineRange r1, LineRange r2) noexcept
0452     {
0453         return r1.start() != r2.start() || r1.end() != r2.end();
0454     }
0455 
0456     /**
0457      * Greater than operator.  Looks only at the lines of the two ranges,
0458      * does not consider their size.
0459      *
0460      * @param r1 first range to compare
0461      * @param r2 second range to compare
0462      *
0463      * @return \e true if \e r1 starts after where \e r2 ends, otherwise \e false
0464      */
0465     constexpr friend bool operator>(LineRange r1, LineRange r2) noexcept
0466     {
0467         return r1.start() > r2.end();
0468     }
0469 
0470     /**
0471      * Less than operator.  Looks only at the lines of the two ranges,
0472      * does not consider their size.
0473      *
0474      * @param r1 first range to compare
0475      * @param r2 second range to compare
0476      *
0477      * @return \e true if \e r1 ends before \e r2 begins, otherwise \e false
0478      */
0479     constexpr friend bool operator<(LineRange r1, LineRange r2) noexcept
0480     {
0481         return r1.end() < r2.start();
0482     }
0483 
0484 private:
0485     /**
0486      * This range's start line.
0487      *
0488      * @internal
0489      */
0490     int m_start = 0;
0491 
0492     /**
0493      * This range's end line.
0494      *
0495      * @internal
0496      */
0497     int m_end = 0;
0498 };
0499 
0500 /**
0501  * QHash function for KTextEditor::LineRange.
0502  * Returns the hash value for @p range.
0503  */
0504 KTEXTEDITOR_EXPORT size_t qHash(KTextEditor::LineRange range, size_t seed = 0) noexcept;
0505 }
0506 
0507 Q_DECLARE_TYPEINFO(KTextEditor::LineRange, Q_PRIMITIVE_TYPE);
0508 
0509 /**
0510  * qDebug() stream operator.  Writes this range to the debug output in a nicely formatted way.
0511  */
0512 KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, KTextEditor::LineRange range);
0513 
0514 namespace QTest
0515 {
0516 // forward declaration of template in qtestcase.h
0517 template<typename T>
0518 char *toString(const T &);
0519 
0520 /**
0521  * QTestLib integration to have nice output in e.g. QCOMPARE failures.
0522  */
0523 template<>
0524 KTEXTEDITOR_EXPORT char *toString(const KTextEditor::LineRange &range);
0525 }
0526 
0527 #endif