xref: /openbsd-src/usr.bin/tmux/grid-reader.c (revision fcde59b201a29a2b4570b00b71e7aa25d61cb5c1)
1 /* $OpenBSD: grid-reader.c,v 1.1 2020/12/22 09:22:14 nicm Exp $ */
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 
21 /* Initialise virtual cursor. */
22 void
23 grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
24 {
25 	gr->gd = gd;
26 	gr->cx = cx;
27 	gr->cy = cy;
28 }
29 
30 /* Get cursor position from reader. */
31 void
32 grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
33 {
34 	*cx = gr->cx;
35 	*cy = gr->cy;
36 }
37 
38 /* Get length of line containing the cursor. */
39 u_int
40 grid_reader_line_length(struct grid_reader *gr)
41 {
42 	return (grid_line_length(gr->gd, gr->cy));
43 }
44 
45 /* Move cursor forward one position. */
46 void
47 grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
48 {
49 	u_int			px;
50 	struct grid_cell	gc;
51 
52 	if (all)
53 		px = gr->gd->sx;
54 	else
55 		px = grid_reader_line_length(gr);
56 
57 	if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
58 		grid_reader_cursor_start_of_line(gr, 0);
59 		grid_reader_cursor_down(gr);
60 	} else if (gr->cx < px) {
61 		gr->cx++;
62 		while (gr->cx < px) {
63 			grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
64 			if (~gc.flags & GRID_FLAG_PADDING)
65 				break;
66 			gr->cx++;
67 		}
68 	}
69 }
70 
71 /* Move cursor back one position. */
72 void
73 grid_reader_cursor_left(struct grid_reader *gr)
74 {
75 	struct grid_cell	gc;
76 
77 	while (gr->cx > 0) {
78 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
79 		if (~gc.flags & GRID_FLAG_PADDING)
80 			break;
81 		gr->cx--;
82 	}
83 	if (gr->cx == 0 && gr->cy > 0) {
84 		grid_reader_cursor_up(gr);
85 		grid_reader_cursor_end_of_line(gr, 0, 0);
86 	} else if (gr->cx > 0)
87 		gr->cx--;
88 }
89 
90 /* Move cursor down one line. */
91 void
92 grid_reader_cursor_down(struct grid_reader *gr)
93 {
94 	struct grid_cell	gc;
95 
96 	if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
97 		gr->cy++;
98 	while (gr->cx > 0) {
99 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
100 		if (~gc.flags & GRID_FLAG_PADDING)
101 			break;
102 		gr->cx--;
103 	}
104 }
105 
106 /* Move cursor up one line. */
107 void
108 grid_reader_cursor_up(struct grid_reader *gr)
109 {
110 	struct grid_cell	gc;
111 
112 	if (gr->cy > 0)
113 		gr->cy--;
114 	while (gr->cx > 0) {
115 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
116 		if (~gc.flags & GRID_FLAG_PADDING)
117 			break;
118 		gr->cx--;
119 	}
120 }
121 
122 /* Move cursor to the start of the line. */
123 void
124 grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
125 {
126 	if (wrap) {
127 		while (gr->cy > 0 &&
128 		    grid_get_line(gr->gd, gr->cy - 1)->flags &
129 		        GRID_LINE_WRAPPED)
130 			gr->cy--;
131 	}
132 	gr->cx = 0;
133 }
134 
135 /* Move cursor to the end of the line. */
136 void
137 grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
138 {
139 	u_int	yy;
140 
141 	if (wrap) {
142 		yy = gr->gd->hsize + gr->gd->sy - 1;
143 		while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
144 		    GRID_LINE_WRAPPED)
145 			gr->cy++;
146 	}
147 	if (all)
148 		gr->cx = gr->gd->sx;
149 	else
150 		gr->cx = grid_reader_line_length(gr);
151 }
152 
153 /* Check if character under cursor is in set. */
154 int
155 grid_reader_in_set(struct grid_reader *gr, const char *set)
156 {
157 	struct grid_cell	gc;
158 
159 	grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
160 	if (gc.flags & GRID_FLAG_PADDING)
161 		return (0);
162 	return (utf8_cstrhas(set, &gc.data));
163 }
164 
165 /* Move cursor to the start of the next word. */
166 void
167 grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
168 {
169 	u_int	xx, yy;
170 	int expected = 0;
171 
172 	/* Do not break up wrapped words. */
173 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
174 		xx = grid_reader_line_length(gr) - 1;
175 	else
176 		xx = grid_reader_line_length(gr);
177 	yy = gr->gd->hsize + gr->gd->sy - 1;
178 
179 	/*
180 	 * If we started inside a word, skip over word characters. Then skip
181 	 * over separators till the next word.
182 	 *
183 	 * expected is initially set to 0 for the former and then 1 for the
184 	 * latter. It is finally set to 0 when the beginning of the next word is
185 	 * found.
186 	 */
187 	do {
188 		while (gr->cx > xx ||
189 		    grid_reader_in_set(gr, separators) == expected) {
190 			/* Move down if we are past the end of the line. */
191 			if (gr->cx > xx) {
192 				if (gr->cy == yy)
193 					return;
194 				grid_reader_cursor_start_of_line(gr, 0);
195 				grid_reader_cursor_down(gr);
196 
197 				if (grid_get_line(gr->gd, gr->cy)->flags &
198 				    GRID_LINE_WRAPPED)
199 					xx = grid_reader_line_length(gr) - 1;
200 				else
201 					xx = grid_reader_line_length(gr);
202 			} else
203 				gr->cx++;
204 		}
205 		expected = !expected;
206 	} while (expected == 1);
207 }
208 
209 /* Move cursor to the end of the next word. */
210 void
211 grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
212 {
213 	u_int	xx, yy;
214 	int	expected = 1;
215 
216 	/* Do not break up wrapped words. */
217 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
218 		xx = grid_reader_line_length(gr) - 1;
219 	else
220 		xx = grid_reader_line_length(gr);
221 	yy = gr->gd->hsize + gr->gd->sy - 1;
222 
223 	/*
224 	 * If we started on a separator, skip over separators. Then skip over
225 	 * word characters till the next separator.
226 	 *
227 	 * expected is initially set to 1 for the former and then 1 for the
228 	 * latter. It is finally set to 1 when the end of the next word is
229 	 * found.
230 	 */
231 	do {
232 		while (gr->cx > xx ||
233 		    grid_reader_in_set(gr, separators) == expected) {
234 			/* Move down if we are past the end of the line. */
235 			if (gr->cx > xx) {
236 				if (gr->cy == yy)
237 					return;
238 				grid_reader_cursor_start_of_line(gr, 0);
239 				grid_reader_cursor_down(gr);
240 
241 				if (grid_get_line(gr->gd, gr->cy)->flags &
242 				    GRID_LINE_WRAPPED)
243 					xx = grid_reader_line_length(gr) - 1;
244 				else
245 					xx = grid_reader_line_length(gr);
246 			} else
247 				gr->cx++;
248 		}
249 		expected = !expected;
250 	} while (expected == 0);
251 }
252 
253 /* Move to the previous place where a word begins. */
254 void
255 grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
256     int already)
257 {
258 	int	oldx, oldy, r;
259 
260 	/* Move back to the previous word character. */
261 	if (already || grid_reader_in_set(gr, separators)) {
262 		for (;;) {
263 			if (gr->cx > 0) {
264 				gr->cx--;
265 				if (!grid_reader_in_set(gr, separators))
266 					break;
267 			} else {
268 				if (gr->cy == 0)
269 					return;
270 				grid_reader_cursor_up(gr);
271 				grid_reader_cursor_end_of_line(gr, 0, 0);
272 
273 				/* Stop if separator at EOL. */
274 				if (gr->cx > 0) {
275 					oldx = gr->cx;
276 					gr->cx--;
277 					r = grid_reader_in_set(gr, separators);
278 					gr->cx = oldx;
279 					if (r)
280 						break;
281 				}
282 			}
283 		}
284 	}
285 
286 	/* Move back to the beginning of this word. */
287 	do {
288 		oldx = gr->cx;
289 		oldy = gr->cy;
290 		if (gr->cx == 0) {
291 			if (gr->cy == 0 ||
292 			  ~grid_get_line(gr->gd, gr->cy - 1)->flags &
293 			  GRID_LINE_WRAPPED)
294 				break;
295 			grid_reader_cursor_up(gr);
296 			grid_reader_cursor_end_of_line(gr, 0, 0);
297 		}
298 		if (gr->cx > 0)
299 			gr->cx--;
300 	} while (!grid_reader_in_set(gr, separators));
301 	gr->cx = oldx;
302 	gr->cy = oldy;
303 }
304