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) |
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 |
56 | 0 | px = grid_reader_line_length(gr); |
57 | |
|
58 | 0 | if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) { |
59 | 0 | grid_reader_cursor_start_of_line(gr, 0); |
60 | 0 | grid_reader_cursor_down(gr); |
61 | 0 | } else if (gr->cx < px) { |
62 | 0 | gr->cx++; |
63 | 0 | while (gr->cx < px) { |
64 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
65 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
66 | 0 | break; |
67 | 0 | gr->cx++; |
68 | 0 | } |
69 | 0 | } |
70 | 0 | } |
71 | | |
72 | | /* Move cursor back one position. */ |
73 | | void |
74 | | grid_reader_cursor_left(struct grid_reader *gr, int wrap) |
75 | 0 | { |
76 | 0 | struct grid_cell gc; |
77 | |
|
78 | 0 | while (gr->cx > 0) { |
79 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
80 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
81 | 0 | break; |
82 | 0 | gr->cx--; |
83 | 0 | } |
84 | 0 | if (gr->cx == 0 && gr->cy > 0 && |
85 | 0 | (wrap || |
86 | 0 | grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) { |
87 | 0 | grid_reader_cursor_up(gr); |
88 | 0 | grid_reader_cursor_end_of_line(gr, 0, 0); |
89 | 0 | } else if (gr->cx > 0) |
90 | 0 | gr->cx--; |
91 | 0 | } |
92 | | |
93 | | /* Move cursor down one line. */ |
94 | | void |
95 | | grid_reader_cursor_down(struct grid_reader *gr) |
96 | 0 | { |
97 | 0 | struct grid_cell gc; |
98 | |
|
99 | 0 | if (gr->cy < gr->gd->hsize + gr->gd->sy - 1) |
100 | 0 | gr->cy++; |
101 | 0 | while (gr->cx > 0) { |
102 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
103 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
104 | 0 | break; |
105 | 0 | gr->cx--; |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | /* Move cursor up one line. */ |
110 | | void |
111 | | grid_reader_cursor_up(struct grid_reader *gr) |
112 | 0 | { |
113 | 0 | struct grid_cell gc; |
114 | |
|
115 | 0 | if (gr->cy > 0) |
116 | 0 | gr->cy--; |
117 | 0 | while (gr->cx > 0) { |
118 | 0 | grid_get_cell(gr->gd, gr->cx, gr->cy, &gc); |
119 | 0 | if (~gc.flags & GRID_FLAG_PADDING) |
120 | 0 | break; |
121 | 0 | gr->cx--; |
122 | 0 | } |
123 | 0 | } |
124 | | |
125 | | /* Move cursor to the start of the line. */ |
126 | | void |
127 | | grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap) |
128 | 0 | { |
129 | 0 | if (wrap) { |
130 | 0 | while (gr->cy > 0 && |
131 | 0 | grid_get_line(gr->gd, gr->cy - 1)->flags & |
132 | 0 | GRID_LINE_WRAPPED) |
133 | 0 | gr->cy--; |
134 | 0 | } |
135 | 0 | gr->cx = 0; |
136 | 0 | } |
137 | | |
138 | | /* Move cursor to the end of the line. */ |
139 | | void |
140 | | grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) |
141 | 0 | { |
142 | 0 | u_int yy; |
143 | |
|
144 | 0 | if (wrap) { |
145 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
146 | 0 | while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags & |
147 | 0 | GRID_LINE_WRAPPED) |
148 | 0 | gr->cy++; |
149 | 0 | } |
150 | 0 | if (all) |
151 | 0 | gr->cx = gr->gd->sx; |
152 | 0 | else |
153 | 0 | gr->cx = grid_reader_line_length(gr); |
154 | 0 | } |
155 | | |
156 | | /* Handle line wrapping while moving the cursor. */ |
157 | | static int |
158 | | grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy) |
159 | 0 | { |
160 | | /* |
161 | | * Make sure the cursor lies within the grid reader's bounding area, |
162 | | * wrapping to the next line as necessary. Return zero if the cursor |
163 | | * would wrap past the bottom of the grid. |
164 | | */ |
165 | 0 | while (gr->cx > *xx) { |
166 | 0 | if (gr->cy == *yy) |
167 | 0 | return (0); |
168 | 0 | grid_reader_cursor_start_of_line(gr, 0); |
169 | 0 | grid_reader_cursor_down(gr); |
170 | |
|
171 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
172 | 0 | *xx = gr->gd->sx - 1; |
173 | 0 | else |
174 | 0 | *xx = grid_reader_line_length(gr); |
175 | 0 | } |
176 | 0 | return (1); |
177 | 0 | } |
178 | | |
179 | | /* Check if character under cursor is in set. */ |
180 | | int |
181 | | grid_reader_in_set(struct grid_reader *gr, const char *set) |
182 | 0 | { |
183 | 0 | return (grid_in_set(gr->gd, gr->cx, gr->cy, set)); |
184 | 0 | } |
185 | | |
186 | | /* Move cursor to the start of the next word. */ |
187 | | void |
188 | | grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) |
189 | 0 | { |
190 | 0 | u_int xx, yy, width; |
191 | | |
192 | | /* Do not break up wrapped words. */ |
193 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
194 | 0 | xx = gr->gd->sx - 1; |
195 | 0 | else |
196 | 0 | xx = grid_reader_line_length(gr); |
197 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
198 | | |
199 | | /* |
200 | | * When navigating via spaces (for example with next-space) separators |
201 | | * should be empty. |
202 | | * |
203 | | * If we started on a separator that is not whitespace, skip over |
204 | | * subsequent separators that are not whitespace. Otherwise, if we |
205 | | * started on a non-whitespace character, skip over subsequent |
206 | | * characters that are neither whitespace nor separators. Then, skip |
207 | | * over whitespace (if any) until the next non-whitespace character. |
208 | | */ |
209 | 0 | if (!grid_reader_handle_wrap(gr, &xx, &yy)) |
210 | 0 | return; |
211 | 0 | if (!grid_reader_in_set(gr, WHITESPACE)) { |
212 | 0 | if (grid_reader_in_set(gr, separators)) { |
213 | 0 | do |
214 | 0 | gr->cx++; |
215 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
216 | 0 | grid_reader_in_set(gr, separators) && |
217 | 0 | !grid_reader_in_set(gr, WHITESPACE)); |
218 | 0 | } else { |
219 | 0 | do |
220 | 0 | gr->cx++; |
221 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
222 | 0 | !(grid_reader_in_set(gr, separators) || |
223 | 0 | grid_reader_in_set(gr, WHITESPACE))); |
224 | 0 | } |
225 | 0 | } |
226 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
227 | 0 | (width = grid_reader_in_set(gr, WHITESPACE))) |
228 | 0 | gr->cx += width; |
229 | 0 | } |
230 | | |
231 | | /* Move cursor to the end of the next word. */ |
232 | | void |
233 | | grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) |
234 | 0 | { |
235 | 0 | u_int xx, yy; |
236 | | |
237 | | /* Do not break up wrapped words. */ |
238 | 0 | if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) |
239 | 0 | xx = gr->gd->sx - 1; |
240 | 0 | else |
241 | 0 | xx = grid_reader_line_length(gr); |
242 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
243 | | |
244 | | /* |
245 | | * When navigating via spaces (for example with next-space), separators |
246 | | * should be empty in both modes. |
247 | | * |
248 | | * If we started on a whitespace, move until reaching the first |
249 | | * non-whitespace character. If that character is a separator, treat |
250 | | * subsequent separators as a word, and continue moving until the first |
251 | | * non-separator. Otherwise, continue moving until the first separator |
252 | | * or whitespace. |
253 | | */ |
254 | |
|
255 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy)) { |
256 | 0 | if (grid_reader_in_set(gr, WHITESPACE)) |
257 | 0 | gr->cx++; |
258 | 0 | else if (grid_reader_in_set(gr, separators)) { |
259 | 0 | do |
260 | 0 | gr->cx++; |
261 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
262 | 0 | grid_reader_in_set(gr, separators) && |
263 | 0 | !grid_reader_in_set(gr, WHITESPACE)); |
264 | 0 | return; |
265 | 0 | } else { |
266 | 0 | do |
267 | 0 | gr->cx++; |
268 | 0 | while (grid_reader_handle_wrap(gr, &xx, &yy) && |
269 | 0 | !(grid_reader_in_set(gr, WHITESPACE) || |
270 | 0 | grid_reader_in_set(gr, separators))); |
271 | 0 | return; |
272 | 0 | } |
273 | 0 | } |
274 | 0 | } |
275 | | |
276 | | /* Move to the previous place where a word begins. */ |
277 | | void |
278 | | grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, |
279 | | int already, int stop_at_eol) |
280 | 0 | { |
281 | 0 | int oldx, oldy, at_eol, word_is_letters; |
282 | | |
283 | | /* Move back to the previous word character. */ |
284 | 0 | if (already || grid_reader_in_set(gr, WHITESPACE)) { |
285 | 0 | for (;;) { |
286 | 0 | if (gr->cx > 0) { |
287 | 0 | gr->cx--; |
288 | 0 | if (!grid_reader_in_set(gr, WHITESPACE)) { |
289 | 0 | word_is_letters = |
290 | 0 | !grid_reader_in_set(gr, separators); |
291 | 0 | break; |
292 | 0 | } |
293 | 0 | } else { |
294 | 0 | if (gr->cy == 0) |
295 | 0 | return; |
296 | 0 | grid_reader_cursor_up(gr); |
297 | 0 | grid_reader_cursor_end_of_line(gr, 0, 0); |
298 | | |
299 | | /* Stop if separator at EOL. */ |
300 | 0 | if (stop_at_eol && gr->cx > 0) { |
301 | 0 | oldx = gr->cx; |
302 | 0 | gr->cx--; |
303 | 0 | at_eol = grid_reader_in_set(gr, |
304 | 0 | WHITESPACE); |
305 | 0 | gr->cx = oldx; |
306 | 0 | if (at_eol) { |
307 | 0 | word_is_letters = 0; |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } else |
314 | 0 | word_is_letters = !grid_reader_in_set(gr, separators); |
315 | | |
316 | | /* Move back to the beginning of this word. */ |
317 | 0 | do { |
318 | 0 | oldx = gr->cx; |
319 | 0 | oldy = gr->cy; |
320 | 0 | if (gr->cx == 0) { |
321 | 0 | if (gr->cy == 0 || |
322 | 0 | (~grid_get_line(gr->gd, gr->cy - 1)->flags & |
323 | 0 | GRID_LINE_WRAPPED)) |
324 | 0 | break; |
325 | 0 | grid_reader_cursor_up(gr); |
326 | 0 | grid_reader_cursor_end_of_line(gr, 0, 1); |
327 | 0 | } |
328 | 0 | if (gr->cx > 0) |
329 | 0 | gr->cx--; |
330 | 0 | } while (!grid_reader_in_set(gr, WHITESPACE) && |
331 | 0 | word_is_letters != grid_reader_in_set(gr, separators)); |
332 | 0 | gr->cx = oldx; |
333 | 0 | gr->cy = oldy; |
334 | 0 | } |
335 | | |
336 | | /* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */ |
337 | | static int |
338 | | grid_reader_cell_equals_data(const struct grid_cell *gc, |
339 | | const struct utf8_data *ud) |
340 | 0 | { |
341 | 0 | if (gc->flags & GRID_FLAG_PADDING) |
342 | 0 | return (0); |
343 | 0 | if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t') |
344 | 0 | return (1); |
345 | 0 | if (gc->data.size != ud->size) |
346 | 0 | return (0); |
347 | 0 | return (memcmp(gc->data.data, ud->data, gc->data.size) == 0); |
348 | 0 | } |
349 | | |
350 | | /* Jump forward to character. */ |
351 | | int |
352 | | grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) |
353 | 0 | { |
354 | 0 | struct grid_cell gc; |
355 | 0 | u_int px, py, xx, yy; |
356 | |
|
357 | 0 | px = gr->cx; |
358 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
359 | |
|
360 | 0 | for (py = gr->cy; py <= yy; py++) { |
361 | 0 | xx = grid_line_length(gr->gd, py); |
362 | 0 | while (px < xx) { |
363 | 0 | grid_get_cell(gr->gd, px, py, &gc); |
364 | 0 | if (grid_reader_cell_equals_data(&gc, jc)) { |
365 | 0 | gr->cx = px; |
366 | 0 | gr->cy = py; |
367 | 0 | return (1); |
368 | 0 | } |
369 | 0 | px++; |
370 | 0 | } |
371 | | |
372 | 0 | if (py == yy || |
373 | 0 | !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) |
374 | 0 | return (0); |
375 | 0 | px = 0; |
376 | 0 | } |
377 | 0 | return (0); |
378 | 0 | } |
379 | | |
380 | | /* Jump back to character. */ |
381 | | int |
382 | | grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) |
383 | 0 | { |
384 | 0 | struct grid_cell gc; |
385 | 0 | u_int px, py, xx; |
386 | |
|
387 | 0 | xx = gr->cx + 1; |
388 | |
|
389 | 0 | for (py = gr->cy + 1; py > 0; py--) { |
390 | 0 | for (px = xx; px > 0; px--) { |
391 | 0 | grid_get_cell(gr->gd, px - 1, py - 1, &gc); |
392 | 0 | if (grid_reader_cell_equals_data(&gc, jc)) { |
393 | 0 | gr->cx = px - 1; |
394 | 0 | gr->cy = py - 1; |
395 | 0 | return (1); |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | 0 | if (py == 1 || |
400 | 0 | !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) |
401 | 0 | return (0); |
402 | 0 | xx = grid_line_length(gr->gd, py - 2); |
403 | 0 | } |
404 | 0 | return (0); |
405 | 0 | } |
406 | | |
407 | | /* Jump back to the first non-blank character of the line. */ |
408 | | void |
409 | | grid_reader_cursor_back_to_indentation(struct grid_reader *gr) |
410 | 0 | { |
411 | 0 | struct grid_cell gc; |
412 | 0 | u_int px, py, xx, yy, oldx, oldy; |
413 | |
|
414 | 0 | yy = gr->gd->hsize + gr->gd->sy - 1; |
415 | 0 | oldx = gr->cx; |
416 | 0 | oldy = gr->cy; |
417 | 0 | grid_reader_cursor_start_of_line(gr, 1); |
418 | |
|
419 | 0 | for (py = gr->cy; py <= yy; py++) { |
420 | 0 | xx = grid_line_length(gr->gd, py); |
421 | 0 | for (px = 0; px < xx; px++) { |
422 | 0 | grid_get_cell(gr->gd, px, py, &gc); |
423 | 0 | if ((gc.data.size != 1 || *gc.data.data != ' ') && |
424 | 0 | ~gc.flags & GRID_FLAG_TAB && |
425 | 0 | ~gc.flags & GRID_FLAG_PADDING) { |
426 | 0 | gr->cx = px; |
427 | 0 | gr->cy = py; |
428 | 0 | return; |
429 | 0 | } |
430 | 0 | } |
431 | 0 | if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) |
432 | 0 | break; |
433 | 0 | } |
434 | 0 | gr->cx = oldx; |
435 | 0 | gr->cy = oldy; |
436 | 0 | } |