to do_verbatim_input(), handle_sigwinch(), get_kbinput(),
get_ascii_kbinput(), get_escape_seq_kbinput(), and
get_verbatim_kbinput(). (DLR)
+ - Overhaul all the movement functions in order to avoid
+ redundant screen redraws (and regexec()s when color support is
+ available) whenever possible during ordinary cursor movement
+ when the text doesn't change. Do the same for moving in
+ do_char(), do_delete(), do_next_word(), do_prev_word(),
+ do_search(), do_research(), and do_replace_loop. Changes to
+ do_first_line(), do_last_line(), do_home(), do_end(),
+ do_page_up(), do_page_down(), do_up(), do_down(), do_left(),
+ do_right(), do_delete(), do_backspace(), do_search(),
+ do_research(), do_replace_loop(), do_find_bracket(), and
+ edit_refresh(). New functions do_left_void(),
+ do_right_void(), need_horizontal_update(),
+ need_vertical_update(), edit_scroll(), and edit_redraw().
+ Also rename the int refresh in do_delete() and do_backspace()
+ to do_refresh so as not to conflict with refresh(). (DLR)
- files.c:
add_open_file()
- Rearrange the NANO_SMALL #ifdef so that the code to set the
- Since REGEXP_COMPILED is only used in search.c, convert it
from a flag to a static int there. (DLR)
- Add justbegend enum, used in do_para_search(). (DLR)
+ - Add updown enum, used in edit_scroll(). (DLR)
- proto.h:
- Remove unused xpt() and add_marked_sameline() prototypes.
(DLR)
sc_init_one(&main_list, NANO_FORWARD_KEY, _("Forward"),
IFHELP(nano_forward_msg, NANO_NO_KEY), NANO_NO_KEY,
- NANO_NO_KEY, VIEW, do_right);
+ NANO_NO_KEY, VIEW, do_right_void);
sc_init_one(&main_list, NANO_BACK_KEY, _("Back"),
IFHELP(nano_back_msg, NANO_NO_KEY), NANO_NO_KEY,
- NANO_NO_KEY, VIEW, do_left);
+ NANO_NO_KEY, VIEW, do_left_void);
sc_init_one(&main_list, NANO_HOME_KEY, _("Home"),
IFHELP(nano_home_msg, NANO_NO_KEY), NANO_NO_KEY,
int do_first_line(void)
{
+ int old_pww = placewewant;
current = fileage;
placewewant = 0;
current_x = 0;
- edit_update(current, TOP);
+ if (edittop != fileage || need_vertical_update(old_pww))
+ edit_update(current, TOP);
return 1;
}
int do_last_line(void)
{
+ int old_pww = placewewant;
current = filebot;
placewewant = 0;
current_x = 0;
- edit_update(current, CENTER);
+ if (edittop->lineno + (editwinrows / 2) != filebot->lineno ||
+ need_vertical_update(old_pww))
+ edit_update(current, CENTER);
return 1;
}
int do_home(void)
{
+ int old_pww = placewewant;
#ifndef NANO_SMALL
if (ISSET(SMART_HOME)) {
int old_current_x = current_x;
}
#endif
check_statblank();
- update_line(current, current_x);
+ if (need_horizontal_update(old_pww))
+ update_line(current, current_x);
return 1;
}
int do_end(void)
{
+ int old_pww = placewewant;
current_x = strlen(current->data);
placewewant = xplustabs();
check_statblank();
- update_line(current, current_x);
+ if (need_horizontal_update(old_pww))
+ update_line(current, current_x);
return 1;
}
int do_page_up(void)
{
- int i;
-
+ int new_pww = placewewant;
+ const filestruct *old_current = current;
#ifndef DISABLE_WRAPPING
wrap_reset();
#endif
- /* If edittop is the first line of the file, move current up there
+ /* If the first line of the file is onscreen, move current up there
* and put the cursor at the beginning of the line. */
if (edittop == fileage) {
current = fileage;
- placewewant = 0;
+ new_pww = 0;
} else {
- /* Move the top line of the edit window up a page. */
- for (i = 0; i < editwinrows - 2 && edittop->prev != NULL; i++)
- edittop = edittop->prev;
+ edit_scroll(UP, editwinrows - 2);
+
#ifndef NANO_SMALL
- /* If we're in smooth scrolling mode and there was at least one
+ /* If we're in smooth scrolling mode and there's at least one
* page of text left, move the current line of the edit window
* up a page. */
- if (ISSET(SMOOTHSCROLL) && current->lineno > editwinrows - 2)
+ if (ISSET(SMOOTHSCROLL) && current->lineno > editwinrows - 2) {
+ int i;
for (i = 0; i < editwinrows - 2; i++)
current = current->prev;
- /* If we're not in smooth scrolling mode and there was at least
+ }
+ /* If we're not in smooth scrolling mode or there isn't at least
* one page of text left, put the cursor at the beginning of the
* top line of the edit window, as Pico does. */
else {
#endif
current = edittop;
- placewewant = 0;
+ new_pww = 0;
#ifndef NANO_SMALL
}
#endif
}
+
/* Get the equivalent x-coordinate of the new line. */
- current_x = actual_x(current->data, placewewant);
+ current_x = actual_x(current->data, new_pww);
- edit_refresh();
+ /* Update all the lines that need to be updated, and then set
+ * placewewant, so that the update will work properly. */
+ edit_redraw(old_current);
+ placewewant = new_pww;
check_statblank();
return 1;
int do_page_down(void)
{
- int i;
-
+ int new_pww = placewewant;
+ const filestruct *old_current = current;
#ifndef DISABLE_WRAPPING
wrap_reset();
#endif
* there and put the cursor at the beginning of the line. */
if (edittop->lineno + editwinrows > filebot->lineno) {
current = filebot;
- placewewant = 0;
+ new_pww = 0;
} else {
- /* Move the top line of the edit window down a page. */
- for (i = 0; i < editwinrows - 2; i++)
- edittop = edittop->next;
+ edit_scroll(DOWN, editwinrows - 2);
+
#ifndef NANO_SMALL
- /* If we're in smooth scrolling mode and there was at least one
+ /* If we're in smooth scrolling mode and there's at least one
* page of text left, move the current line of the edit window
* down a page. */
- if (ISSET(SMOOTHSCROLL) && current->lineno + editwinrows - 2 <= filebot->lineno)
+ if (ISSET(SMOOTHSCROLL) && current->lineno + editwinrows - 2 <=
+ filebot->lineno) {
+ int i;
for (i = 0; i < editwinrows - 2; i++)
current = current->next;
- /* If we're not in smooth scrolling mode and there was at least
+ }
+ /* If we're not in smooth scrolling mode or there isn't at least
* one page of text left, put the cursor at the beginning of the
* top line of the edit window, as Pico does. */
else {
#endif
current = edittop;
- placewewant = 0;
+ new_pww = 0;
#ifndef NANO_SMALL
}
#endif
}
+
/* Get the equivalent x-coordinate of the new line. */
- current_x = actual_x(current->data, placewewant);
+ current_x = actual_x(current->data, new_pww);
- edit_refresh();
+ /* Update all the lines that need to be updated, and then set
+ * placewewant, so that the update will work properly. */
+ edit_redraw(old_current);
+ placewewant = new_pww;
check_statblank();
return 1;
assert(current_y == current->lineno - edittop->lineno);
current = current->prev;
current_x = actual_x(current->data, placewewant);
- if (current_y > 0) {
- update_line(current->next, 0);
- /* It was necessary to change current first, so that the
- * mark display will change! */
- update_line(current, current_x);
- } else
+
+ /* If we're on the first row of the edit window, scroll up one line
+ * if we're in smooth scrolling mode, or up half a page if we're
+ * not. */
+ if (current_y == 0)
+ edit_scroll(UP,
#ifndef NANO_SMALL
- if (ISSET(SMOOTHSCROLL))
- edit_update(current, TOP);
- else
+ ISSET(SMOOTHSCROLL) ? 1 :
#endif
- edit_update(current, CENTER);
+ editwinrows / 2);
+
+ /* Update the lines left alone by edit_scroll(): the line we were on
+ * before and the line we're on now. The former needs to be redrawn
+ * if we're not on the first page, and the latter needs to be
+ * drawn. */
+ if (need_vertical_update(0))
+ update_line(current->next, 0);
+ update_line(current, current_x);
+
return 1;
}
current = current->next;
current_x = actual_x(current->data, placewewant);
- /* Note that current_y is zero-based. This test checks for the
- * cursor's being not on the last row of the edit window. */
- if (current_y != editwinrows - 1) {
- update_line(current->prev, 0);
- update_line(current, current_x);
- } else
+ /* If we're on the last row of the edit window, scroll down one line
+ * if we're in smooth scrolling mode, or down half a page if we're
+ * not. */
+ if (current_y == editwinrows - 1)
+ edit_scroll(DOWN,
#ifndef NANO_SMALL
- if (ISSET(SMOOTHSCROLL))
- /* In this case current_y does not change. The cursor remains
- * at the bottom of the edit window. */
- edit_update(edittop->next, TOP);
- else
+ ISSET(SMOOTHSCROLL) ? 1 :
#endif
- edit_update(current, CENTER);
+ editwinrows / 2);
+
+ /* Update the lines left alone by edit_scroll(): the line we were on
+ * before and the line we're on now. The former needs to be redrawn
+ * if we're not on the first page, and the latter needs to be
+ * drawn. */
+ if (need_vertical_update(0))
+ update_line(current->prev, 0);
+ update_line(current, current_x);
+
return 1;
}
-int do_left(void)
+int do_left(int allow_update)
{
+ int old_pww = placewewant;
if (current_x > 0)
current_x--;
else if (current != fileage) {
}
placewewant = xplustabs();
check_statblank();
- update_line(current, current_x);
+ if (allow_update && need_horizontal_update(old_pww))
+ update_line(current, current_x);
return 1;
}
-int do_right(void)
+int do_left_void(void)
+{
+ return do_left(TRUE);
+}
+
+int do_right(int allow_update)
{
+ int old_pww = placewewant;
assert(current_x <= strlen(current->data));
if (current->data[current_x] != '\0')
}
placewewant = xplustabs();
check_statblank();
- update_line(current, current_x);
+ if (allow_update && need_horizontal_update(old_pww))
+ update_line(current, current_x);
return 1;
}
+
+int do_right_void(void)
+{
+ return do_right(TRUE);
+}
{
size_t current_len = strlen(current->data);
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
- int refresh = FALSE;
- /* Do we have to run edit_refresh(), or can we get away with
+ int do_refresh = FALSE;
+ /* Do we have to call edit_refresh(), or can we get away with
* update_line()? */
#endif
mark_beginx++;
#endif
- do_right();
+ do_right(FALSE);
#ifndef DISABLE_WRAPPING
+ /* If we're wrapping text, we need to call edit_refresh(). */
if (!ISSET(NO_WRAP) && ch != '\t')
- refresh = do_wrap(current);
+ do_refresh = do_wrap(current);
#endif
#ifdef ENABLE_COLOR
+ /* If color syntaxes are turned on, we need to call
+ * edit_refresh(). */
if (ISSET(COLOR_SYNTAX))
- refresh = TRUE;
+ do_refresh = TRUE;
#endif
#if !defined(DISABLE_WRAPPING) || defined(ENABLE_COLOR)
- if (refresh)
+ if (do_refresh)
edit_refresh();
+ else
#endif
+ update_line(current, current_x);
}
int do_verbatim_input(void)
int do_backspace(void)
{
if (current != fileage || current_x > 0) {
- do_left();
+ do_left(FALSE);
do_delete();
}
return 1;
int do_delete(void)
{
+ int do_refresh = FALSE;
+ /* Do we have to call edit_refresh(), or can we get away with
+ * update_line()? */
+
assert(current != NULL && current->data != NULL && current_x <=
strlen(current->data));
filestruct *foo = current->next;
assert(current_x == strlen(current->data));
+
+ /* If we're deleting at the end of a line, we need to call
+ * edit_refresh(). */
+ if (current->data[current_x] == '\0')
+ do_refresh = TRUE;
+
current->data = charealloc(current->data, current_x +
strlen(foo->data) + 1);
strcpy(current->data + current_x, foo->data);
totsize--;
set_modified();
- edit_refresh();
+
+#ifdef ENABLE_COLOR
+ /* If color syntaxes are turned on, we need to call
+ * edit_refresh(). */
+ if (ISSET(COLOR_SYNTAX))
+ do_refresh = TRUE;
+#endif
+
+ if (do_refresh)
+ edit_refresh();
+ else
+ update_line(current, current_x);
+
return 1;
}
#ifndef NANO_SMALL
int do_next_word(void)
{
+ const filestruct *current_save = current;
assert(current != NULL && current->data != NULL);
/* Skip letters in this word first. */
/* Refresh the screen. If current has run off the bottom, this
* call puts it at the center line. */
- edit_refresh();
+ edit_redraw(current_save);
return 0;
}
/* The same thing for backwards. */
int do_prev_word(void)
{
+ const filestruct *current_save = current;
assert(current != NULL && current->data != NULL);
/* Skip letters in this word first. */
/* Refresh the screen. If current has run off the top, this call
* puts it at the center line. */
- edit_refresh();
+ edit_redraw(current_save);
return 0;
}
{
char *save_search;
char *save_replace;
- filestruct *current_save = current;
size_t current_x_save = current_x;
+ filestruct *current_save = current;
filestruct *edittop_save = edittop;
/* Save where we are. */
int i = 0;
int do_para_search(justbegend search_type, size_t *quote, size_t *par,
size_t *indent, int do_refresh)
{
+ const filestruct *current_save = current;
size_t quote_len;
/* Length of the initial quotation of the paragraph we
* search. */
size_t par_len;
/* Number of lines in that paragraph. */
-
- /* We save this global variable to see if we're where we started
- * when searching for the beginning of the paragraph. */
- filestruct *current_save = current;
-
- size_t indent_len; /* Generic indentation length. */
- filestruct *line; /* Generic line of text. */
-
+ size_t indent_len;
+ /* Generic indentation length. */
+ filestruct *line;
+ /* Generic line of text. */
static int do_restart = 1;
/* Whether we're restarting when searching for the beginning
* line of the paragraph. */
if (current->prev == NULL) {
placewewant = 0;
if (do_refresh)
- edit_refresh();
+ edit_redraw(current_save);
#ifdef HAVE_REGEX_H
if (!do_restart)
regfree(&qreg);
if (current->next == NULL) {
placewewant = 0;
if (do_refresh)
- edit_refresh();
+ edit_redraw(current_save);
#ifdef HAVE_REGEX_H
regfree(&qreg);
#endif
/* Refresh the screen if needed. */
if (do_refresh)
- edit_refresh();
+ edit_redraw(current_save);
/* Save the values of quote_len, par_len, and indent_len if
* needed. */
* unjustifies. Note we don't need to save totlines. */
int current_x_save = current_x;
int current_y_save = current_y;
- filestruct *current_save = current;
int flags_save = flags;
long totsize_save = totsize;
+ filestruct *current_save = current;
filestruct *edittop_save = edittop;
#ifndef NANO_SMALL
filestruct *mark_beginbuf_save = mark_beginbuf;
JUSTIFY, BEGIN, END
} justbegend;
+typedef enum {
+ UP, DOWN
+} updown;
+
typedef enum {
TOP, CENTER, NONE
} topmidnone;
int do_page_down(void);
int do_up(void);
int do_down(void);
-int do_left(void);
-int do_right(void);
+int do_left(int allow_update);
+int do_left_void(void);
+int do_right(int allow_update);
+int do_right_void(void);
/* Public functions in nano.c */
void finish(void);
void edit_add(const filestruct *fileptr, const char *converted, int
yval, size_t start);
void update_line(const filestruct *fileptr, size_t index);
+int need_horizontal_update(int old_placewewant);
+int need_vertical_update(int old_placewewant);
+void edit_scroll(updown direction, int nlines);
+void edit_redraw(const filestruct *old_current);
void edit_refresh(void);
void edit_update(filestruct *fileptr, topmidnone location);
int statusq(int allowtabs, const shortcut *s, const char *def,
/* Search for a string. */
int do_search(void)
{
- int i;
+ int i, fileptr_x = current_x, didfind;
filestruct *fileptr = current;
- int fileptr_x = current_x, didfind;
#ifndef DISABLE_WRAPPING
wrap_reset();
search_last_line = FALSE;
didfind = findnextstr(TRUE, FALSE, current, current_x, answer, FALSE);
- edit_refresh();
- placewewant = xplustabs();
/* Check to see if there's only one occurrence of the string and
* we're on it now. */
#endif
}
+ edit_redraw(fileptr);
+ placewewant = xplustabs();
search_abort();
return 1;
/* Search for the next string without prompting. */
int do_research(void)
{
- filestruct *fileptr = current;
int fileptr_x = current_x, didfind;
+ filestruct *fileptr = current;
#ifndef DISABLE_WRAPPING
wrap_reset();
search_last_line = FALSE;
didfind = findnextstr(TRUE, FALSE, current, current_x, last_search, FALSE);
- edit_refresh();
- placewewant = xplustabs();
/* Check to see if there's only one occurrence of the string and
* we're on it now. */
} else
statusbar(_("No current search pattern"));
+ edit_redraw(fileptr);
+ placewewant = xplustabs();
search_abort();
return 1;
size_t *real_current_x, int wholewords)
{
int replaceall = 0, numreplaced = -1;
- const filestruct *current_save = current;
size_t current_x_save = current_x;
+ const filestruct *current_save = current;
#ifdef HAVE_REGEX_H
/* The starting-line match and bol/eol regex flags. */
int begin_line = FALSE, bol_or_eol = FALSE;
}
#endif
- edit_refresh();
+ if (!replaceall)
+ edit_redraw(current_save);
#ifdef HAVE_REGEX_H
if (ISSET(USE_REGEXP))
free(current->data);
current->data = copy;
- edit_refresh();
+ if (!replaceall) {
+#ifdef ENABLE_COLOR
+ if (ISSET(COLOR_SYNTAX))
+ edit_refresh();
+ else
+#endif
+ update_line(current, current_x);
+ }
+
set_modified();
numreplaced++;
}
char ch_under_cursor, wanted_ch;
const char *pos, *brackets = "([{<>}])";
char regexp_pat[] = "[ ]";
- int flagsave, current_x_save, count = 1;
+ int current_x_save, flagsave, count = 1;
filestruct *current_save;
ch_under_cursor = current->data[current_x];
count++;
/* Found complementary bracket. */
else if (--count == 0) {
- edit_refresh();
+ edit_redraw(current_save);
placewewant = xplustabs();
break;
}
mvwaddch(edit, line, COLS - 1, '$');
}
+/* Return a nonzero value if we need an update after moving
+ * horizontally. We need one if the mark is on or if old_pww and
+ * placewewant are on different pages. Assume none of the text has
+ * changed since the last update. */
+int need_horizontal_update(int old_pww)
+{
+ return
+#ifndef NANO_SMALL
+ ISSET(MARK_ISSET) ||
+#endif
+ get_page_start(old_pww) != get_page_start(placewewant);
+}
+
+/* Return a nonzero value if we need an update after moving vertically.
+ * We need one if the mark is on or if old_pww and placewewant
+ * are on different pages. Assume none of the text has changed since
+ * the last update. */
+int need_vertical_update(int old_pww)
+{
+ return
+#ifndef NANO_SMALL
+ ISSET(MARK_ISSET) ||
+#endif
+ get_page_start(old_pww) != get_page_start(placewewant);
+}
+
+/* Scroll the edit window in the given direction and the given number
+ * of lines, and draw new lines on the blank lines left after the
+ * scrolling. direction is the direction to scroll, either UP or DOWN,
+ * and nlines is the number of lines to scroll. Don't redraw the old
+ * topmost or bottommost line (where we assume current is) before
+ * scrolling or draw the new topmost or bottommost line after scrolling
+ * (where we assume current will be), since we don't know where we are
+ * on the page or whether we'll stay there. Assume none of the text has
+ * changed since the last update. */
+void edit_scroll(updown direction, int nlines)
+{
+ filestruct *foo;
+ int i, scroll_rows = 0;
+
+ /* Scrolling less than one line or more than editwinrows lines is
+ * redundant, so don't allow it. */
+ if (nlines < 1 || nlines > editwinrows)
+ return;
+
+ /* Move the top line of the edit window up or down (depending on the
+ * value of direction) nlines lines. If there are fewer lines of
+ * text than that left, move it to the top or bottom line of the
+ * file (depending on the value of direction). Keep track of
+ * how many lines we moved in scroll_rows. */
+ for (i = nlines; i > 0; i--) {
+ if (direction == UP) {
+ if (edittop->prev == NULL)
+ break;
+ edittop = edittop->prev;
+ scroll_rows--;
+ } else {
+ if (edittop->next == NULL)
+ break;
+ edittop = edittop->next;
+ scroll_rows++;
+ }
+ }
+
+ /* Scroll the text on the screen up or down scroll_rows lines,
+ * depending on the value of direction. */
+ scrollok(edit, TRUE);
+ wscrl(edit, scroll_rows);
+ scrollok(edit, FALSE);
+
+ foo = edittop;
+ if (direction != UP) {
+ int slines = editwinrows - nlines;
+ for (; slines > 0 && foo != NULL; slines--)
+ foo = foo->next;
+ }
+
+ /* And draw new lines on the blank top or bottom lines of the edit
+ * window, depending on the value of direction. Don't draw the new
+ * topmost or new bottommost line. */
+ while (scroll_rows != 0 && foo != NULL) {
+ if (foo->next != NULL)
+ update_line(foo, 0);
+ if (direction == UP)
+ scroll_rows++;
+ else
+ scroll_rows--;
+ foo = foo->next;
+ }
+}
+
+/* Update any lines between old_current and current that need to be
+ * updated. Note that we use placewewant to determine whether we need
+ * updates and current_x to update current, so if placewewant needs to
+ * be changed, it should be changed after calling this, and if current_x
+ * needs to be changed, it should be changed before calling this.
+ * Assume none of the text has changed since the last update. */
+void edit_redraw(const filestruct *old_current)
+{
+ int do_refresh = need_vertical_update(0);
+ const filestruct *foo;
+
+ /* If either old_current or current is offscreen, refresh the screen
+ * and get out. */
+ if (old_current->lineno < edittop->lineno || old_current->lineno >=
+ edittop->lineno + editwinrows || current->lineno <
+ edittop->lineno || current->lineno >= edittop->lineno +
+ editwinrows) {
+ edit_refresh();
+ return;
+ }
+
+ /* Update old_current and current if we're not on the first page.
+ * If the mark is on, update all the lines between old_current and
+ * current too. */
+ foo = old_current;
+ while (foo != current) {
+ if (do_refresh)
+ update_line(foo, 0);
+#ifndef NANO_SMALL
+ if (!ISSET(MARK_ISSET))
+#endif
+ break;
+ if (foo->lineno > current->lineno)
+ foo = foo->prev;
+ else
+ foo = foo->next;
+ }
+ if (do_refresh)
+ update_line(current, current_x);
+}
+
/* Refresh the screen without changing the position of lines. */
void edit_refresh(void)
{
#endif
while (nlines < editwinrows) {
- update_line(foo, current_x);
+ update_line(foo, (foo == current) ? current_x : 0);
nlines++;
if (foo->next == NULL)
break;