From 0e9bef3429e92e9d9c6a087f741828b35329f242 Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Sun, 17 Jul 2022 08:56:29 +0200 Subject: [PATCH 1/2] 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 Original-patch-by: Devin Hussey --- 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) { From d4a1dbd4a9a5724238f42688f8716b593ebc17bb Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Fri, 22 Jul 2022 09:11:31 +0200 Subject: [PATCH 2/2] tweaks: rename two variables, to not contain the name of another Also, elide an unneeded condition: when not softwrapping, left_x will be zero, which is always smaller than or equal to the indentation. Furthermore, reshuffle a few lines, improve three comments, and adjust one to mention the parameter that was added in the previous commit. --- src/move.c | 20 ++++++++++---------- src/winio.c | 23 +++++++++++++---------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/move.c b/src/move.c index d110869b..ddcbd50f 100644 --- a/src/move.c +++ b/src/move.c @@ -410,12 +410,12 @@ void do_home(void) bool moved_off_chunk = TRUE; #ifndef NANO_TINY bool moved = FALSE; - size_t leftedge = 0, leftedge_x = 0; + size_t leftedge = 0; + size_t left_x = 0; if (ISSET(SOFTWRAP)) { leftedge = leftedge_for(was_column, openfile->current); - leftedge_x = proper_x(openfile->current, &leftedge, FALSE, leftedge, - NULL); + left_x = proper_x(openfile->current, &leftedge, FALSE, leftedge, NULL); } if (ISSET(SMART_HOME)) { @@ -428,7 +428,7 @@ void do_home(void) if (openfile->current_x == indent_x) { openfile->current_x = 0; moved = TRUE; - } else if (!ISSET(SOFTWRAP) || leftedge_x <= indent_x) { + } else if (left_x <= indent_x) { openfile->current_x = indent_x; moved = TRUE; } @@ -438,10 +438,10 @@ void do_home(void) if (!moved && ISSET(SOFTWRAP)) { /* If already at the left edge of the screen, move fully home. * Otherwise, move to the left edge. */ - if (openfile->current_x == leftedge_x) + if (openfile->current_x == left_x) openfile->current_x = 0; else { - openfile->current_x = leftedge_x; + openfile->current_x = left_x; openfile->placewewant = leftedge; moved_off_chunk = FALSE; } @@ -477,7 +477,7 @@ void do_end(void) size_t leftedge = leftedge_for(was_column, openfile->current); size_t rightedge = get_softwrap_breakpoint(openfile->current->data, leftedge, &kickoff, &last_chunk); - size_t rightedge_x; + size_t right_x; /* If we're on the last chunk, we're already at the end of the line. * Otherwise, we're one column past the end of the line. Shifting @@ -486,14 +486,14 @@ void do_end(void) if (!last_chunk) rightedge--; - rightedge_x = actual_x(openfile->current->data, rightedge); + right_x = actual_x(openfile->current->data, rightedge); /* If already at the right edge of the screen, move fully to * the end of the line. Otherwise, move to the right edge. */ - if (openfile->current_x == rightedge_x) + if (openfile->current_x == right_x) openfile->current_x = line_len; else { - openfile->current_x = rightedge_x; + openfile->current_x = right_x; openfile->placewewant = rightedge; moved_off_chunk = FALSE; } diff --git a/src/winio.c b/src/winio.c index 402cbc8f..1ccb0022 100644 --- a/src/winio.c +++ b/src/winio.c @@ -3071,10 +3071,11 @@ void edit_scroll(bool direction) } #ifndef NANO_TINY -/* Get the column number after leftedge where we can break the given text, and - * 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. */ +/* Get the column number after leftedge where we can break the given linedata, + * and return it. (This will always be at most editwincols after leftedge.) + * When kickoff is TRUE, start at the beginning of the linedata; otherwise, + * continue from where the previous call left off. Set end_of_line to TRUE + * when end-of-line is reached while searching for a possible breakpoint. */ size_t get_softwrap_breakpoint(const char *linedata, size_t leftedge, bool *kickoff, bool *end_of_line) { @@ -3138,27 +3139,29 @@ size_t get_softwrap_breakpoint(const char *linedata, size_t leftedge, return (editwincols > 1) ? breaking_col : column - 1; } -/* Get the row of the softwrapped chunk of the given line that column is on, - * relative to the first row (zero-based), and return it. If leftedge isn't - * NULL, return the leftmost column of the chunk in it. */ +/* Return the row number of the softwrapped chunk in the given line that the + * given column is on, relative to the first row (zero-based). If leftedge + * isn't NULL, return in it the leftmost column of the chunk. */ size_t get_chunk_and_edge(size_t column, linestruct *line, size_t *leftedge) { - size_t current_chunk = 0, start_col = 0, end_col; + size_t current_chunk = 0; bool end_of_line = FALSE; bool kickoff = TRUE; + size_t start_col = 0; + size_t end_col; while (TRUE) { 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. */ + /* When the column is in range or we reached end-of-line, we're done. */ if (end_of_line || (start_col <= column && column < end_col)) { if (leftedge != NULL) *leftedge = start_col; return current_chunk; } - current_chunk++; start_col = end_col; + current_chunk++; } }