Annotation of src/usr.bin/tmux/grid-reader.c, Revision 1.5
1.5 ! nicm 1: /* $OpenBSD: grid-reader.c,v 1.4 2021/04/05 08:43:48 nicm Exp $ */
1.1 nicm 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"
1.2 nicm 20: #include <string.h>
1.1 nicm 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: {
26: gr->gd = gd;
27: gr->cx = cx;
28: gr->cy = cy;
29: }
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: {
35: *cx = gr->cx;
36: *cy = gr->cy;
37: }
38:
39: /* Get length of line containing the cursor. */
40: u_int
41: grid_reader_line_length(struct grid_reader *gr)
42: {
43: return (grid_line_length(gr->gd, gr->cy));
44: }
45:
46: /* Move cursor forward one position. */
47: void
48: grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
49: {
50: u_int px;
51: struct grid_cell gc;
52:
53: if (all)
54: px = gr->gd->sx;
55: else
56: px = grid_reader_line_length(gr);
57:
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) {
62: gr->cx++;
63: while (gr->cx < px) {
64: grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
65: if (~gc.flags & GRID_FLAG_PADDING)
66: break;
67: gr->cx++;
68: }
69: }
70: }
71:
72: /* Move cursor back one position. */
73: void
1.4 nicm 74: grid_reader_cursor_left(struct grid_reader *gr, int wrap)
1.1 nicm 75: {
76: struct grid_cell gc;
77:
78: while (gr->cx > 0) {
79: grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
80: if (~gc.flags & GRID_FLAG_PADDING)
81: break;
82: gr->cx--;
83: }
1.4 nicm 84: if (gr->cx == 0 && gr->cy > 0 &&
85: (wrap ||
86: grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
1.1 nicm 87: grid_reader_cursor_up(gr);
88: grid_reader_cursor_end_of_line(gr, 0, 0);
89: } else if (gr->cx > 0)
90: gr->cx--;
91: }
92:
93: /* Move cursor down one line. */
94: void
95: grid_reader_cursor_down(struct grid_reader *gr)
96: {
97: struct grid_cell gc;
98:
99: if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
100: gr->cy++;
101: while (gr->cx > 0) {
102: grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
103: if (~gc.flags & GRID_FLAG_PADDING)
104: break;
105: gr->cx--;
106: }
107: }
108:
109: /* Move cursor up one line. */
110: void
111: grid_reader_cursor_up(struct grid_reader *gr)
112: {
113: struct grid_cell gc;
114:
115: if (gr->cy > 0)
116: gr->cy--;
117: while (gr->cx > 0) {
118: grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
119: if (~gc.flags & GRID_FLAG_PADDING)
120: break;
121: gr->cx--;
122: }
123: }
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: {
129: if (wrap) {
130: while (gr->cy > 0 &&
131: grid_get_line(gr->gd, gr->cy - 1)->flags &
132: GRID_LINE_WRAPPED)
133: gr->cy--;
134: }
135: gr->cx = 0;
136: }
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: {
142: u_int yy;
143:
144: if (wrap) {
145: yy = gr->gd->hsize + gr->gd->sy - 1;
146: while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
147: GRID_LINE_WRAPPED)
148: gr->cy++;
149: }
150: if (all)
151: gr->cx = gr->gd->sx;
152: else
153: gr->cx = grid_reader_line_length(gr);
154: }
155:
156: /* Check if character under cursor is in set. */
157: int
158: grid_reader_in_set(struct grid_reader *gr, const char *set)
159: {
160: struct grid_cell gc;
161:
162: grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
163: if (gc.flags & GRID_FLAG_PADDING)
164: return (0);
165: return (utf8_cstrhas(set, &gc.data));
166: }
167:
168: /* Move cursor to the start of the next word. */
169: void
170: grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
171: {
172: u_int xx, yy;
173: int expected = 0;
174:
175: /* Do not break up wrapped words. */
176: if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
1.3 nicm 177: xx = gr->gd->sx - 1;
1.1 nicm 178: else
179: xx = grid_reader_line_length(gr);
180: yy = gr->gd->hsize + gr->gd->sy - 1;
181:
182: /*
183: * If we started inside a word, skip over word characters. Then skip
184: * over separators till the next word.
185: *
186: * expected is initially set to 0 for the former and then 1 for the
187: * latter. It is finally set to 0 when the beginning of the next word is
188: * found.
189: */
190: do {
191: while (gr->cx > xx ||
192: grid_reader_in_set(gr, separators) == expected) {
193: /* Move down if we are past the end of the line. */
194: if (gr->cx > xx) {
195: if (gr->cy == yy)
196: return;
197: grid_reader_cursor_start_of_line(gr, 0);
198: grid_reader_cursor_down(gr);
199:
200: if (grid_get_line(gr->gd, gr->cy)->flags &
201: GRID_LINE_WRAPPED)
1.3 nicm 202: xx = gr->gd->sx - 1;
1.1 nicm 203: else
204: xx = grid_reader_line_length(gr);
205: } else
206: gr->cx++;
207: }
208: expected = !expected;
209: } while (expected == 1);
210: }
211:
212: /* Move cursor to the end of the next word. */
213: void
214: grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
215: {
216: u_int xx, yy;
217: int expected = 1;
218:
219: /* Do not break up wrapped words. */
220: if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
1.3 nicm 221: xx = gr->gd->sx - 1;
1.1 nicm 222: else
223: xx = grid_reader_line_length(gr);
224: yy = gr->gd->hsize + gr->gd->sy - 1;
225:
226: /*
227: * If we started on a separator, skip over separators. Then skip over
228: * word characters till the next separator.
229: *
230: * expected is initially set to 1 for the former and then 1 for the
231: * latter. It is finally set to 1 when the end of the next word is
232: * found.
233: */
234: do {
235: while (gr->cx > xx ||
236: grid_reader_in_set(gr, separators) == expected) {
237: /* Move down if we are past the end of the line. */
238: if (gr->cx > xx) {
239: if (gr->cy == yy)
240: return;
241: grid_reader_cursor_start_of_line(gr, 0);
242: grid_reader_cursor_down(gr);
243:
244: if (grid_get_line(gr->gd, gr->cy)->flags &
245: GRID_LINE_WRAPPED)
1.3 nicm 246: xx = gr->gd->sx - 1;
1.1 nicm 247: else
248: xx = grid_reader_line_length(gr);
249: } else
250: gr->cx++;
251: }
252: expected = !expected;
253: } while (expected == 0);
254: }
255:
256: /* Move to the previous place where a word begins. */
257: void
258: grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
259: int already)
260: {
261: int oldx, oldy, r;
262:
263: /* Move back to the previous word character. */
264: if (already || grid_reader_in_set(gr, separators)) {
265: for (;;) {
266: if (gr->cx > 0) {
267: gr->cx--;
268: if (!grid_reader_in_set(gr, separators))
269: break;
270: } else {
271: if (gr->cy == 0)
272: return;
273: grid_reader_cursor_up(gr);
274: grid_reader_cursor_end_of_line(gr, 0, 0);
275:
276: /* Stop if separator at EOL. */
277: if (gr->cx > 0) {
278: oldx = gr->cx;
279: gr->cx--;
280: r = grid_reader_in_set(gr, separators);
281: gr->cx = oldx;
282: if (r)
283: break;
284: }
285: }
286: }
287: }
288:
289: /* Move back to the beginning of this word. */
290: do {
291: oldx = gr->cx;
292: oldy = gr->cy;
293: if (gr->cx == 0) {
294: if (gr->cy == 0 ||
295: ~grid_get_line(gr->gd, gr->cy - 1)->flags &
296: GRID_LINE_WRAPPED)
297: break;
298: grid_reader_cursor_up(gr);
1.3 nicm 299: grid_reader_cursor_end_of_line(gr, 0, 1);
1.1 nicm 300: }
301: if (gr->cx > 0)
302: gr->cx--;
303: } while (!grid_reader_in_set(gr, separators));
304: gr->cx = oldx;
305: gr->cy = oldy;
1.2 nicm 306: }
307:
308: /* Jump forward to character. */
309: int
310: grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
311: {
312: struct grid_cell gc;
313: u_int px, py, xx, yy;
314:
315: px = gr->cx;
316: yy = gr->gd->hsize + gr->gd->sy - 1;
317:
318: for (py = gr->cy; py <= yy; py++) {
319: xx = grid_line_length(gr->gd, py);
320: while (px < xx) {
321: grid_get_cell(gr->gd, px, py, &gc);
322: if (!(gc.flags & GRID_FLAG_PADDING) &&
323: gc.data.size == jc->size &&
324: memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
325: gr->cx = px;
326: gr->cy = py;
327: return 1;
328: }
329: px++;
330: }
331:
332: if (py == yy ||
333: !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
334: return 0;
335: px = 0;
336: }
337: return 0;
338: }
339:
340: /* Jump back to character. */
341: int
342: grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
343: {
344: struct grid_cell gc;
345: u_int px, py, xx;
346:
347: xx = gr->cx + 1;
348:
349: for (py = gr->cy + 1; py > 0; py--) {
350: for (px = xx; px > 0; px--) {
351: grid_get_cell(gr->gd, px - 1, py - 1, &gc);
352: if (!(gc.flags & GRID_FLAG_PADDING) &&
353: gc.data.size == jc->size &&
354: memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
355: gr->cx = px - 1;
356: gr->cy = py - 1;
357: return 1;
358: }
359: }
360:
361: if (py == 1 ||
362: !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
363: return 0;
364: xx = grid_line_length(gr->gd, py - 2);
365: }
366: return 0;
1.4 nicm 367: }
368:
369: /* Jump back to the first non-blank character of the line. */
370: void
371: grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
372: {
373: struct grid_cell gc;
1.5 ! nicm 374: u_int px, py, xx, yy, oldx, oldy;
1.4 nicm 375:
376: yy = gr->gd->hsize + gr->gd->sy - 1;
1.5 ! nicm 377: oldx = gr->cx;
! 378: oldy = gr->cy;
1.4 nicm 379: grid_reader_cursor_start_of_line(gr, 1);
380:
381: for (py = gr->cy; py <= yy; py++) {
382: xx = grid_line_length(gr->gd, py);
383: for (px = 0; px < xx; px++) {
384: grid_get_cell(gr->gd, px, py, &gc);
1.5 ! nicm 385: if (gc.data.size != 1 || *gc.data.data != ' ') {
! 386: gr->cx = px;
! 387: gr->cy = py;
! 388: return;
! 389: }
1.4 nicm 390: }
391: if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
392: break;
393: }
1.5 ! nicm 394: gr->cx = oldx;
! 395: gr->cy = oldy;
1.1 nicm 396: }