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.
This commit is contained in:
Benno Schulenberg 2022-03-15 17:25:41 +01:00
parent 51bd04b541
commit 15a0a129c8
9 changed files with 73 additions and 53 deletions

View File

@ -280,6 +280,7 @@ void check_the_multis(linestruct *line)
/* There is a mismatch, so something changed: repaint. */
refresh_needed = TRUE;
perturbed = TRUE;
return;
}
}

View File

@ -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. */

View File

@ -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)

View File

@ -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;

View File

@ -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. */

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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)
} else
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))
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. */