From 8d85daee1a732f6a3fff61aa9729d25c07f50d58 Mon Sep 17 00:00:00 2001
From: David Lawrence Ramsey <pooka109@gmail.com>
Date: Fri, 4 Aug 2017 15:43:14 -0500
Subject: [PATCH 1/2] softwrap: don't break softwrapped chunks before partial
 tabs

Tabs are of variable length depending on position, so allow them to be
displayed partially on one chunk and partially on another.  Break chunks
at the edge of the screen when they end in tabs, and adjust the index to
stay on the same chunk when tabs begin on the previous chunk (which is
needed for Up, Down, PageUp, PageDown, and Home; End doesn't need to
care about the previous chunk).

This fixes http://savannah.gnu.org/bugs/?51621.
---
 src/move.c  | 26 +++++++++++++++++++++-----
 src/winio.c | 22 ++++++++++++++++++++--
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/src/move.c b/src/move.c
index 85159a1..1e8940d 100644
--- a/src/move.c
+++ b/src/move.c
@@ -62,6 +62,22 @@ void get_edge_and_target(size_t *leftedge, size_t *target_column)
     }
 }
 
+/* Return the index in text that corresponds to the given column on the chunk
+ * starting on the given leftedge.  This is the same as actual_x(), unless the
+ * column is in the middle of a tab at the start of a chunk: in that case, the
+ * index is shifted to after the tab, so we can navigate it properly. */
+size_t proper_x(const char *text, size_t leftedge, size_t column)
+{
+    size_t index = actual_x(text, column);
+
+#ifndef NANO_TINY
+    if (ISSET(SOFTWRAP) && leftedge % tabsize != 0 && text[index] == '\t')
+	index++;
+#endif
+
+    return index;
+}
+
 /* Move up nearly one screenful. */
 void do_page_up(void)
 {
@@ -88,7 +104,7 @@ void do_page_up(void)
     }
 
     openfile->placewewant = leftedge + target_column;
-    openfile->current_x = actual_x(openfile->current->data,
+    openfile->current_x = proper_x(openfile->current->data, leftedge,
 				actual_last_column(leftedge, target_column));
 
     /* Move the viewport so that the cursor stays immobile, if possible. */
@@ -122,7 +138,7 @@ void do_page_down(void)
     }
 
     openfile->placewewant = leftedge + target_column;
-    openfile->current_x = actual_x(openfile->current->data,
+    openfile->current_x = proper_x(openfile->current->data, leftedge,
 				actual_last_column(leftedge, target_column));
 
     /* Move the viewport so that the cursor stays immobile, if possible. */
@@ -352,7 +368,7 @@ void do_home(bool be_clever)
 
     if (ISSET(SOFTWRAP)) {
 	leftedge = leftedge_for(was_column, openfile->current);
-	leftedge_x = actual_x(openfile->current->data, leftedge);
+	leftedge_x = proper_x(openfile->current->data, leftedge, leftedge);
     }
 
     if (ISSET(SMART_HOME) && be_clever) {
@@ -482,7 +498,7 @@ void do_up(bool scroll_only)
 	return;
 
     openfile->placewewant = leftedge + target_column;
-    openfile->current_x = actual_x(openfile->current->data,
+    openfile->current_x = proper_x(openfile->current->data, leftedge,
 				actual_last_column(leftedge, target_column));
 
     /* When the cursor was on the first line of the edit window (or when just
@@ -526,7 +542,7 @@ void do_down(bool scroll_only)
 	return;
 
     openfile->placewewant = leftedge + target_column;
-    openfile->current_x = actual_x(openfile->current->data,
+    openfile->current_x = proper_x(openfile->current->data, leftedge,
 				actual_last_column(leftedge, target_column));
 
     /* When the cursor was on the last line of the edit window (or when just
diff --git a/src/winio.c b/src/winio.c
index a66a4a3..2ffcd0c 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2966,6 +2966,8 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
 	/* Current index in text. */
     size_t column = 0;
 	/* Current column position in text. */
+    size_t prev_index = 0;
+	/* Previous index in text. */
     size_t prev_column = 0;
 	/* Previous column position in text. */
     size_t goal_column;
@@ -2982,8 +2984,10 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
     while (*text != '\0' && column < leftedge)
 	text += parse_mbchar(text, NULL, &column);
 
-    /* Use a full screen row for text. */
-    goal_column = column + editwincols;
+    /* Use a full screen row for text.  If a tab begins on the previous chunk
+     * and ends on this one, column will not be the first column of the screen
+     * row, so use leftedge instead. */
+    goal_column = leftedge + editwincols;
 
     while (*text != '\0' && column <= goal_column) {
 	/* When breaking at blanks, do it *before* the target column. */
@@ -2993,6 +2997,7 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
 	    lastblank_column = column;
 	}
 
+	prev_index = index;
 	prev_column = column;
 	char_len = parse_mbchar(text, NULL, &column);
 	text += char_len;
@@ -3011,9 +3016,22 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
     if (found_blank) {
 	text = text - index + lastblank_index;
 	parse_mbchar(text, NULL, &lastblank_column);
+
+	/* If a tab begins on this chunk and ends on the next one, break at the
+	 * last column of this screen row. */
+	if (lastblank_column > goal_column && *text == '\t')
+	    lastblank_column = goal_column;
+
 	return lastblank_column;
     }
 
+    text = text - index + prev_index;
+
+    /* If a tab begins on this chunk and ends on the next one, break at the
+     * last column of this screen row. */
+    if (*text == '\t')
+	prev_column = goal_column;
+
     /* Otherwise, return the column of the last character that doesn't
      * overshoot the target, since we can't break the text anywhere else. */
     return (editwincols > 1) ? prev_column : column - 1;
-- 
2.9.0

