Line | Count | Source |
1 | | /* $OpenBSD$ */ |
2 | | |
3 | | /* |
4 | | * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 | | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 | | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include "tmux.h" |
20 | | #include <string.h> |
21 | | |
22 | | /* Initialise virtual cursor. */ |
23 | | void |
24 | | grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy) |
25 | 0 | { |
26 | 0 | gr->gd = gd; |
27 | 0 | gr->cx = cx; |
28 | 0 | gr->cy = cy; |
29 | 0 | } |
30 | | |
31 | | /* Get cursor position from reader. */ |
32 | | void |
33 | | grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy) |
34 | 0 | { |
35 | 0 | *cx = gr->cx; |
36 | 0 | *cy = gr->cy; |
37 | 0 | } |
38 | | |
39 | | /* Get length of line containing the cursor. */ |
40 | | u_int |
41 | | grid_reader_line_length(struct grid_reader *gr) |
42 | 0 | { |
43 | 0 | return (grid_line_length(gr->gd, gr->cy)); |
44 | 0 | } |
45 | | |
46 | | /* Move cursor forward one position. */ |
47 | | void |
48 | | grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore) |
49 | 0 | { |
50 | 0 | u_int px; |
51 | 0 | struct grid_cell gc; |
52 | |
|
53 | 0 | if (all) |
54 | 0 | px = gr->gd->sx; |
55 | 0 | else if (onemore) |
56 | 0 | px = grid_reader_line_length(gr); |
57 | 0 | else { |
58 | 0 | px = grid_reader_line_length(gr); |
59 | 0 | if (px != 0) |
60 | 0 | px--; |
61 | 0 | } |
62 | |
|
63 | 0 | if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { |
64 | 0 | grid_reader_cursor_start_of_line(gr, 0); |
65 | 0 | grid_reader_cursor_down(gr); |
66 | 0 | } else if (gr->cx < px) { |
67 | 0 | gr->cx++; |
68 | 0 | while (gr->cx < px) { |
69 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
70 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
71 | 0 | break; |
72 | 0 | gr->cx++; |
73 | 0 | } |
74 | 0 | } |
75 | 0 | } |
76 | | |
77 | | /* Move cursor back one position. */ |
78 | | void |
79 | | grid_reader_cursor_left(struct grid_reader *gr, int wrap) |
80 | 0 | { |
81 | 0 | struct grid_cell gc; |
82 | |
|
83 | 0 | while (gr->cx > 0) { |
84 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
85 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
86 | 0 | break; |
87 | 0 | gr->cx--; |
88 | 0 | } |
89 | 0 | if (gr->cx == 0 && gr->cy > 0 && |
90 | 0 | (wrap || |
91 | 0 | grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) { |
92 | 0 | grid_reader_cursor_up(gr); |
93 | 0 | grid_reader_cursor_end_of_line(gr, 0, 0); |
94 | 0 | } else if (gr->cx > 0) |
95 | 0 | gr->cx--; |
96 | 0 | } |
97 | | |
98 | | /* Move cursor down one line. */ |
99 | | void |
100 | | grid_reader_cursor_down(struct grid_reader *gr) |
101 | 0 | { |
102 | 0 | struct grid_cell gc; |
103 | |
|
104 | 0 | if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) |
105 | 0 | gr->cy++; |
106 | 0 | while (gr->cx > 0) { |
107 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
108 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
109 | 0 | break; |
110 | 0 | gr->cx--; |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | /* Move cursor up one line. */ |
115 | | void |
116 | | grid_reader_cursor_up(struct grid_reader *gr) |
117 | 0 | { |
118 | 0 | struct grid_cell gc; |
119 | |
|
120 | 0 | if (gr->cy > 0) |
121 | 0 | gr->cy--; |
122 | 0 | while (gr->cx > 0) { |
123 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
124 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
125 | 0 | break; |
126 | 0 | gr->cx--; |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | /* Move cursor to the start of the line. */ |
131 | | void |
132 | | grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) |
133 | 0 | { |
134 | 0 | if (wrap) { |
135 | 0 | while (gr->cy > 0 && |
136 | 0 | grid_get_line(gr->gd, gr->cy - 1)->flags & |
137 | 0 | GRID_LINE_WRAPPED) |
138 | 0 | gr->cy--; |
139 | 0 | } |
140 | 0 | gr->cx = 0; |
141 | 0 | } |
142 | | |
143 | | /* Move cursor to the end of the line. */ |
144 | | void |
145 | | grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) |
146 | 0 | { |
147 | 0 | u_int yy; |
148 | |
|
149 | 0 | if (wrap) { |
150 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
151 | 0 | while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & |
152 | 0 | GRID_LINE_WRAPPED) |
153 | 0 | gr->cy++; |
154 | 0 | } |
155 | 0 | if (all) |
156 | 0 | gr->cx = gr->gd->sx; |
157 | 0 | else |
158 | 0 | gr->cx = grid_reader_line_length(gr); |
159 | 0 | } |
160 | | |
161 | | /* Handle line wrapping while moving the cursor. */ |
162 | | static int |
163 | | grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy) |
164 | 0 | { |
165 | | /* |
166 | | * Make sure the cursor lies within the grid reader's bounding area, |
167 | | * wrapping to the next line as necessary. Return zero if the cursor |
168 | | * would wrap past the bottom of the grid. |
169 | | */ |
170 | 0 | while (gr->cx > *xx) { |
171 | 0 | if (gr->cy == *yy) |
172 | 0 | return (0); |
173 | 0 | grid_reader_cursor_start_of_line(gr, 0); |
174 | 0 | grid_reader_cursor_down(gr); |
175 | |
|
176 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
177 | 0 | *xx = gr->gd->sx - 1; |
178 | 0 | else |
179 | 0 | *xx = grid_reader_line_length(gr); |
180 | 0 | } |
181 | 0 | return (1); |
182 | 0 | } |
183 | | |
184 | | /* Check if character under cursor is in set. */ |
185 | | int |
186 | | grid_reader_in_set(struct grid_reader *gr, const char *set) |
187 | 0 | { |
188 | 0 | return (grid_in_set(gr->gd, gr->cx, gr->cy, set)); |
189 | 0 | } |
190 | | |
191 | | /* Move cursor to the start of the next word. */ |
192 | | void |
193 | | grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) |
194 | 0 | { |
195 | 0 | u_int xx, yy, width; |
196 | | |
197 | | /* Do not break up wrapped words. */ |
198 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
199 | 0 | xx = gr->gd->sx - 1; |
200 | 0 | else |
201 | 0 | xx = grid_reader_line_length(gr); |
202 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
203 | | |
204 | | /* |
205 | | * When navigating via spaces (for example with next-space) separators |
206 | | * should be empty. |
207 | | * |
208 | | * If we started on a separator that is not whitespace, skip over |
209 | | * subsequent separators that are not whitespace. Otherwise, if we |
210 | | * started on a non-whitespace character, skip over subsequent |
211 | | * characters that are neither whitespace nor separators. Then, skip |
212 | | * over whitespace (if any) until the next non-whitespace character. |
213 | | */ |
214 | 0 | if (!grid_reader_handle_wrap(gr, &xx, &yy)) |
215 | 0 | return; |
216 | 0 | if (!grid_reader_in_set(gr, WHITESPACE)) { |
217 | 0 | if (grid_reader_in_set(gr, separators)) { |
218 | 0 | do |
219 | 0 | gr->cx++; |
220 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
221 | 0 | grid_reader_in_set(gr, separators) && |
222 | 0 | !grid_reader_in_set(gr, WHITESPACE)); |
223 | 0 | } else { |
224 | 0 | do |
225 | 0 | gr->cx++; |
226 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
227 | 0 | !(grid_reader_in_set(gr, separators) || |
228 | 0 | grid_reader_in_set(gr, WHITESPACE))); |
229 | 0 | } |
230 | 0 | } |
231 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
232 | 0 | (width = grid_reader_in_set(gr, WHITESPACE))) |
233 | 0 | gr->cx += width; |
234 | 0 | } |
235 | | |
236 | | /* Move cursor to the end of the next word. */ |
237 | | void |
238 | | grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) |
239 | 0 | { |
240 | 0 | u_int xx, yy; |
241 | | |
242 | | /* Do not break up wrapped words. */ |
243 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
244 | 0 | xx = gr->gd->sx - 1; |
245 | 0 | else |
246 | 0 | xx = grid_reader_line_length(gr); |
247 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
248 | | |
249 | | /* |
250 | | * When navigating via spaces (for example with next-space), separators |
251 | | * should be empty in both modes. |
252 | | * |
253 | | * If we started on a whitespace, move until reaching the first |
254 | | * non-whitespace character. If that character is a separator, treat |
255 | | * subsequent separators as a word, and continue moving until the first |
256 | | * non-separator. Otherwise, continue moving until the first separator |
257 | | * or whitespace. |
258 | | */ |
259 | |
|
260 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy)) { |
261 | 0 | if (grid_reader_in_set(gr, WHITESPACE)) |
262 | 0 | gr->cx++; |
263 | 0 | else if (grid_reader_in_set(gr, separators)) { |
264 | 0 | do |
265 | 0 | gr->cx++; |
266 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
267 | 0 | grid_reader_in_set(gr, separators) && |
268 | 0 | !grid_reader_in_set(gr, WHITESPACE)); |
269 | 0 | return; |
270 | 0 | } else { |
271 | 0 | do |
272 | 0 | gr->cx++; |
273 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
274 | 0 | !(grid_reader_in_set(gr, WHITESPACE) || |
275 | 0 | grid_reader_in_set(gr, separators))); |
276 | 0 | return; |
277 | 0 | } |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | | /* Move to the previous place where a word begins. */ |
282 | | void |
283 | | grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, |
284 | | int already, int stop_at_eol) |
285 | 0 | { |
286 | 0 | int oldx, oldy, at_eol, word_is_letters; |
287 | | |
288 | | /* Move back to the previous word character. */ |
289 | 0 | if (already || grid_reader_in_set(gr, WHITESPACE)) { |
290 | 0 | for (;;) { |
291 | 0 | if (gr->cx > 0) { |
292 | 0 | gr->cx--; |
293 | 0 | if (!grid_reader_in_set(gr, WHITESPACE)) { |
294 | 0 | word_is_letters = |
295 | 0 | !grid_reader_in_set(gr, separators); |
296 | 0 | break; |
297 | 0 | } |
298 | 0 | } else { |
299 | 0 | if (gr->cy == 0) |
300 | 0 | return; |
301 | 0 | grid_reader_cursor_up(gr); |
302 | 0 | grid_reader_cursor_end_of_line(gr, 0, 0); |
303 | | |
304 | | /* Stop if separator at EOL. */ |
305 | 0 | if (stop_at_eol && gr->cx > 0) { |
306 | 0 | oldx = gr->cx; |
307 | 0 | gr->cx--; |
308 | 0 | at_eol = grid_reader_in_set(gr, |
309 | 0 | WHITESPACE); |
310 | 0 | gr->cx = oldx; |
311 | 0 | if (at_eol) { |
312 | 0 | word_is_letters = 0; |
313 | 0 | break; |
314 | 0 | } |
315 | 0 | } |
316 | 0 | } |
317 | 0 | } |
318 | 0 | } else |
319 | 0 | word_is_letters = !grid_reader_in_set(gr, separators); |
320 | | |
321 | | /* Move back to the beginning of this word. */ |
322 | 0 | do { |
323 | 0 | oldx = gr->cx; |
324 | 0 | oldy = gr->cy; |
325 | 0 | if (gr->cx == 0) { |
326 | 0 | if (gr->cy == 0 || |
327 | 0 | (~grid_get_line(gr->gd, gr->cy - 1)->flags & |
328 | 0 | GRID_LINE_WRAPPED)) |
329 | 0 | break; |
330 | 0 | grid_reader_cursor_up(gr); |
331 | 0 | grid_reader_cursor_end_of_line(gr, 0, 1); |
332 | 0 | } |
333 | 0 | if (gr->cx > 0) |
334 | 0 | gr->cx--; |
335 | 0 | } while (!grid_reader_in_set(gr, WHITESPACE) && |
336 | 0 | word_is_letters != grid_reader_in_set(gr, separators)); |
337 | 0 | gr->cx = oldx; |
338 | 0 | gr->cy = oldy; |
339 | 0 | } |
340 | | |
341 | | /* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */ |
342 | | static int |
343 | | grid_reader_cell_equals_data(const struct grid_cell *gc, |
344 | | const struct utf8_data *ud) |
345 | 0 | { |
346 | 0 | if (gc->flags & GRID_FLAG_PADDING) |
347 | 0 | return (0); |
348 | 0 | if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t') |
349 | 0 | return (1); |
350 | 0 | if (gc->data.size != ud->size) |
351 | 0 | return (0); |
352 | 0 | return (memcmp(gc->data.data, ud->data, gc->data.size) == 0); |
353 | 0 | } |
354 | | |
355 | | /* Jump forward to character. */ |
356 | | int |
357 | | grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) |
358 | 0 | { |
359 | 0 | struct grid_cell gc; |
360 | 0 | u_int px, py, xx, yy; |
361 | |
|
362 | 0 | px = gr->cx; |
363 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
364 | |
|
365 | 0 | for (py = gr->cy; py <= yy; py++) { |
366 | 0 | xx = grid_line_length(gr->gd, py); |
367 | 0 | while (px < xx) { |
368 | 0 | grid_get_cell(gr->gd, px, py, &gc); |
369 | 0 | if (grid_reader_cell_equals_data(&gc, jc)) { |
370 | 0 | gr->cx = px; |
371 | 0 | gr->cy = py; |
372 | 0 | return (1); |
373 | 0 | } |
374 | 0 | px++; |
375 | 0 | } |
376 | | |
377 | 0 | if (py == yy || |
378 | 0 | !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) |
379 | 0 | return (0); |
380 | 0 | px = 0; |
381 | 0 | } |
382 | 0 | return (0); |
383 | 0 | } |
384 | | |
385 | | /* Jump back to character. */ |
386 | | int |
387 | | grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) |
388 | 0 | { |
389 | 0 | struct grid_cell gc; |
390 | 0 | u_int px, py, xx; |
391 | |
|
392 | 0 | xx = gr->cx + 1; |
393 | |
|
394 | 0 | for (py = gr->cy + 1; py > 0; py--) { |
395 | 0 | for (px = xx; px > 0; px--) { |
396 | 0 | grid_get_cell(gr->gd, px - 1, py - 1, &gc); |
397 | 0 | if (grid_reader_cell_equals_data(&gc, jc)) { |
398 | 0 | gr->cx = px - 1; |
399 | 0 | gr->cy = py - 1; |
400 | 0 | return (1); |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | 0 | if (py == 1 || |
405 | 0 | !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) |
406 | 0 | return (0); |
407 | 0 | xx = grid_line_length(gr->gd, py - 2); |
408 | 0 | } |
409 | 0 | return (0); |
410 | 0 | } |
411 | | |
412 | | /* Jump back to the first non-blank character of the line. */ |
413 | | void |
414 | | grid_reader_cursor_back_to_indentation(struct grid_reader *gr) |
415 | 0 | { |
416 | 0 | struct grid_cell gc; |
417 | 0 | u_int px, py, xx, yy, oldx, oldy; |
418 | |
|
419 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
420 | 0 | oldx = gr->cx; |
421 | 0 | oldy = gr->cy; |
422 | 0 | grid_reader_cursor_start_of_line(gr, 1); |
423 | |
|
424 | 0 | for (py = gr->cy; py <= yy; py++) { |
425 | 0 | xx = grid_line_length(gr->gd, py); |
426 | 0 | for (px = 0; px < xx; px++) { |
427 | 0 | grid_get_cell(gr->gd, px, py, &gc); |
428 | 0 | if ((gc.data.size != 1 || *gc.data.data != ' ') && |
429 | 0 | ~gc.flags & GRID_FLAG_TAB && |
430 | 0 | ~gc.flags & GRID_FLAG_PADDING) { |
431 | 0 | gr->cx = px; |
432 | 0 | gr->cy = py; |
433 | 0 | return; |
434 | 0 | } |
435 | 0 | } |
436 | 0 | if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) |
437 | 0 | break; |
438 | 0 | } |
439 | 0 | gr->cx = oldx; |
440 | 0 | gr->cy = oldy; |
441 | 0 | } |