From a231f20082987823084afdf51e313b74ebb5dca7 Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Sun, 17 Jul 2022 08:56:29 +0200
Subject: [PATCH] display: remember text and column positions when softwrapping
 a line

This adjusts the main softwrapping routine so that it remembers the
reached point in a line's text and the corresponding column.  This
avoids having to scan the line from the beginning for each iterative
call, and thus saves a substantial amount of time when softwrapping
very long lines.

This mitigates https://savannah.gnu.org/bugs/?62442.
Reported-by: Devin Hussey <husseydevin@gmail.com>

Original-patch-by: Devin Hussey <husseydevin@gmail.com>
---
 src/move.c       |  3 ++-
 src/prototypes.h |  4 ++--
 src/winio.c      | 34 +++++++++++++++++++++++++---------
 3 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/src/move.c b/src/move.c
index 3db49c92..d110869b 100644
--- a/src/move.c
+++ b/src/move.c
@@ -472,10 +472,11 @@ void do_end(void)
 
 #ifndef NANO_TINY
 	if (ISSET(SOFTWRAP)) {
+		bool kickoff = TRUE;
 		bool last_chunk = FALSE;
 		size_t leftedge = leftedge_for(was_column, openfile->current);
 		size_t rightedge = get_softwrap_breakpoint(openfile->current->data,
-												leftedge, &last_chunk);
+											leftedge, &kickoff, &last_chunk);
 		size_t rightedge_x;
 
 		/* If we're on the last chunk, we're already at the end of the line.
diff --git a/src/prototypes.h b/src/prototypes.h
index 0708ded0..473779ed 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -607,8 +607,8 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge);
 bool less_than_a_screenful(size_t was_lineno, size_t was_leftedge);
 void edit_scroll(bool direction);
 #ifndef NANO_TINY
-size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
-								bool *end_of_line);
+size_t get_softwrap_breakpoint(const char *linedata, size_t leftedge,
+								bool *kickoff, bool *end_of_line);
 size_t get_chunk_and_edge(size_t column, linestruct *line, size_t *leftedge);
 size_t chunk_for(size_t column, linestruct *line);
 size_t leftedge_for(size_t column, linestruct *line);
diff --git a/src/winio.c b/src/winio.c
index dee54851..402cbc8f 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2825,6 +2825,8 @@ int update_softwrapped_line(linestruct *line)
 		/* The end column of the current chunk. */
 	char *converted;
 		/* The data of the chunk with tabs and control characters expanded. */
+	bool kickoff = TRUE;
+		/* This tells the softwrapping routine to start at beginning-of-line. */
 	bool end_of_line = FALSE;
 		/* Becomes TRUE when the last chunk of the line has been reached. */
 
@@ -2849,7 +2851,7 @@ int update_softwrapped_line(linestruct *line)
 	starting_row = row;
 
 	while (!end_of_line && row < editwinrows) {
-		to_col = get_softwrap_breakpoint(line->data, from_col, &end_of_line);
+		to_col = get_softwrap_breakpoint(line->data, from_col, &kickoff, &end_of_line);
 
 		sequel_column = (end_of_line) ? 0 : to_col;
 
@@ -2930,13 +2932,14 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
 #ifndef NANO_TINY
 	if (ISSET(SOFTWRAP)) {
 		size_t current_leftedge = *leftedge;
+		bool kickoff = TRUE;
 
 		/* Advance through the requested number of chunks. */
 		for (i = nrows; i > 0; i--) {
 			bool end_of_line = FALSE;
 
 			current_leftedge = get_softwrap_breakpoint((*line)->data,
-										current_leftedge, &end_of_line);
+									current_leftedge, &kickoff, &end_of_line);
 
 			if (!end_of_line)
 				continue;
@@ -2946,6 +2949,7 @@ int go_forward_chunks(int nrows, linestruct **line, size_t *leftedge)
 
 			*line = (*line)->next;
 			current_leftedge = 0;
+			kickoff = TRUE;
 		}
 
 		/* Only change leftedge when we actually could move. */
@@ -3071,20 +3075,29 @@ void edit_scroll(bool direction)
  * return it.  This will always be editwincols or less after leftedge.  Set
  * end_of_line to TRUE if we reach the end of the line while searching the
  * text.  Assume leftedge is the leftmost column of a softwrapped chunk. */
-size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
-								bool *end_of_line)
+size_t get_softwrap_breakpoint(const char *linedata, size_t leftedge,
+								bool *kickoff, bool *end_of_line)
 {
+	static const char *text;
+		/* Pointer at the current character in this line's data. */
+	static size_t column;
+		/* Column position that corresponds to the above pointer. */
 	size_t goal_column = leftedge + editwincols;
 		/* The place at or before which text must be broken. */
 	size_t breaking_col = goal_column;
 		/* The column where text can be broken, when there's no better. */
-	size_t column = 0;
-		/* Current column position in text. */
 	size_t last_blank_col = 0;
 		/* The column position of the last seen whitespace character. */
 	const char *farthest_blank = NULL;
 		/* A pointer to the last seen whitespace character in text. */
 
+	/* Initialize the static variables when it's another line. */
+	if (*kickoff) {
+		text = linedata;
+		column = 0;
+		*kickoff = FALSE;
+	}
+
 	/* First find the place in text where the current chunk starts. */
 	while (*text != '\0' && column < leftedge)
 		text += advance_over(text, &column);
@@ -3132,9 +3145,10 @@ size_t get_chunk_and_edge(size_t column, linestruct *line, size_t *leftedge)
 {
 	size_t current_chunk = 0, start_col = 0, end_col;
 	bool end_of_line = FALSE;
+	bool kickoff = TRUE;
 
 	while (TRUE) {
-		end_col = get_softwrap_breakpoint(line->data, start_col, &end_of_line);
+		end_col = get_softwrap_breakpoint(line->data, start_col, &kickoff, &end_of_line);
 
 		/* We reached the end of the line and/or found column, so get out. */
 		if (end_of_line || (start_col <= column && column < end_col)) {
@@ -3196,9 +3210,10 @@ size_t actual_last_column(size_t leftedge, size_t column)
 {
 #ifndef NANO_TINY
 	if (ISSET(SOFTWRAP)) {
+		bool kickoff = TRUE;
 		bool last_chunk = FALSE;
 		size_t end_col = get_softwrap_breakpoint(openfile->current->data,
-										leftedge, &last_chunk) - leftedge;
+										leftedge, &kickoff, &last_chunk) - leftedge;
 
 		/* If we're not on the last chunk, we're one column past the end of
 		 * the row.  Shifting back one column might put us in the middle of
@@ -3476,6 +3491,7 @@ void spotlight_softwrapped(size_t from_col, size_t to_col)
 	size_t leftedge = leftedge_for(from_col, openfile->current);
 	size_t break_col;
 	bool end_of_line = FALSE;
+	bool kickoff = TRUE;
 	char *word;
 
 	place_the_cursor();
@@ -3483,7 +3499,7 @@ void spotlight_softwrapped(size_t from_col, size_t to_col)
 
 	while (row < editwinrows) {
 		break_col = get_softwrap_breakpoint(openfile->current->data,
-												leftedge, &end_of_line);
+											leftedge, &kickoff, &end_of_line);
 
 		/* If the highlighting ends on this chunk, we can stop after it. */
 		if (break_col >= to_col) {
-- 
2.35.3

