new default feature: smooth whole-window sideways scrolling

Instead of horizontally scrolling just the current line by nearly
a screen width when the cursor is about to go offscreen, now all
screen lines are scrolled horizontally together by just the amount
needed to keep the cursor comfortably in view.

References:
  https://lists.gnu.org/archive/html/nano-devel/2026-01/msg00000.html
  https://lists.gnu.org/archive/html/nano-devel/2026-01/msg00003.html
  https://lists.gnu.org/archive/html/nano-devel/2014-05/msg00025.html
  https://lists.gnu.org/archive/html/nano-devel/2014-06/msg00152.html

Nano could have acquired this feature nearly twelve years ago, but...
somehow that old patch fell through the cracks and was forgotten.  :|

Motivated-by: Xylia Allegretta <xylia.allegretta@gmail.com>
Original-patch-by: Mark Majeres <mark@engine12.com>
This commit is contained in:
Benno Schulenberg
2026-01-04 15:32:16 +01:00
parent 255845cc13
commit 7779a19903
8 changed files with 52 additions and 4 deletions

View File

@@ -53,6 +53,9 @@ void expunge(undo_type action)
/* When softwrapping, a changed number of chunks requires a refresh. */
if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount)
refresh_needed = TRUE;
/* When panning, and we have come near edge of the viewport... */
else if (!ISSET(SOLO_SIDESCROLL) && openfile->placewewant < brink + CUSHION)
refresh_needed = TRUE;
/* Adjust the mark if it is after the cursor on the current line. */
if (openfile->mark == openfile->current &&

View File

@@ -143,6 +143,9 @@
/* The default number of columns from end of line where wrapping occurs. */
#define COLUMNS_FROM_EOL 8
/* The number of columns the cursor should stay away from the edge. */
#define CUSHION 3
/* The default comment character when a syntax does not specify any. */
#define GENERAL_COMMENT_CHARACTER "#"
@@ -376,7 +379,8 @@ enum {
USE_MAGIC,
MINIBAR,
ZERO,
MODERN_BINDINGS
MODERN_BINDINGS,
SOLO_SIDESCROLL
};
/* Structure types. */

View File

@@ -73,6 +73,8 @@ char *title = NULL;
bool refresh_needed = FALSE;
/* Did a command mangle enough of the buffer that we should
* repaint the screen? */
size_t brink = 0;
/* From which column the edit window is drawn (when panning). */
bool focusing = TRUE;
/* Whether an update of the edit window should center the cursor. */

View File

@@ -1551,6 +1551,10 @@ void inject(char *burst, size_t count)
openfile->placewewant = xplustabs();
/* When panning, and we have come near the edge of the viewport... */
if (!ISSET(SOLO_SIDESCROLL) && openfile->placewewant > brink + editwincols - CUSHION - 1 )
refresh_needed = TRUE;
#ifndef NANO_TINY
/* 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,

View File

@@ -47,6 +47,8 @@ extern int final_status;
extern bool inhelp;
extern char *title;
extern size_t brink;
extern bool focusing;
extern bool as_an_at;

View File

@@ -332,6 +332,11 @@ int findnextstr(const char *needle, bool whole_word_only, int modus,
spotlighted = TRUE;
light_from_col = xplustabs();
light_to_col = wideness(line->data, found_x + found_len);
/* When panning, ensure the end of the match will be visible too. */
if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP))
brink = get_page_start(light_to_col);
refresh_needed = TRUE;
}
#endif
@@ -591,6 +596,9 @@ ssize_t do_replace_loop(const char *needle, bool whole_word_only,
light_to_col = wideness(openfile->current->data,
openfile->current_x + match_len);
if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP))
brink = get_page_start(light_to_col);
/* Refresh the edit window, scrolling it if necessary. */
edit_refresh();

View File

@@ -343,6 +343,17 @@ char *free_and_assign(char *dest, char *src)
* displayed in the edit window when the cursor is at the given column. */
size_t get_page_start(size_t column)
{
if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP)) {
if (column < CUSHION)
return 0;
else if (column < brink + CUSHION)
return column - CUSHION;
else if (column > brink + editwincols - CUSHION - 1)
return column - editwincols + CUSHION + 1;
else
return brink;
}
if (column == 0 || column + 2 < editwincols || ISSET(SOFTWRAP))
return 0;
else if (editwincols > 8)

View File

@@ -2838,7 +2838,10 @@ int update_line(linestruct *line, size_t index)
#endif
row = line->lineno - openfile->edittop->lineno;
from_col = get_page_start(wideness(line->data, index));
if (!ISSET(SOLO_SIDESCROLL))
from_col = brink;
else
from_col = get_page_start(wideness(line->data, index));
/* Expand the piece to be drawn to its representable form, and draw it. */
converted = display_string(line->data, from_col, editwincols, TRUE, FALSE);
@@ -2931,9 +2934,13 @@ bool line_needs_update(const size_t old_column, const size_t new_column)
#ifndef NANO_TINY
if (openfile->mark)
return TRUE;
else
#endif
return (get_page_start(old_column) != get_page_start(new_column));
if (get_page_start(old_column) == get_page_start(new_column))
return FALSE;
if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP))
refresh_needed = TRUE;
return !refresh_needed;
}
/* Try to move up nrows softwrapped chunks from the given line and the
@@ -3342,6 +3349,9 @@ void edit_redraw(linestruct *old_current, update_type manner)
adjust_viewport(ISSET(JUMPY_SCROLLING) ? CENTERING : manner);
refresh_needed = TRUE;
return;
} else if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP)) {
refresh_needed = TRUE;
return;
}
#ifndef NANO_TINY
@@ -3381,6 +3391,10 @@ void edit_refresh(void)
if (current_is_offscreen())
adjust_viewport((focusing || ISSET(JUMPY_SCROLLING)) ? CENTERING : FLOWING);
/* When panning, ensure the cursor will be within the viewport. */
if (!ISSET(SOLO_SIDESCROLL) && !ISSET(SOFTWRAP))
brink = get_page_start(xplustabs());
#ifdef ENABLE_COLOR
/* When needed and useful, initialize the colors for the current syntax. */
if (openfile->syntax && !have_palette && !ISSET(NO_SYNTAX) && has_colors())