1 | /* |
2 | SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <rodda@kde.org> |
3 | SPDX-FileCopyrightText: 2001-2005 Christoph Cullmann <cullmann@kde.org> |
4 | SPDX-FileCopyrightText: 2014 Dominik Haumann <dhaumann@kde.org> |
5 | SPDX-FileCopyrightText: 2002 Christian Couder <christian@kdevelop.org> |
6 | SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org> |
7 | SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de> |
8 | |
9 | SPDX-License-Identifier: LGPL-2.0-or-later |
10 | */ |
11 | |
12 | #ifndef KTEXTEDITOR_CURSOR_H |
13 | #define KTEXTEDITOR_CURSOR_H |
14 | |
15 | #include <ktexteditor_export.h> |
16 | |
17 | #include <QtGlobal> |
18 | |
19 | class QDebug; |
20 | class QString; |
21 | class QStringView; |
22 | |
23 | namespace KTextEditor |
24 | { |
25 | class Document; |
26 | class Range; |
27 | |
28 | /** |
29 | * \class Cursor cursor.h <KTextEditor/Cursor> |
30 | * |
31 | * \short The Cursor represents a position in a Document. |
32 | * |
33 | * \section kte_cursor_intro Introduction |
34 | * A Cursor represents a position in a Document through a tuple |
35 | * of two int%s, namely the line() and column(). A Cursor maintains |
36 | * no affiliation with a particular Document, meaning that it remains |
37 | * constant if not changed through the Cursor API. |
38 | * |
39 | * \section kte_cursor_notes Important Notes |
40 | * |
41 | * Working with a cursor, one should be aware of the following notes: |
42 | * - Lines and columns start a 0. |
43 | * - The Cursor class is designed to be passed by value (only 8 Bytes). |
44 | * - Think of cursors as having their position at the start of a character, |
45 | * not in the middle of one. |
46 | * - invalid() Cursor%s are located at (-1, -1). In addition, a Cursor |
47 | * is invalid(), if either its line() and/or its column() is arbitrarily |
48 | * negative, i.e. < 0. |
49 | * - All Cursor%s with line() >= 0 and column() >= 0 are valid. In this case |
50 | * isValid() returns \e true. |
51 | * - A Cursor has a non-virtual destructor. Hence, you cannot derive from Cursor. |
52 | * |
53 | * \section kte_cursor_properties Cursor Efficiency |
54 | * |
55 | * The Cursor consists of just two int%s, the line() and the column(). |
56 | * Therefore, a Cursor instance takes 8 Bytes of memory. Further, a Cursor |
57 | * is a non-virtual class, turning it into a primitive old data type (POD). |
58 | * Thus, it can be moved and copied very efficiently. |
59 | * |
60 | * \section kte_cursor_more Additional Concepts |
61 | * |
62 | * In addition to the Cursor, the KTextEditor API provides advanced concepts: |
63 | * - The DocumentCursor is a Cursor bound to a specific Document. In addition |
64 | * to the Cursor API, it provides convenience functions like |
65 | * DocumentCursor::isValidTextPosition() or DocumentCursor::move(). |
66 | * The DocumentCursor does not maintain its position, though. |
67 | * - The MovingCursor is also bound to a specific Document. In addition to the |
68 | * DocumentCursor, the MovingCursor maintains its position, meaning that |
69 | * whenever the Document changes, the MovingCursor moves, too. |
70 | * - The Cursor forms the basis for the Range. |
71 | * |
72 | * \sa DocumentCursor, MovingCursor, Range |
73 | */ |
74 | class KTEXTEDITOR_EXPORT Cursor |
75 | { |
76 | public: |
77 | /** |
78 | * The default constructor creates a cursor at position (0, 0). |
79 | */ |
80 | constexpr Cursor() noexcept = default; |
81 | |
82 | /** |
83 | * This constructor creates a cursor initialized with \p line |
84 | * and \p column. |
85 | * \param line line for cursor |
86 | * \param column column for cursor |
87 | */ |
88 | constexpr Cursor(int line, int column) noexcept |
89 | : m_line(line) |
90 | , m_column(column) |
91 | { |
92 | } |
93 | |
94 | /** |
95 | * Returns whether the current position of this cursor is a valid position |
96 | * (line + column must both be >= 0). |
97 | * |
98 | * @note If you want to check, whether a cursor position is a valid |
99 | * \e text-position, use DocumentCursor::isValidTextPosition(), |
100 | * or Document::isValidTextPosition(). |
101 | */ |
102 | constexpr bool isValid() const noexcept |
103 | { |
104 | return m_line >= 0 && m_column >= 0; |
105 | } |
106 | |
107 | /** |
108 | * Returns an invalid cursor. |
109 | * The returned cursor position is set to (-1, -1). |
110 | * \see isValid() |
111 | */ |
112 | constexpr static Cursor invalid() noexcept |
113 | { |
114 | return Cursor(-1, -1); |
115 | } |
116 | |
117 | /** |
118 | * Returns a cursor representing the start of any document - i.e., line 0, column 0. |
119 | */ |
120 | constexpr static Cursor start() noexcept |
121 | { |
122 | return Cursor(); |
123 | } |
124 | |
125 | /** |
126 | * Returns the cursor position as string in the format "(line, column)". |
127 | * \see fromString() |
128 | */ |
129 | QString toString() const; |
130 | |
131 | /** |
132 | * Returns a Cursor created from the string \p str containing the format |
133 | * "(line, column)". In case the string cannot be parsed, Cursor::invalid() |
134 | * is returned. |
135 | * \see toString() |
136 | */ |
137 | static Cursor fromString(QStringView str) noexcept; |
138 | |
139 | /** |
140 | * \name Position |
141 | * |
142 | * The following functions provide access to, and manipulation of, the cursor's position. |
143 | * \{ |
144 | */ |
145 | /** |
146 | * Set the current cursor position to \e position. |
147 | * |
148 | * \param position new cursor position |
149 | */ |
150 | void setPosition(Cursor position) noexcept |
151 | { |
152 | m_line = position.m_line; |
153 | m_column = position.m_column; |
154 | } |
155 | |
156 | /** |
157 | * \overload |
158 | * |
159 | * Set the cursor position to \e line and \e column. |
160 | * |
161 | * \param line new cursor line |
162 | * \param column new cursor column |
163 | */ |
164 | void setPosition(int line, int column) noexcept |
165 | { |
166 | m_line = line; |
167 | m_column = column; |
168 | } |
169 | |
170 | /** |
171 | * Retrieve the line on which this cursor is situated. |
172 | * \return line number, where 0 is the first line. |
173 | */ |
174 | constexpr int line() const noexcept |
175 | { |
176 | return m_line; |
177 | } |
178 | |
179 | /** |
180 | * Set the cursor line to \e line. |
181 | * \param line new cursor line |
182 | */ |
183 | void setLine(int line) noexcept |
184 | { |
185 | m_line = line; |
186 | } |
187 | |
188 | /** |
189 | * Retrieve the column on which this cursor is situated. |
190 | * \return column number, where 0 is the first column. |
191 | */ |
192 | constexpr int column() const noexcept |
193 | { |
194 | return m_column; |
195 | } |
196 | |
197 | /** |
198 | * Set the cursor column to \e column. |
199 | * \param column new cursor column |
200 | */ |
201 | void setColumn(int column) noexcept |
202 | { |
203 | m_column = column; |
204 | } |
205 | |
206 | /** |
207 | * Determine if this cursor is located at the start of a line (= at column 0). |
208 | * \return \e true if the cursor is situated at the start of the line, \e false if it isn't. |
209 | */ |
210 | constexpr bool atStartOfLine() const noexcept |
211 | { |
212 | return m_column == 0; |
213 | } |
214 | |
215 | /** |
216 | * Determine if this cursor is located at the start of a document (= at position (0, 0)). |
217 | * \return \e true if the cursor is situated at the start of the document, \e false if it isn't. |
218 | */ |
219 | constexpr bool atStartOfDocument() const noexcept |
220 | { |
221 | return m_line == 0 && m_column == 0; |
222 | } |
223 | |
224 | /** |
225 | * Get both the line and column of the cursor position. |
226 | * \param line will be filled with current cursor line |
227 | * \param column will be filled with current cursor column |
228 | */ |
229 | void position(int &line, int &column) const noexcept |
230 | { |
231 | line = m_line; |
232 | column = m_column; |
233 | } |
234 | //!\} |
235 | |
236 | /** |
237 | * Addition operator. Takes two cursors and returns their summation. |
238 | * \param c1 the first position |
239 | * \param c2 the second position |
240 | * \return a the summation of the two input cursors |
241 | */ |
242 | constexpr friend Cursor operator+(Cursor c1, Cursor c2) noexcept |
243 | { |
244 | return Cursor(c1.line() + c2.line(), c1.column() + c2.column()); |
245 | } |
246 | |
247 | /** |
248 | * Addition assignment operator. Adds \p c2 to this cursor. |
249 | * \param c1 the cursor being added to |
250 | * \param c2 the position to add |
251 | * \return a reference to the cursor which has just been added to |
252 | */ |
253 | friend Cursor &operator+=(Cursor &c1, Cursor c2) noexcept |
254 | { |
255 | c1.setPosition(line: c1.line() + c2.line(), column: c1.column() + c2.column()); |
256 | return c1; |
257 | } |
258 | |
259 | /** |
260 | * Subtraction operator. Takes two cursors and returns the subtraction |
261 | * of \p c2 from \p c1. |
262 | * |
263 | * \param c1 the first position |
264 | * \param c2 the second position |
265 | * \return a cursor representing the subtraction of \p c2 from \p c1 |
266 | */ |
267 | constexpr friend Cursor operator-(Cursor c1, Cursor c2) noexcept |
268 | { |
269 | return Cursor(c1.line() - c2.line(), c1.column() - c2.column()); |
270 | } |
271 | |
272 | /** |
273 | * Subtraction assignment operator. Subtracts \p c2 from \p c1. |
274 | * \param c1 the cursor being subtracted from |
275 | * \param c2 the position to subtract |
276 | * \return a reference to the cursor which has just been subtracted from |
277 | */ |
278 | friend Cursor &operator-=(Cursor &c1, Cursor c2) noexcept |
279 | { |
280 | c1.setPosition(line: c1.line() - c2.line(), column: c1.column() - c2.column()); |
281 | return c1; |
282 | } |
283 | |
284 | /** |
285 | * Equality operator. |
286 | * |
287 | * \note comparison between two invalid cursors is undefined. |
288 | * comparison between and invalid and a valid cursor will always be \e false. |
289 | * |
290 | * \param c1 first cursor to compare |
291 | * \param c2 second cursor to compare |
292 | * \return \e true, if c1's and c2's line and column are \e equal. |
293 | */ |
294 | constexpr friend bool operator==(Cursor c1, Cursor c2) noexcept |
295 | { |
296 | return c1.line() == c2.line() && c1.column() == c2.column(); |
297 | } |
298 | |
299 | /** |
300 | * Inequality operator. |
301 | * \param c1 first cursor to compare |
302 | * \param c2 second cursor to compare |
303 | * \return \e true, if c1's and c2's line and column are \e not equal. |
304 | */ |
305 | constexpr friend bool operator!=(Cursor c1, Cursor c2) noexcept |
306 | { |
307 | return !(c1 == c2); |
308 | } |
309 | |
310 | /** |
311 | * Greater than operator. |
312 | * \param c1 first cursor to compare |
313 | * \param c2 second cursor to compare |
314 | * \return \e true, if c1's position is greater than c2's position, |
315 | * otherwise \e false. |
316 | */ |
317 | constexpr friend bool operator>(Cursor c1, Cursor c2) noexcept |
318 | { |
319 | return c1.line() > c2.line() || (c1.line() == c2.line() && c1.m_column > c2.m_column); |
320 | } |
321 | |
322 | /** |
323 | * Greater than or equal to operator. |
324 | * \param c1 first cursor to compare |
325 | * \param c2 second cursor to compare |
326 | * \return \e true, if c1's position is greater than or equal to c2's |
327 | * position, otherwise \e false. |
328 | */ |
329 | constexpr friend bool operator>=(Cursor c1, Cursor c2) noexcept |
330 | { |
331 | return c1.line() > c2.line() || (c1.line() == c2.line() && c1.m_column >= c2.m_column); |
332 | } |
333 | |
334 | /** |
335 | * Less than operator. |
336 | * \param c1 first cursor to compare |
337 | * \param c2 second cursor to compare |
338 | * \return \e true, if c1's position is greater than or equal to c2's |
339 | * position, otherwise \e false. |
340 | */ |
341 | constexpr friend bool operator<(Cursor c1, Cursor c2) noexcept |
342 | { |
343 | return !(c1 >= c2); |
344 | } |
345 | |
346 | /** |
347 | * Less than or equal to operator. |
348 | * \param c1 first cursor to compare |
349 | * \param c2 second cursor to compare |
350 | * \return \e true, if c1's position is lesser than or equal to c2's |
351 | * position, otherwise \e false. |
352 | */ |
353 | constexpr friend bool operator<=(Cursor c1, Cursor c2) noexcept |
354 | { |
355 | return !(c1 > c2); |
356 | } |
357 | |
358 | private: |
359 | /** |
360 | * \internal |
361 | * |
362 | * Cursor line |
363 | */ |
364 | int m_line = 0; |
365 | |
366 | /** |
367 | * \internal |
368 | * |
369 | * Cursor column |
370 | */ |
371 | int m_column = 0; |
372 | }; |
373 | |
374 | /** |
375 | * QHash function for KTextEditor::Cursor. |
376 | * Returns the hash value for @p cursor. |
377 | */ |
378 | KTEXTEDITOR_EXPORT size_t qHash(KTextEditor::Cursor cursor, size_t seed = 0) noexcept; |
379 | |
380 | } // namespace KTextEditor |
381 | |
382 | Q_DECLARE_TYPEINFO(KTextEditor::Cursor, Q_PRIMITIVE_TYPE); |
383 | |
384 | /** |
385 | * qDebug() stream operator. Writes this cursor to the debug output in a nicely formatted way. |
386 | */ |
387 | KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, KTextEditor::Cursor cursor); |
388 | |
389 | namespace QTest |
390 | { |
391 | // forward declaration of template in qtestcase.h |
392 | template<typename T> |
393 | char *toString(const T &); |
394 | |
395 | /** |
396 | * QTestLib integration to have nice output in e.g. QCOMPARE failures. |
397 | */ |
398 | template<> |
399 | KTEXTEDITOR_EXPORT char *toString(const KTextEditor::Cursor &cursor); |
400 | } |
401 | |
402 | #endif |
403 | |