4 * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
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.
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.
22 /* Initialise virtual cursor. */
24 grid_reader_start(struct grid_reader
*gr
, struct grid
*gd
, u_int cx
, u_int cy
)
31 /* Get cursor position from reader. */
33 grid_reader_get_cursor(struct grid_reader
*gr
, u_int
*cx
, u_int
*cy
)
39 /* Get length of line containing the cursor. */
41 grid_reader_line_length(struct grid_reader
*gr
)
43 return (grid_line_length(gr
->gd
, gr
->cy
));
46 /* Move cursor forward one position. */
48 grid_reader_cursor_right(struct grid_reader
*gr
, int wrap
, int all
)
56 px
= grid_reader_line_length(gr
);
58 if (wrap
&& gr
->cx
>= px
&& gr
->cy
< gr
->gd
->hsize
+ gr
->gd
->sy
- 1) {
59 grid_reader_cursor_start_of_line(gr
, 0);
60 grid_reader_cursor_down(gr
);
61 } else if (gr
->cx
< px
) {
64 grid_get_cell(gr
->gd
, gr
->cx
, gr
->cy
, &gc
);
65 if (~gc
.flags
& GRID_FLAG_PADDING
)
72 /* Move cursor back one position. */
74 grid_reader_cursor_left(struct grid_reader
*gr
, int wrap
)
79 grid_get_cell(gr
->gd
, gr
->cx
, gr
->cy
, &gc
);
80 if (~gc
.flags
& GRID_FLAG_PADDING
)
84 if (gr
->cx
== 0 && gr
->cy
> 0 &&
86 grid_get_line(gr
->gd
, gr
->cy
- 1)->flags
& GRID_LINE_WRAPPED
)) {
87 grid_reader_cursor_up(gr
);
88 grid_reader_cursor_end_of_line(gr
, 0, 0);
89 } else if (gr
->cx
> 0)
93 /* Move cursor down one line. */
95 grid_reader_cursor_down(struct grid_reader
*gr
)
99 if (gr
->cy
< gr
->gd
->hsize
+ gr
->gd
->sy
- 1)
102 grid_get_cell(gr
->gd
, gr
->cx
, gr
->cy
, &gc
);
103 if (~gc
.flags
& GRID_FLAG_PADDING
)
109 /* Move cursor up one line. */
111 grid_reader_cursor_up(struct grid_reader
*gr
)
118 grid_get_cell(gr
->gd
, gr
->cx
, gr
->cy
, &gc
);
119 if (~gc
.flags
& GRID_FLAG_PADDING
)
125 /* Move cursor to the start of the line. */
127 grid_reader_cursor_start_of_line(struct grid_reader
*gr
, int wrap
)
131 grid_get_line(gr
->gd
, gr
->cy
- 1)->flags
&
138 /* Move cursor to the end of the line. */
140 grid_reader_cursor_end_of_line(struct grid_reader
*gr
, int wrap
, int all
)
145 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
146 while (gr
->cy
< yy
&& grid_get_line(gr
->gd
, gr
->cy
)->flags
&
153 gr
->cx
= grid_reader_line_length(gr
);
156 /* Handle line wrapping while moving the cursor. */
158 grid_reader_handle_wrap(struct grid_reader
*gr
, u_int
*xx
, u_int
*yy
)
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.
165 while (gr
->cx
> *xx
) {
168 grid_reader_cursor_start_of_line(gr
, 0);
169 grid_reader_cursor_down(gr
);
171 if (grid_get_line(gr
->gd
, gr
->cy
)->flags
& GRID_LINE_WRAPPED
)
172 *xx
= gr
->gd
->sx
- 1;
174 *xx
= grid_reader_line_length(gr
);
179 /* Check if character under cursor is in set. */
181 grid_reader_in_set(struct grid_reader
*gr
, const char *set
)
185 grid_get_cell(gr
->gd
, gr
->cx
, gr
->cy
, &gc
);
186 if (gc
.flags
& GRID_FLAG_PADDING
)
188 return (utf8_cstrhas(set
, &gc
.data
));
191 /* Move cursor to the start of the next word. */
193 grid_reader_cursor_next_word(struct grid_reader
*gr
, const char *separators
)
197 /* Do not break up wrapped words. */
198 if (grid_get_line(gr
->gd
, gr
->cy
)->flags
& GRID_LINE_WRAPPED
)
201 xx
= grid_reader_line_length(gr
);
202 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
205 * When navigating via spaces (for example with next-space) separators
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.
214 if (!grid_reader_handle_wrap(gr
, &xx
, &yy
))
216 if (!grid_reader_in_set(gr
, WHITESPACE
)) {
217 if (grid_reader_in_set(gr
, separators
)) {
220 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
221 grid_reader_in_set(gr
, separators
) &&
222 !grid_reader_in_set(gr
, WHITESPACE
));
226 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
227 !(grid_reader_in_set(gr
, separators
) ||
228 grid_reader_in_set(gr
, WHITESPACE
)));
231 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
232 grid_reader_in_set(gr
, WHITESPACE
))
236 /* Move cursor to the end of the next word. */
238 grid_reader_cursor_next_word_end(struct grid_reader
*gr
, const char *separators
)
242 /* Do not break up wrapped words. */
243 if (grid_get_line(gr
->gd
, gr
->cy
)->flags
& GRID_LINE_WRAPPED
)
246 xx
= grid_reader_line_length(gr
);
247 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
250 * When navigating via spaces (for example with next-space), separators
251 * should be empty in both modes.
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
260 while (grid_reader_handle_wrap(gr
, &xx
, &yy
)) {
261 if (grid_reader_in_set(gr
, WHITESPACE
))
263 else if (grid_reader_in_set(gr
, separators
)) {
266 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
267 grid_reader_in_set(gr
, separators
) &&
268 !grid_reader_in_set(gr
, WHITESPACE
));
273 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
274 !(grid_reader_in_set(gr
, WHITESPACE
) ||
275 grid_reader_in_set(gr
, separators
)));
281 /* Move to the previous place where a word begins. */
283 grid_reader_cursor_previous_word(struct grid_reader
*gr
, const char *separators
,
284 int already
, int stop_at_eol
)
286 int oldx
, oldy
, at_eol
, word_is_letters
;
288 /* Move back to the previous word character. */
289 if (already
|| grid_reader_in_set(gr
, WHITESPACE
)) {
293 if (!grid_reader_in_set(gr
, WHITESPACE
)) {
295 !grid_reader_in_set(gr
, separators
);
301 grid_reader_cursor_up(gr
);
302 grid_reader_cursor_end_of_line(gr
, 0, 0);
304 /* Stop if separator at EOL. */
305 if (stop_at_eol
&& gr
->cx
> 0) {
308 at_eol
= grid_reader_in_set(gr
,
319 word_is_letters
= !grid_reader_in_set(gr
, separators
);
321 /* Move back to the beginning of this word. */
327 (~grid_get_line(gr
->gd
, gr
->cy
- 1)->flags
&
330 grid_reader_cursor_up(gr
);
331 grid_reader_cursor_end_of_line(gr
, 0, 1);
335 } while (!grid_reader_in_set(gr
, WHITESPACE
) &&
336 word_is_letters
!= grid_reader_in_set(gr
, separators
));
341 /* Jump forward to character. */
343 grid_reader_cursor_jump(struct grid_reader
*gr
, const struct utf8_data
*jc
)
346 u_int px
, py
, xx
, yy
;
349 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
351 for (py
= gr
->cy
; py
<= yy
; py
++) {
352 xx
= grid_line_length(gr
->gd
, py
);
354 grid_get_cell(gr
->gd
, px
, py
, &gc
);
355 if (!(gc
.flags
& GRID_FLAG_PADDING
) &&
356 gc
.data
.size
== jc
->size
&&
357 memcmp(gc
.data
.data
, jc
->data
, gc
.data
.size
) == 0) {
366 !(grid_get_line(gr
->gd
, py
)->flags
& GRID_LINE_WRAPPED
))
373 /* Jump back to character. */
375 grid_reader_cursor_jump_back(struct grid_reader
*gr
, const struct utf8_data
*jc
)
382 for (py
= gr
->cy
+ 1; py
> 0; py
--) {
383 for (px
= xx
; px
> 0; px
--) {
384 grid_get_cell(gr
->gd
, px
- 1, py
- 1, &gc
);
385 if (!(gc
.flags
& GRID_FLAG_PADDING
) &&
386 gc
.data
.size
== jc
->size
&&
387 memcmp(gc
.data
.data
, jc
->data
, gc
.data
.size
) == 0) {
395 !(grid_get_line(gr
->gd
, py
- 2)->flags
& GRID_LINE_WRAPPED
))
397 xx
= grid_line_length(gr
->gd
, py
- 2);
402 /* Jump back to the first non-blank character of the line. */
404 grid_reader_cursor_back_to_indentation(struct grid_reader
*gr
)
407 u_int px
, py
, xx
, yy
, oldx
, oldy
;
409 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
412 grid_reader_cursor_start_of_line(gr
, 1);
414 for (py
= gr
->cy
; py
<= yy
; py
++) {
415 xx
= grid_line_length(gr
->gd
, py
);
416 for (px
= 0; px
< xx
; px
++) {
417 grid_get_cell(gr
->gd
, px
, py
, &gc
);
418 if (gc
.data
.size
!= 1 || *gc
.data
.data
!= ' ') {
424 if (~grid_get_line(gr
->gd
, py
)->flags
& GRID_LINE_WRAPPED
)