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
)
183 return (grid_in_set(gr
->gd
, gr
->cx
, gr
->cy
, set
));
186 /* Move cursor to the start of the next word. */
188 grid_reader_cursor_next_word(struct grid_reader
*gr
, const char *separators
)
192 /* Do not break up wrapped words. */
193 if (grid_get_line(gr
->gd
, gr
->cy
)->flags
& GRID_LINE_WRAPPED
)
196 xx
= grid_reader_line_length(gr
);
197 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
200 * When navigating via spaces (for example with next-space) separators
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.
209 if (!grid_reader_handle_wrap(gr
, &xx
, &yy
))
211 if (!grid_reader_in_set(gr
, WHITESPACE
)) {
212 if (grid_reader_in_set(gr
, separators
)) {
215 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
216 grid_reader_in_set(gr
, separators
) &&
217 !grid_reader_in_set(gr
, WHITESPACE
));
221 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
222 !(grid_reader_in_set(gr
, separators
) ||
223 grid_reader_in_set(gr
, WHITESPACE
)));
226 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
227 (width
= grid_reader_in_set(gr
, WHITESPACE
)))
231 /* Move cursor to the end of the next word. */
233 grid_reader_cursor_next_word_end(struct grid_reader
*gr
, const char *separators
)
237 /* Do not break up wrapped words. */
238 if (grid_get_line(gr
->gd
, gr
->cy
)->flags
& GRID_LINE_WRAPPED
)
241 xx
= grid_reader_line_length(gr
);
242 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
245 * When navigating via spaces (for example with next-space), separators
246 * should be empty in both modes.
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
255 while (grid_reader_handle_wrap(gr
, &xx
, &yy
)) {
256 if (grid_reader_in_set(gr
, WHITESPACE
))
258 else if (grid_reader_in_set(gr
, separators
)) {
261 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
262 grid_reader_in_set(gr
, separators
) &&
263 !grid_reader_in_set(gr
, WHITESPACE
));
268 while (grid_reader_handle_wrap(gr
, &xx
, &yy
) &&
269 !(grid_reader_in_set(gr
, WHITESPACE
) ||
270 grid_reader_in_set(gr
, separators
)));
276 /* Move to the previous place where a word begins. */
278 grid_reader_cursor_previous_word(struct grid_reader
*gr
, const char *separators
,
279 int already
, int stop_at_eol
)
281 int oldx
, oldy
, at_eol
, word_is_letters
;
283 /* Move back to the previous word character. */
284 if (already
|| grid_reader_in_set(gr
, WHITESPACE
)) {
288 if (!grid_reader_in_set(gr
, WHITESPACE
)) {
290 !grid_reader_in_set(gr
, separators
);
296 grid_reader_cursor_up(gr
);
297 grid_reader_cursor_end_of_line(gr
, 0, 0);
299 /* Stop if separator at EOL. */
300 if (stop_at_eol
&& gr
->cx
> 0) {
303 at_eol
= grid_reader_in_set(gr
,
314 word_is_letters
= !grid_reader_in_set(gr
, separators
);
316 /* Move back to the beginning of this word. */
322 (~grid_get_line(gr
->gd
, gr
->cy
- 1)->flags
&
325 grid_reader_cursor_up(gr
);
326 grid_reader_cursor_end_of_line(gr
, 0, 1);
330 } while (!grid_reader_in_set(gr
, WHITESPACE
) &&
331 word_is_letters
!= grid_reader_in_set(gr
, separators
));
336 /* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
338 grid_reader_cell_equals_data(const struct grid_cell
*gc
,
339 const struct utf8_data
*ud
)
341 if (gc
->flags
& GRID_FLAG_PADDING
)
343 if (gc
->flags
& GRID_FLAG_TAB
&& ud
->size
== 1 && *ud
->data
== '\t')
345 if (gc
->data
.size
!= ud
->size
)
347 return (memcmp(gc
->data
.data
, ud
->data
, gc
->data
.size
) == 0);
350 /* Jump forward to character. */
352 grid_reader_cursor_jump(struct grid_reader
*gr
, const struct utf8_data
*jc
)
355 u_int px
, py
, xx
, yy
;
358 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
360 for (py
= gr
->cy
; py
<= yy
; py
++) {
361 xx
= grid_line_length(gr
->gd
, py
);
363 grid_get_cell(gr
->gd
, px
, py
, &gc
);
364 if (grid_reader_cell_equals_data(&gc
, jc
)) {
373 !(grid_get_line(gr
->gd
, py
)->flags
& GRID_LINE_WRAPPED
))
380 /* Jump back to character. */
382 grid_reader_cursor_jump_back(struct grid_reader
*gr
, const struct utf8_data
*jc
)
389 for (py
= gr
->cy
+ 1; py
> 0; py
--) {
390 for (px
= xx
; px
> 0; px
--) {
391 grid_get_cell(gr
->gd
, px
- 1, py
- 1, &gc
);
392 if (grid_reader_cell_equals_data(&gc
, jc
)) {
400 !(grid_get_line(gr
->gd
, py
- 2)->flags
& GRID_LINE_WRAPPED
))
402 xx
= grid_line_length(gr
->gd
, py
- 2);
407 /* Jump back to the first non-blank character of the line. */
409 grid_reader_cursor_back_to_indentation(struct grid_reader
*gr
)
412 u_int px
, py
, xx
, yy
, oldx
, oldy
;
414 yy
= gr
->gd
->hsize
+ gr
->gd
->sy
- 1;
417 grid_reader_cursor_start_of_line(gr
, 1);
419 for (py
= gr
->cy
; py
<= yy
; py
++) {
420 xx
= grid_line_length(gr
->gd
, py
);
421 for (px
= 0; px
< xx
; px
++) {
422 grid_get_cell(gr
->gd
, px
, py
, &gc
);
423 if ((gc
.data
.size
!= 1 || *gc
.data
.data
!= ' ') &&
424 ~gc
.flags
& GRID_FLAG_TAB
&&
425 ~gc
.flags
& GRID_FLAG_PADDING
) {
431 if (~grid_get_line(gr
->gd
, py
)->flags
& GRID_LINE_WRAPPED
)