From 9a9f36fca7a728dd2c5b66a0f29b203b638c0e25 Mon Sep 17 00:00:00 2001 From: Benno Schulenberg Date: Sat, 6 Jun 2020 18:57:17 +0200 Subject: [PATCH] indicator: rework how the "scrollbar" is computed when softwrapping Instead of storing for each line the ordinal number of the first chunk in that line, simply store the number of extra chunks in the line. It now takes some more computation to actually draw the scrollbar, but it saves a lot of computation when just inserting or deleting characters (when using --softwrap and causing a change in chunk count). This fixes https://savannah.gnu.org/bugs/?58510. Bug existed since commit 6bccedb3 from two days ago. --- src/cut.c | 16 +++++++--------- src/files.c | 14 ++++++++++++++ src/nano.c | 45 ++++++++++++++++----------------------------- src/nano.h | 4 ++-- src/proto.h | 1 + src/search.c | 4 ++++ src/text.c | 19 ++++++++++++++++++- src/winio.c | 15 ++++++++------- 8 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/cut.c b/src/cut.c index 3c0a8533..35c38841 100644 --- a/src/cut.c +++ b/src/cut.c @@ -34,7 +34,7 @@ void do_deletion(undo_type action) int charlen = char_length(openfile->current->data + openfile->current_x); size_t line_len = strlen(openfile->current->data + openfile->current_x); #ifndef NANO_TINY - size_t old_amount = 0; + size_t old_amount = openfile->current->extrarows; /* If the type of action changed or the cursor moved to a different * line, create a new undo item, otherwise update the existing item. */ @@ -43,20 +43,18 @@ void do_deletion(undo_type action) add_undo(action, NULL); else update_undo(action); - - if (ISSET(SOFTWRAP)) - old_amount = extra_chunks_in(openfile->current); #endif /* Move the remainder of the line "in", over the current character. */ memmove(&openfile->current->data[openfile->current_x], &openfile->current->data[openfile->current_x + charlen], line_len - charlen + 1); #ifndef NANO_TINY - /* When softwrapping and the number of chunks in the current line has - * changed, the chunks must be renumbered and the screen refreshed. */ - if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount) { - renumber_from(openfile->current); - refresh_needed = TRUE; + /* When softwrapping, recompute the number of chunks in the line, + * and schedule a refresh if the number changed. */ + if (ISSET(SOFTWRAP)) { + openfile->current->extrarows = extra_chunks_in(openfile->current); + if (openfile->current->extrarows != old_amount) + refresh_needed = TRUE; } /* Adjust the mark if it is after the cursor on the current line. */ diff --git a/src/files.c b/src/files.c index b1674e95..e21761fe 100644 --- a/src/files.c +++ b/src/files.c @@ -356,6 +356,16 @@ bool has_valid_path(const char *filename) return validity; } +/* Compute and store how many extra rows each line needs when softwrapping. */ +void compute_the_extra_rows_per_line(void) +{ +#ifndef NANO_TINY + if (ISSET(SOFTWRAP)) + for (linestruct *ln = openfile->filetop; ln != NULL; ln = ln->next) + ln->extrarows = extra_chunks_in(ln); +#endif +} + /* This does one of three things. If the filename is "", it just creates * a new empty buffer. When the filename is not empty, it reads that file * into a new buffer when requested, otherwise into the existing buffer. */ @@ -453,6 +463,8 @@ bool open_buffer(const char *filename, bool new_one) openfile->placewewant = 0; } + compute_the_extra_rows_per_line(); + #ifdef ENABLE_COLOR /* If a new buffer was opened, check whether a syntax can be applied. */ if (new_one) @@ -527,6 +539,8 @@ void redecorate_after_switch(void) * or softwrap mode may have been toggled, so make sure that the * starting column for the first row gets an appropriate value. */ ensure_firstcolumn_is_aligned(); + + compute_the_extra_rows_per_line(); #endif /* Update title bar and multiline info to match the current buffer. */ diff --git a/src/nano.c b/src/nano.c index b3296113..0f961b7f 100644 --- a/src/nano.c +++ b/src/nano.c @@ -76,9 +76,7 @@ linestruct *make_new_node(linestruct *prevnode) #endif newnode->lineno = (prevnode) ? prevnode->lineno + 1 : 1; #ifndef NANO_TINY - if (ISSET(SOFTWRAP)) - newnode->chunk_nr = (prevnode) ? - prevnode->chunk_nr + extra_chunks_in(prevnode) + 1 : 1; + newnode->extrarows = -2; /* Bad value, to make it easier to find bugs. */ newnode->has_anchor = FALSE; #endif @@ -154,7 +152,7 @@ linestruct *copy_node(const linestruct *src) #endif dst->lineno = src->lineno; #ifndef NANO_TINY - dst->chunk_nr = src->chunk_nr; + dst->extrarows = src->extrarows; dst->has_anchor = FALSE; #endif @@ -190,21 +188,8 @@ void renumber_from(linestruct *line) { ssize_t number = (line->prev == NULL) ? 0 : line->prev->lineno; -#ifndef NANO_TINY - if (ISSET(SOFTWRAP) && line->prev == NULL) { - line->lineno = ++number; - line->chunk_nr = 1; - line = line->next; - } -#endif - while (line != NULL) { line->lineno = ++number; -#ifndef NANO_TINY - if (ISSET(SOFTWRAP)) - line->chunk_nr = line->prev->chunk_nr + - extra_chunks_in(line->prev) + 1; -#endif line = line->next; } } @@ -1095,7 +1080,7 @@ void do_toggle(int flag) break; case SOFTWRAP: if (ISSET(SOFTWRAP)) - renumber_from(openfile->filetop); + compute_the_extra_rows_per_line(); else openfile->firstcolumn = 0; refresh_needed = TRUE; @@ -1426,12 +1411,12 @@ void inject(char *burst, size_t count) linestruct *thisline = openfile->current; size_t datalen = strlen(thisline->data); #ifndef NANO_TINY - size_t original_row = 0, old_amount = 0; + size_t old_amount = openfile->current->extrarows; + size_t original_row = 0; if (ISSET(SOFTWRAP)) { if (openfile->current_y == editwinrows - 1) original_row = chunk_for(xplustabs(), thisline); - old_amount = extra_chunks_in(thisline); } #endif @@ -1492,15 +1477,17 @@ void inject(char *burst, size_t count) #endif #ifndef NANO_TINY - /* If we were on the last row of the edit window and moved to a new chunk, - * or if the number of chunks that the current softwrapped line occupies - * changed, we need a full refresh. */ - if (ISSET(SOFTWRAP) && ((openfile->current_y == editwinrows - 1 && - chunk_for(xplustabs(), openfile->current) > original_row) || - extra_chunks_in(openfile->current) != old_amount)) { - renumber_from(openfile->current); - refresh_needed = TRUE; - focusing = FALSE; + /* When softwrapping and the number of chunks in the current line changed, + * or we were on the last row of the edit window and moved to a new chunk, + * we need a full refresh. */ + if (ISSET(SOFTWRAP)) { + openfile->current->extrarows = extra_chunks_in(openfile->current); + if (openfile->current->extrarows != old_amount || + (openfile->current_y == editwinrows - 1 && + chunk_for(xplustabs(), openfile->current) > original_row)) { + refresh_needed = TRUE; + focusing = FALSE; + } } #endif diff --git a/src/nano.h b/src/nano.h index b14296e1..7df938ff 100644 --- a/src/nano.h +++ b/src/nano.h @@ -285,8 +285,8 @@ typedef struct linestruct { ssize_t lineno; /* The number of this line. */ #ifndef NANO_TINY - ssize_t chunk_nr; - /* The ordinal number of the first chunk in this line. */ + ssize_t extrarows; + /* The extra rows that this line occupies when softwrapping. */ #endif struct linestruct *next; /* Next node. */ diff --git a/src/proto.h b/src/proto.h index fc0e3272..d59be1b8 100644 --- a/src/proto.h +++ b/src/proto.h @@ -283,6 +283,7 @@ void make_new_buffer(void); #ifndef NANO_TINY bool delete_lockfile(const char *lockfilename); #endif +void compute_the_extra_rows_per_line(void); bool open_buffer(const char *filename, bool new_buffer); void set_modified(void); void prepare_for_display(void); diff --git a/src/search.c b/src/search.c index 0570a482..6ffa9693 100644 --- a/src/search.c +++ b/src/search.c @@ -654,6 +654,10 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only, free(openfile->current->data); openfile->current->data = copy; +#ifndef NANO_TINY + if (ISSET(SOFTWRAP)) + openfile->current->extrarows = extra_chunks_in(openfile->current); +#endif #ifdef ENABLE_COLOR /* Check whether the replacement requires a change in the coloring. */ check_the_multis(openfile->current); diff --git a/src/text.c b/src/text.c index adab2631..6f5feae8 100644 --- a/src/text.c +++ b/src/text.c @@ -102,6 +102,9 @@ void indent_a_line(linestruct *line, char *indentation) openfile->totsize += indent_len; + if (ISSET(SOFTWRAP)) + line->extrarows = extra_chunks_in(line); + /* Compensate for the change in the current line. */ if (line == openfile->mark && openfile->mark_x > 0) openfile->mark_x += indent_len; @@ -230,6 +233,9 @@ void unindent_a_line(linestruct *line, size_t indent_len) openfile->totsize -= indent_len; + if (ISSET(SOFTWRAP)) + line->extrarows = extra_chunks_in(line); + /* Adjust the positions of mark and cursor, when they are affected. */ compensate_leftward(line, indent_len); } @@ -418,8 +424,13 @@ void do_comment(void) /* Comment/uncomment each of the selected lines when possible, and * store undo data when a line changed. */ for (line = top; line != bot->next; line = line->next) { - if (comment_line(action, line, comment_seq)) + if (comment_line(action, line, comment_seq)) { +#ifndef NANO_TINY + if (ISSET(SOFTWRAP)) + line->extrarows = extra_chunks_in(line); +#endif update_multiline_undo(line->lineno, ""); + } } set_modified(); @@ -660,6 +671,9 @@ void do_undo(void) openfile->mark = NULL; openfile->placewewant = xplustabs(); + if (ISSET(SOFTWRAP)) + openfile->current->extrarows = extra_chunks_in(openfile->current); + openfile->totsize = u->wassize; /* When at the point where the file was last saved, unset "Modified". */ @@ -818,6 +832,9 @@ void do_redo(void) openfile->mark = NULL; openfile->placewewant = xplustabs(); + if (ISSET(SOFTWRAP)) + openfile->current->extrarows = extra_chunks_in(openfile->current); + openfile->totsize = u->newsize; /* When at the point where the file was last saved, unset "Modified". */ diff --git a/src/winio.c b/src/winio.c index af25a144..376ae5a1 100644 --- a/src/winio.c +++ b/src/winio.c @@ -2957,10 +2957,12 @@ void draw_scrollbar(void) int first_row = openfile->edittop->lineno; if (ISSET(SOFTWRAP)) { - totalrows = openfile->filebot->chunk_nr + - extra_chunks_in(openfile->filebot); - first_row = openfile->edittop->chunk_nr + - chunk_for(openfile->firstcolumn, openfile->edittop); + for (linestruct *ln = openfile->filetop; ln != openfile->edittop; ln = ln->next) + first_row += ln->extrarows; + first_row += chunk_for(openfile->firstcolumn, openfile->edittop); + + for (linestruct *ln = openfile->filetop; ln != NULL; ln = ln->next) + totalrows += ln->extrarows; } int lowest = ((first_row - 1) * editwinrows) / totalrows; @@ -3142,11 +3144,10 @@ size_t leftedge_for(size_t column, linestruct *line) * has changed, because then the width of softwrapped chunks has changed. */ void ensure_firstcolumn_is_aligned(void) { - if (ISSET(SOFTWRAP)) { + if (ISSET(SOFTWRAP)) openfile->firstcolumn = leftedge_for(openfile->firstcolumn, openfile->edittop); - renumber_from(openfile->filetop); - } else + else openfile->firstcolumn = 0; /* If smooth scrolling is on, make sure the viewport doesn't center. */