commit 15a0a129c8a02ccae84613ef2c5935c0635b35e0
parent 51bd04b54143a1c215a3ae2dbc86ac33167cb70c
Author: Benno Schulenberg <bensberg@telfort.nl>
Date: Tue, 15 Mar 2022 17:25:41 +0100
painting: recalculate the multidata when making large strides or changes
When making a forward movement larger than a screenful, we cannot rely
on the multidata of the line before the new screen start to have been
set correctly by a previous screen drawing, so we need to recompute all
of the multidata, for the whole buffer, so that afterward we can freely
move around and draw the screen without having to do any backtracking.
Also when a piece of text larger than a screenful is pasted or inserted,
all the multidata needs to be recomputed.
This fixes https://savannah.gnu.org/bugs/?60041,
and fixes https://savannah.gnu.org/bugs/?62056.
First bug existed in this form since version 2.4.2, commit d49c267f
(but editing Python was incomparably slower in those days).
Second bug existed since version 5.6, commit 43d94692.
Diffstat:
9 files changed, 73 insertions(+), 53 deletions(-)
diff --git a/src/color.c b/src/color.c
@@ -280,6 +280,7 @@ void check_the_multis(linestruct *line)
/* There is a mismatch, so something changed: repaint. */
refresh_needed = TRUE;
+ perturbed = TRUE;
return;
}
}
diff --git a/src/cut.c b/src/cut.c
@@ -435,9 +435,19 @@ void ingraft_buffer(linestruct *topline)
/* Meld a copy of the given buffer into the current file buffer. */
void copy_from_buffer(linestruct *somebuffer)
{
+#ifdef ENABLE_COLOR
+ size_t threshold = openfile->edittop->lineno + editwinrows - 1;
+#endif
linestruct *the_copy = copy_buffer(somebuffer);
ingraft_buffer(the_copy);
+
+#ifdef ENABLE_COLOR
+ if (openfile->current->lineno > threshold || ISSET(SOFTWRAP))
+ recook = TRUE;
+ else
+ perturbed = TRUE;
+#endif
}
#ifndef NANO_TINY
@@ -509,6 +519,9 @@ void do_snip(bool marked, bool until_eof, bool append)
set_modified();
refresh_needed = TRUE;
+#ifdef ENABLE_COLOR
+ perturbed = TRUE;
+#endif
}
/* Move text from the current buffer into the cutbuffer. */
diff --git a/src/files.c b/src/files.c
@@ -836,12 +836,14 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable)
report_size = TRUE;
/* If we inserted less than a screenful, don't center the cursor. */
- if (undoable && less_than_a_screenful(was_lineno, was_leftedge))
+ if (undoable && less_than_a_screenful(was_lineno, was_leftedge)) {
focusing = FALSE;
+ perturbed = TRUE;
#ifdef ENABLE_COLOR
- else if (undoable)
- precalc_multicolorinfo();
+ } else if (undoable) {
+ recook = TRUE;
#endif
+ }
#ifndef NANO_TINY
if (undoable)
diff --git a/src/global.c b/src/global.c
@@ -203,6 +203,10 @@ bool have_palette = FALSE;
/* Whether the colors for the current syntax have been initialized. */
bool rescind_colors = FALSE;
/* Becomes TRUE when NO_COLOR is set in the environment. */
+bool perturbed = FALSE;
+ /* Whether the multiline-coloring situation has changed. */
+bool recook = FALSE;
+ /* Whether the multidata should be recalculated. */
#endif
int currmenu = MMOST;
diff --git a/src/move.c b/src/move.c
@@ -44,6 +44,9 @@ void to_last_line(void)
openfile->current_y = editwinrows - 1;
refresh_needed = TRUE;
+#ifdef ENABLE_COLOR
+ recook |= perturbed;
+#endif
focusing = FALSE;
}
@@ -222,6 +225,9 @@ void to_para_end(void)
openfile->current_x = strlen(openfile->current->data);
edit_redraw(was_current, CENTERING);
+#ifdef ENABLE_COLOR
+ recook |= perturbed;
+#endif
}
#endif /* ENABLE_JUSTIFY */
@@ -263,6 +269,9 @@ void to_next_block(void)
openfile->current_x = 0;
edit_redraw(was_current, CENTERING);
+#ifdef ENABLE_COLOR
+ recook |= perturbed;
+#endif
}
/* Move to the previous word. */
diff --git a/src/prototypes.h b/src/prototypes.h
@@ -142,6 +142,8 @@ extern syntaxtype *syntaxes;
extern char *syntaxstr;
extern bool have_palette;
extern bool rescind_colors;
+extern bool perturbed;
+extern bool recook;
#endif
extern bool refresh_needed;
diff --git a/src/search.c b/src/search.c
@@ -65,6 +65,9 @@ void tidy_up_after_search(void)
if (openfile->mark)
refresh_needed = TRUE;
#endif
+#ifdef ENABLE_COLOR
+ recook |= perturbed;
+#endif
}
/* Prepare the prompt and ask the user what to search for. Keep looping
@@ -657,6 +660,10 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only,
free(openfile->current->data);
openfile->current->data = altered;
+#ifdef ENABLE_COLOR
+ check_the_multis(openfile->current);
+ refresh_needed = FALSE;
+#endif
set_modified();
as_an_at = TRUE;
numreplaced++;
@@ -790,6 +797,11 @@ void goto_line_and_column(ssize_t line, ssize_t column, bool retain_answer,
if (line < 1)
line = 1;
+#ifdef ENABLE_COLOR
+ if (line > openfile->edittop->lineno + editwinrows - 1 || ISSET(SOFTWRAP))
+ recook |= perturbed;
+#endif
+
/* Iterate to the requested line. */
for (openfile->current = openfile->filetop; line > 1 &&
openfile->current != openfile->filebot; line--)
@@ -994,6 +1006,10 @@ void go_to_and_confirm(linestruct *line)
openfile->current_x = 0;
edit_redraw(was_current, CENTERING);
statusbar(_("Jumped to anchor"));
+#ifdef ENABLE_COLOR
+ if (line->lineno > was_current->lineno)
+ recook |= perturbed;
+#endif
} else if (openfile->current->has_anchor)
statusline(REMARK, _("This is the only anchor"));
else
diff --git a/src/text.c b/src/text.c
@@ -665,6 +665,13 @@ void do_undo(void)
openfile->totsize = u->wassize;
+#ifdef ENABLE_COLOR
+ if (u->type <= REPLACE)
+ check_the_multis(openfile->current);
+ else if (u->type == INSERT)
+ perturbed = TRUE;
+#endif
+
/* When at the point where the buffer was last saved, unset "Modified". */
if (openfile->current_undo == openfile->last_saved) {
openfile->modified = FALSE;
@@ -825,6 +832,13 @@ void do_redo(void)
openfile->totsize = u->newsize;
+#ifdef ENABLE_COLOR
+ if (u->type <= REPLACE)
+ check_the_multis(openfile->current);
+ else if (u->type == INSERT)
+ recook = TRUE;
+#endif
+
/* When at the point where the buffer was last saved, unset "Modified". */
if (openfile->current_undo == openfile->last_saved) {
openfile->modified = FALSE;
diff --git a/src/winio.c b/src/winio.c
@@ -2596,9 +2596,9 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
/* Assume nothing gets painted until proven otherwise below. */
line->multidata[varnish->id] = NOTHING;
- /* Apart from the first row, check the multidata of the preceding line:
+ /* Check the multidata of the preceding line:
* it tells us about the situation so far, and thus what to do here. */
- if (row > 0 && start_line != NULL && start_line->multidata != NULL) {
+ if (start_line != NULL && start_line->multidata != NULL) {
if (start_line->multidata[varnish->id] == WHOLELINE ||
start_line->multidata[varnish->id] == STARTSHERE)
goto seek_an_end;
@@ -2606,56 +2606,9 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
start_line->multidata[varnish->id] == ENDSHERE ||
start_line->multidata[varnish->id] == JUSTONTHIS)
goto step_two;
- }
-
- /* The preceding line has no precalculated multidata.
- * So, do some backtracking to find out what to paint. */
-
- /* First step: see if there is a line before current that
- * matches 'start' and is not complemented by an 'end'. */
- while (start_line != NULL && regexec(varnish->start,
- start_line->data, 1, &startmatch, 0) == REG_NOMATCH) {
- /* There is no start on this line; but if there is an end,
- * there is no need to look for starts on earlier lines. */
- if (regexec(varnish->end, start_line->data, 0, NULL, 0) == 0)
- goto step_two;
- start_line = start_line->prev;
- }
-
- /* If no start was found, skip to the next step. */
- if (start_line == NULL)
- goto step_two;
-
- /* If the start has been qualified as an end earlier, believe it. */
- if (start_line->multidata != NULL &&
- (start_line->multidata[varnish->id] == ENDSHERE ||
- start_line->multidata[varnish->id] == JUSTONTHIS))
+ } else
goto step_two;
- /* Maybe there is an end on that same line? If yes, maybe
- * there is another start after it? And so on, until EOL. */
- while (TRUE) {
- /* Begin searching for an end after the start match. */
- index += startmatch.rm_eo;
- /* If there is no end after this last start, good. */
- if (regexec(varnish->end, start_line->data + index, 1, &endmatch,
- (index == 0) ? 0 : REG_NOTBOL) == REG_NOMATCH)
- break;
- /* Begin searching for a new start after the end match. */
- index += endmatch.rm_eo;
- /* If both start and end match are mere anchors, advance. */
- if (startmatch.rm_so == startmatch.rm_eo &&
- endmatch.rm_so == endmatch.rm_eo) {
- if (start_line->data[index] == '\0')
- goto step_two;
- index = step_right(start_line->data, index);
- }
- /* If there is no later start on this line, next step. */
- if (regexec(varnish->start, start_line->data + index,
- 1, &startmatch, REG_NOTBOL) == REG_NOMATCH)
- goto step_two;
- }
-
seek_an_end:
/* If there is no end on this line, paint whole line, and be done. */
if (regexec(varnish->end, line->data, 1, &endmatch, 0) == REG_NOMATCH) {
@@ -3356,6 +3309,12 @@ void edit_refresh(void)
/* When needed and useful, initialize the colors for the current syntax. */
if (openfile->syntax && !have_palette && !ISSET(NO_SYNTAX) && has_colors())
prepare_palette();
+
+ if (recook) {
+ precalc_multicolorinfo();
+ perturbed = FALSE;
+ recook = FALSE;
+ }
#endif
/* If the current line is out of view, get it back on screen. */