xref: /openbsd-src/usr.bin/tmux/grid-reader.c (revision 61e9d0de7a4c2cb6906206c99b245d89b1ad9d87)
1*61e9d0deSnicm /* $OpenBSD: grid-reader.c,v 1.9 2024/11/20 20:54:02 nicm Exp $ */
2704a71ceSnicm 
3704a71ceSnicm /*
4704a71ceSnicm  * Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
5704a71ceSnicm  *
6704a71ceSnicm  * Permission to use, copy, modify, and distribute this software for any
7704a71ceSnicm  * purpose with or without fee is hereby granted, provided that the above
8704a71ceSnicm  * copyright notice and this permission notice appear in all copies.
9704a71ceSnicm  *
10704a71ceSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11704a71ceSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12704a71ceSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13704a71ceSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14704a71ceSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15704a71ceSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16704a71ceSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17704a71ceSnicm  */
18704a71ceSnicm 
19704a71ceSnicm #include "tmux.h"
20d70f1befSnicm #include <string.h>
21704a71ceSnicm 
22704a71ceSnicm /* Initialise virtual cursor. */
23704a71ceSnicm void
24704a71ceSnicm grid_reader_start(struct grid_reader *gr, struct grid *gd, u_int cx, u_int cy)
25704a71ceSnicm {
26704a71ceSnicm 	gr->gd = gd;
27704a71ceSnicm 	gr->cx = cx;
28704a71ceSnicm 	gr->cy = cy;
29704a71ceSnicm }
30704a71ceSnicm 
31704a71ceSnicm /* Get cursor position from reader. */
32704a71ceSnicm void
33704a71ceSnicm grid_reader_get_cursor(struct grid_reader *gr, u_int *cx, u_int *cy)
34704a71ceSnicm {
35704a71ceSnicm 	*cx = gr->cx;
36704a71ceSnicm 	*cy = gr->cy;
37704a71ceSnicm }
38704a71ceSnicm 
39704a71ceSnicm /* Get length of line containing the cursor. */
40704a71ceSnicm u_int
41704a71ceSnicm grid_reader_line_length(struct grid_reader *gr)
42704a71ceSnicm {
43704a71ceSnicm 	return (grid_line_length(gr->gd, gr->cy));
44704a71ceSnicm }
45704a71ceSnicm 
46704a71ceSnicm /* Move cursor forward one position. */
47704a71ceSnicm void
48704a71ceSnicm grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
49704a71ceSnicm {
50704a71ceSnicm 	u_int			px;
51704a71ceSnicm 	struct grid_cell	gc;
52704a71ceSnicm 
53704a71ceSnicm 	if (all)
54704a71ceSnicm 		px = gr->gd->sx;
55704a71ceSnicm 	else
56704a71ceSnicm 		px = grid_reader_line_length(gr);
57704a71ceSnicm 
58704a71ceSnicm 	if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
59704a71ceSnicm 		grid_reader_cursor_start_of_line(gr, 0);
60704a71ceSnicm 		grid_reader_cursor_down(gr);
61704a71ceSnicm 	} else if (gr->cx < px) {
62704a71ceSnicm 		gr->cx++;
63704a71ceSnicm 		while (gr->cx < px) {
64704a71ceSnicm 			grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
65704a71ceSnicm 			if (~gc.flags & GRID_FLAG_PADDING)
66704a71ceSnicm 				break;
67704a71ceSnicm 			gr->cx++;
68704a71ceSnicm 		}
69704a71ceSnicm 	}
70704a71ceSnicm }
71704a71ceSnicm 
72704a71ceSnicm /* Move cursor back one position. */
73704a71ceSnicm void
742870e7b8Snicm grid_reader_cursor_left(struct grid_reader *gr, int wrap)
75704a71ceSnicm {
76704a71ceSnicm 	struct grid_cell	gc;
77704a71ceSnicm 
78704a71ceSnicm 	while (gr->cx > 0) {
79704a71ceSnicm 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
80704a71ceSnicm 		if (~gc.flags & GRID_FLAG_PADDING)
81704a71ceSnicm 			break;
82704a71ceSnicm 		gr->cx--;
83704a71ceSnicm 	}
842870e7b8Snicm 	if (gr->cx == 0 && gr->cy > 0 &&
852870e7b8Snicm 	    (wrap ||
862870e7b8Snicm 	     grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) {
87704a71ceSnicm 		grid_reader_cursor_up(gr);
88704a71ceSnicm 		grid_reader_cursor_end_of_line(gr, 0, 0);
89704a71ceSnicm 	} else if (gr->cx > 0)
90704a71ceSnicm 		gr->cx--;
91704a71ceSnicm }
92704a71ceSnicm 
93704a71ceSnicm /* Move cursor down one line. */
94704a71ceSnicm void
95704a71ceSnicm grid_reader_cursor_down(struct grid_reader *gr)
96704a71ceSnicm {
97704a71ceSnicm 	struct grid_cell	gc;
98704a71ceSnicm 
99704a71ceSnicm 	if (gr->cy < gr->gd->hsize + gr->gd->sy - 1)
100704a71ceSnicm 		gr->cy++;
101704a71ceSnicm 	while (gr->cx > 0) {
102704a71ceSnicm 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
103704a71ceSnicm 		if (~gc.flags & GRID_FLAG_PADDING)
104704a71ceSnicm 			break;
105704a71ceSnicm 		gr->cx--;
106704a71ceSnicm 	}
107704a71ceSnicm }
108704a71ceSnicm 
109704a71ceSnicm /* Move cursor up one line. */
110704a71ceSnicm void
111704a71ceSnicm grid_reader_cursor_up(struct grid_reader *gr)
112704a71ceSnicm {
113704a71ceSnicm 	struct grid_cell	gc;
114704a71ceSnicm 
115704a71ceSnicm 	if (gr->cy > 0)
116704a71ceSnicm 		gr->cy--;
117704a71ceSnicm 	while (gr->cx > 0) {
118704a71ceSnicm 		grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
119704a71ceSnicm 		if (~gc.flags & GRID_FLAG_PADDING)
120704a71ceSnicm 			break;
121704a71ceSnicm 		gr->cx--;
122704a71ceSnicm 	}
123704a71ceSnicm }
124704a71ceSnicm 
125704a71ceSnicm /* Move cursor to the start of the line. */
126704a71ceSnicm void
127704a71ceSnicm grid_reader_cursor_start_of_line(struct grid_reader *gr, int wrap)
128704a71ceSnicm {
129704a71ceSnicm 	if (wrap) {
130704a71ceSnicm 		while (gr->cy > 0 &&
131704a71ceSnicm 		    grid_get_line(gr->gd, gr->cy - 1)->flags &
132704a71ceSnicm 		        GRID_LINE_WRAPPED)
133704a71ceSnicm 			gr->cy--;
134704a71ceSnicm 	}
135704a71ceSnicm 	gr->cx = 0;
136704a71ceSnicm }
137704a71ceSnicm 
138704a71ceSnicm /* Move cursor to the end of the line. */
139704a71ceSnicm void
140704a71ceSnicm grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all)
141704a71ceSnicm {
142704a71ceSnicm 	u_int	yy;
143704a71ceSnicm 
144704a71ceSnicm 	if (wrap) {
145704a71ceSnicm 		yy = gr->gd->hsize + gr->gd->sy - 1;
146704a71ceSnicm 		while (gr->cy < yy && grid_get_line(gr->gd, gr->cy)->flags &
147704a71ceSnicm 		    GRID_LINE_WRAPPED)
148704a71ceSnicm 			gr->cy++;
149704a71ceSnicm 	}
150704a71ceSnicm 	if (all)
151704a71ceSnicm 		gr->cx = gr->gd->sx;
152704a71ceSnicm 	else
153704a71ceSnicm 		gr->cx = grid_reader_line_length(gr);
154704a71ceSnicm }
155704a71ceSnicm 
1568f36458cSnicm /* Handle line wrapping while moving the cursor. */
1578f36458cSnicm static int
1588f36458cSnicm grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
1598f36458cSnicm {
1608f36458cSnicm 	/*
1618f36458cSnicm 	 * Make sure the cursor lies within the grid reader's bounding area,
1628f36458cSnicm 	 * wrapping to the next line as necessary. Return zero if the cursor
1638f36458cSnicm 	 * would wrap past the bottom of the grid.
1648f36458cSnicm 	 */
1658f36458cSnicm 	while (gr->cx > *xx) {
1668f36458cSnicm 		if (gr->cy == *yy)
1678f36458cSnicm 			return (0);
1688f36458cSnicm 		grid_reader_cursor_start_of_line(gr, 0);
1698f36458cSnicm 		grid_reader_cursor_down(gr);
1708f36458cSnicm 
1718f36458cSnicm 		if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
1728f36458cSnicm 			*xx = gr->gd->sx - 1;
1738f36458cSnicm 		else
1748f36458cSnicm 			*xx = grid_reader_line_length(gr);
1758f36458cSnicm 	}
1768f36458cSnicm 	return (1);
1778f36458cSnicm }
1788f36458cSnicm 
179704a71ceSnicm /* Check if character under cursor is in set. */
180704a71ceSnicm int
181704a71ceSnicm grid_reader_in_set(struct grid_reader *gr, const char *set)
182704a71ceSnicm {
183*61e9d0deSnicm 	return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
184704a71ceSnicm }
185704a71ceSnicm 
186704a71ceSnicm /* Move cursor to the start of the next word. */
187704a71ceSnicm void
188704a71ceSnicm grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
189704a71ceSnicm {
190*61e9d0deSnicm 	u_int	xx, yy, width;
191704a71ceSnicm 
192704a71ceSnicm 	/* Do not break up wrapped words. */
193704a71ceSnicm 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
194f4e980ccSnicm 		xx = gr->gd->sx - 1;
195704a71ceSnicm 	else
196704a71ceSnicm 		xx = grid_reader_line_length(gr);
197704a71ceSnicm 	yy = gr->gd->hsize + gr->gd->sy - 1;
198704a71ceSnicm 
199704a71ceSnicm 	/*
2008f36458cSnicm 	 * When navigating via spaces (for example with next-space) separators
2018f36458cSnicm 	 * should be empty.
202704a71ceSnicm 	 *
2038f36458cSnicm 	 * If we started on a separator that is not whitespace, skip over
2048f36458cSnicm 	 * subsequent separators that are not whitespace. Otherwise, if we
2058f36458cSnicm 	 * started on a non-whitespace character, skip over subsequent
2068f36458cSnicm 	 * characters that are neither whitespace nor separators. Then, skip
2078f36458cSnicm 	 * over whitespace (if any) until the next non-whitespace character.
208704a71ceSnicm 	 */
2098f36458cSnicm 	if (!grid_reader_handle_wrap(gr, &xx, &yy))
210704a71ceSnicm 		return;
2118f36458cSnicm 	if (!grid_reader_in_set(gr, WHITESPACE)) {
2128f36458cSnicm 		if (grid_reader_in_set(gr, separators)) {
2138f36458cSnicm 			do
214704a71ceSnicm 				gr->cx++;
2158f36458cSnicm 			while (grid_reader_handle_wrap(gr, &xx, &yy) &&
2168f36458cSnicm 			    grid_reader_in_set(gr, separators) &&
2178f36458cSnicm 			    !grid_reader_in_set(gr, WHITESPACE));
2188f36458cSnicm 		} else {
2198f36458cSnicm 			do
2208f36458cSnicm 				gr->cx++;
2218f36458cSnicm 			while (grid_reader_handle_wrap(gr, &xx, &yy) &&
2228f36458cSnicm 			    !(grid_reader_in_set(gr, separators) ||
2238f36458cSnicm 			    grid_reader_in_set(gr, WHITESPACE)));
224704a71ceSnicm 		}
2258f36458cSnicm 	}
2268f36458cSnicm 	while (grid_reader_handle_wrap(gr, &xx, &yy) &&
227*61e9d0deSnicm 	    (width = grid_reader_in_set(gr, WHITESPACE)))
228*61e9d0deSnicm 		gr->cx += width;
229704a71ceSnicm }
230704a71ceSnicm 
231704a71ceSnicm /* Move cursor to the end of the next word. */
232704a71ceSnicm void
233704a71ceSnicm grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators)
234704a71ceSnicm {
235704a71ceSnicm 	u_int	xx, yy;
236704a71ceSnicm 
237704a71ceSnicm 	/* Do not break up wrapped words. */
238704a71ceSnicm 	if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
239f4e980ccSnicm 		xx = gr->gd->sx - 1;
240704a71ceSnicm 	else
241704a71ceSnicm 		xx = grid_reader_line_length(gr);
242704a71ceSnicm 	yy = gr->gd->hsize + gr->gd->sy - 1;
243704a71ceSnicm 
244704a71ceSnicm 	/*
2458f36458cSnicm 	 * When navigating via spaces (for example with next-space), separators
2468f36458cSnicm 	 * should be empty in both modes.
247704a71ceSnicm 	 *
2488f36458cSnicm 	 * If we started on a whitespace, move until reaching the first
2498f36458cSnicm 	 * non-whitespace character. If that character is a separator, treat
2508f36458cSnicm 	 * subsequent separators as a word, and continue moving until the first
2518f36458cSnicm 	 * non-separator. Otherwise, continue moving until the first separator
2528f36458cSnicm 	 * or whitespace.
253704a71ceSnicm 	 */
254704a71ceSnicm 
2558f36458cSnicm 	while (grid_reader_handle_wrap(gr, &xx, &yy)) {
2568f36458cSnicm 		if (grid_reader_in_set(gr, WHITESPACE))
257704a71ceSnicm 			gr->cx++;
2588f36458cSnicm 		else if (grid_reader_in_set(gr, separators)) {
2598f36458cSnicm 			do
2608f36458cSnicm 				gr->cx++;
2618f36458cSnicm 			while (grid_reader_handle_wrap(gr, &xx, &yy) &&
2628f36458cSnicm 			    grid_reader_in_set(gr, separators) &&
2638f36458cSnicm 			    !grid_reader_in_set(gr, WHITESPACE));
2648f36458cSnicm 			return;
2658f36458cSnicm 		} else {
2668f36458cSnicm 			do
2678f36458cSnicm 				gr->cx++;
2688f36458cSnicm 			while (grid_reader_handle_wrap(gr, &xx, &yy) &&
2698f36458cSnicm 			    !(grid_reader_in_set(gr, WHITESPACE) ||
2708f36458cSnicm 			    grid_reader_in_set(gr, separators)));
2718f36458cSnicm 			return;
272704a71ceSnicm 		}
2738f36458cSnicm 	}
274704a71ceSnicm }
275704a71ceSnicm 
276704a71ceSnicm /* Move to the previous place where a word begins. */
277704a71ceSnicm void
278704a71ceSnicm grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
2798f36458cSnicm     int already, int stop_at_eol)
280704a71ceSnicm {
2818f36458cSnicm 	int	oldx, oldy, at_eol, word_is_letters;
282704a71ceSnicm 
283704a71ceSnicm 	/* Move back to the previous word character. */
2848f36458cSnicm 	if (already || grid_reader_in_set(gr, WHITESPACE)) {
285704a71ceSnicm 		for (;;) {
286704a71ceSnicm 			if (gr->cx > 0) {
287704a71ceSnicm 				gr->cx--;
2888f36458cSnicm 				if (!grid_reader_in_set(gr, WHITESPACE)) {
2898f36458cSnicm 					word_is_letters =
2908f36458cSnicm 					    !grid_reader_in_set(gr, separators);
291704a71ceSnicm 					break;
2928f36458cSnicm 				}
293704a71ceSnicm 			} else {
294704a71ceSnicm 				if (gr->cy == 0)
295704a71ceSnicm 					return;
296704a71ceSnicm 				grid_reader_cursor_up(gr);
297704a71ceSnicm 				grid_reader_cursor_end_of_line(gr, 0, 0);
298704a71ceSnicm 
299704a71ceSnicm 				/* Stop if separator at EOL. */
3008f36458cSnicm 				if (stop_at_eol && gr->cx > 0) {
301704a71ceSnicm 					oldx = gr->cx;
302704a71ceSnicm 					gr->cx--;
3038f36458cSnicm 					at_eol = grid_reader_in_set(gr,
3048f36458cSnicm 					    WHITESPACE);
305704a71ceSnicm 					gr->cx = oldx;
3068f36458cSnicm 					if (at_eol) {
3078f36458cSnicm 						word_is_letters = 0;
308704a71ceSnicm 						break;
309704a71ceSnicm 					}
310704a71ceSnicm 				}
311704a71ceSnicm 			}
312704a71ceSnicm 		}
3138f36458cSnicm 	} else
3148f36458cSnicm 		word_is_letters = !grid_reader_in_set(gr, separators);
315704a71ceSnicm 
316704a71ceSnicm 	/* Move back to the beginning of this word. */
317704a71ceSnicm 	do {
318704a71ceSnicm 		oldx = gr->cx;
319704a71ceSnicm 		oldy = gr->cy;
320704a71ceSnicm 		if (gr->cx == 0) {
321704a71ceSnicm 			if (gr->cy == 0 ||
3228f36458cSnicm 			    (~grid_get_line(gr->gd, gr->cy - 1)->flags &
3238f36458cSnicm 			    GRID_LINE_WRAPPED))
324704a71ceSnicm 				break;
325704a71ceSnicm 			grid_reader_cursor_up(gr);
326f4e980ccSnicm 			grid_reader_cursor_end_of_line(gr, 0, 1);
327704a71ceSnicm 		}
328704a71ceSnicm 		if (gr->cx > 0)
329704a71ceSnicm 			gr->cx--;
3308f36458cSnicm 	} while (!grid_reader_in_set(gr, WHITESPACE) &&
3318f36458cSnicm 	    word_is_letters != grid_reader_in_set(gr, separators));
332704a71ceSnicm 	gr->cx = oldx;
333704a71ceSnicm 	gr->cy = oldy;
334704a71ceSnicm }
335d70f1befSnicm 
33626b00f59Snicm /* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
33726b00f59Snicm static int
33826b00f59Snicm grid_reader_cell_equals_data(const struct grid_cell *gc,
33926b00f59Snicm     const struct utf8_data *ud)
34026b00f59Snicm {
34126b00f59Snicm 	if (gc->flags & GRID_FLAG_PADDING)
34226b00f59Snicm 		return (0);
34302d75531Snicm 	if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
34402d75531Snicm 		return (1);
34526b00f59Snicm 	if (gc->data.size != ud->size)
34626b00f59Snicm 		return (0);
34726b00f59Snicm 	return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
34826b00f59Snicm }
34926b00f59Snicm 
350d70f1befSnicm /* Jump forward to character. */
351d70f1befSnicm int
352d70f1befSnicm grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
353d70f1befSnicm {
354d70f1befSnicm 	struct grid_cell	gc;
355d70f1befSnicm 	u_int			px, py, xx, yy;
356d70f1befSnicm 
357d70f1befSnicm 	px = gr->cx;
358d70f1befSnicm 	yy = gr->gd->hsize + gr->gd->sy - 1;
359d70f1befSnicm 
360d70f1befSnicm 	for (py = gr->cy; py <= yy; py++) {
361d70f1befSnicm 		xx = grid_line_length(gr->gd, py);
362d70f1befSnicm 		while (px < xx) {
363d70f1befSnicm 			grid_get_cell(gr->gd, px, py, &gc);
36426b00f59Snicm 			if (grid_reader_cell_equals_data(&gc, jc)) {
365d70f1befSnicm 				gr->cx = px;
366d70f1befSnicm 				gr->cy = py;
3678f36458cSnicm 				return (1);
368d70f1befSnicm 			}
369d70f1befSnicm 			px++;
370d70f1befSnicm 		}
371d70f1befSnicm 
372d70f1befSnicm 		if (py == yy ||
373d70f1befSnicm 		    !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED))
3748f36458cSnicm 			return (0);
375d70f1befSnicm 		px = 0;
376d70f1befSnicm 	}
3778f36458cSnicm 	return (0);
378d70f1befSnicm }
379d70f1befSnicm 
380d70f1befSnicm /* Jump back to character. */
381d70f1befSnicm int
382d70f1befSnicm grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
383d70f1befSnicm {
384d70f1befSnicm 	struct grid_cell	gc;
385d70f1befSnicm 	u_int			px, py, xx;
386d70f1befSnicm 
387d70f1befSnicm 	xx = gr->cx + 1;
388d70f1befSnicm 
389d70f1befSnicm 	for (py = gr->cy + 1; py > 0; py--) {
390d70f1befSnicm 		for (px = xx; px > 0; px--) {
391d70f1befSnicm 			grid_get_cell(gr->gd, px - 1, py - 1, &gc);
39226b00f59Snicm 			if (grid_reader_cell_equals_data(&gc, jc)) {
393d70f1befSnicm 				gr->cx = px - 1;
394d70f1befSnicm 				gr->cy = py - 1;
3958f36458cSnicm 				return (1);
396d70f1befSnicm 			}
397d70f1befSnicm 		}
398d70f1befSnicm 
399d70f1befSnicm 		if (py == 1 ||
400d70f1befSnicm 		    !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED))
4018f36458cSnicm 			return (0);
402d70f1befSnicm 		xx = grid_line_length(gr->gd, py - 2);
403d70f1befSnicm 	}
4048f36458cSnicm 	return (0);
405d70f1befSnicm }
4062870e7b8Snicm 
4072870e7b8Snicm /* Jump back to the first non-blank character of the line. */
4082870e7b8Snicm void
4092870e7b8Snicm grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
4102870e7b8Snicm {
4112870e7b8Snicm 	struct grid_cell	gc;
4126d68d2a2Snicm 	u_int			px, py, xx, yy, oldx, oldy;
4132870e7b8Snicm 
4142870e7b8Snicm 	yy = gr->gd->hsize + gr->gd->sy - 1;
4156d68d2a2Snicm 	oldx = gr->cx;
4166d68d2a2Snicm 	oldy = gr->cy;
4172870e7b8Snicm 	grid_reader_cursor_start_of_line(gr, 1);
4182870e7b8Snicm 
4192870e7b8Snicm 	for (py = gr->cy; py <= yy; py++) {
4202870e7b8Snicm 		xx = grid_line_length(gr->gd, py);
4212870e7b8Snicm 		for (px = 0; px < xx; px++) {
4222870e7b8Snicm 			grid_get_cell(gr->gd, px, py, &gc);
423*61e9d0deSnicm 			if ((gc.data.size != 1 || *gc.data.data != ' ') &&
424*61e9d0deSnicm 			    ~gc.flags & GRID_FLAG_TAB &&
425*61e9d0deSnicm 			    ~gc.flags & GRID_FLAG_PADDING) {
4266d68d2a2Snicm 				gr->cx = px;
4276d68d2a2Snicm 				gr->cy = py;
4286d68d2a2Snicm 				return;
4296d68d2a2Snicm 			}
4302870e7b8Snicm 		}
4312870e7b8Snicm 		if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)
4322870e7b8Snicm 			break;
4332870e7b8Snicm 	}
4346d68d2a2Snicm 	gr->cx = oldx;
4356d68d2a2Snicm 	gr->cy = oldy;
4362870e7b8Snicm }
437