From ffa4d489676f40582b63c1791d3bf5d3b75d8421 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 2 Apr 2019 08:45:32 +0000
Subject: [PATCH] Store and restore cursor across reflow by working out a
 position based on unwrapped lines, rather than a grid offset. Fixes problems
 reported by Thomas Sattler and Paul de Weerd.

---
 grid.c   | 69 ++++++++++++++++++++++++++++++++++++--------------------
 screen.c |  8 +++----
 tmux.h   |  4 ++--
 3 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/grid.c b/grid.c
index 531afa9d..53751c03 100644
--- a/grid.c
+++ b/grid.c
@@ -1291,40 +1291,61 @@ grid_reflow(struct grid *gd, u_int sx)
 	free(target);
 }
 
-/* Convert point position to offset from the start of the grid. */
-u_int
-grid_to_offset(struct grid *gd, u_int px, u_int py)
+/* Convert to position based on wrapped lines. */
+void
+grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
 {
-	u_int	yy, offset = 0;
+	u_int	ax = 0, ay = 0, yy;
 
-	if (py > gd->hsize + gd->sy - 1) {
-		px = UINT_MAX;
-		py = gd->hsize + gd->sy - 1;
+	for (yy = 0; yy < py; yy++) {
+		if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
+			ax += gd->linedata[yy].cellused;
+		else {
+			ax = 0;
+			ay++;
+		}
 	}
-
-	for (yy = 0; yy < py; yy++)
-		offset += gd->linedata[yy].cellused;
-	if (px > gd->linedata[yy].cellused)
-		px = gd->linedata[yy].cellused;
-	return (offset + px);
+	if (px >= gd->linedata[yy].cellused)
+		ax = UINT_MAX;
+	else
+		ax += px;
+	*wx = ax;
+	*wy = ay;
 }
 
-/* Convert offset from the start of the grid to point position. */
+/* Convert position based on wrapped lines back. */
 void
-grid_from_offset(struct grid *gd, u_int offset, u_int *px, u_int *py)
+grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
 {
-	u_int	yy;
-
-	*px = *py = 0;
+	u_int	yy, ax = 0, ay = 0;
 
 	for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
-		if (offset <= gd->linedata[yy].cellused)
+		if (ay == wy)
 			break;
-		offset -= gd->linedata[yy].cellused;
+		if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
+			ax += gd->linedata[yy].cellused;
+		else {
+			ax = 0;
+			ay++;
+		}
 	}
-	if (offset < gd->linedata[yy].cellused)
-		*px = offset;
-	else
-		*px = gd->linedata[yy].cellused;
+
+	/*
+	 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
+	 * until we find the end or the line now containing wx.
+	 */
+	if (wx == UINT_MAX) {
+		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
+			yy++;
+		wx = gd->linedata[yy].cellused;
+	} else {
+		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
+			if (wx < gd->linedata[yy].cellused)
+				break;
+			wx -= gd->linedata[yy].cellused;
+			yy++;
+		}
+	}
+	*px = wx;
 	*py = yy;
 }
diff --git a/screen.c b/screen.c
index 92eb31de..d24f4ba4 100644
--- a/screen.c
+++ b/screen.c
@@ -464,17 +464,17 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
 static void
 screen_reflow(struct screen *s, u_int new_x)
 {
-	u_int		offset, cx = s->cx, cy = s->grid->hsize + s->cy;
+	u_int		cx = s->cx, cy = s->grid->hsize + s->cy, wx, wy;
 	struct timeval	start, tv;
 
 	gettimeofday(&start, NULL);
 
-	offset = grid_to_offset(s->grid, cx, cy);
-	log_debug("%s: cursor %u,%u offset is %u", __func__, cx, cy, offset);
+	grid_wrap_position(s->grid, cx, cy, &wx, &wy);
+	log_debug("%s: cursor %u,%u is %u,%u", __func__, cx, cy, wx, wy);
 
 	grid_reflow(s->grid, new_x);
 
-	grid_from_offset(s->grid, offset, &cx, &cy);
+	grid_unwrap_position(s->grid, &cx, &cy, wx, wy);
 	log_debug("%s: new cursor is %u,%u", __func__, cx, cy);
 
 	if (cy >= s->grid->hsize) {
diff --git a/tmux.h b/tmux.h
index 91002ad0..0821d790 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2101,8 +2101,8 @@ char	*grid_string_cells(struct grid *, u_int, u_int, u_int,
 void	 grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
 	     u_int);
 void	 grid_reflow(struct grid *, u_int);
-u_int	 grid_to_offset(struct grid *, u_int, u_int);
-void	 grid_from_offset(struct grid *, u_int, u_int *, u_int *);
+void	 grid_wrap_position(struct grid *, u_int, u_int, u_int *, u_int *);
+void	 grid_unwrap_position(struct grid *, u_int *, u_int *, u_int, u_int);
 
 /* grid-view.c */
 void	 grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *);