[PATCH] new feature: horizontal scrolling of full screen

Xylia Allegretta <[email protected]> Mon, 5 Jan 2026 03:45:19 +0000
Newsgroups gmane.editors.nano.devel
Message-ID <CAE7GgQ87UrGF3ONQmAPhYABO7wmba4ztT0v9q0P5djBK+MaXQw@mail.gmail.com>
"In short: there are not enough checks for whether to redraw the
screen when --fullscrolling is in effect."

I've added more checks to ensure that the screen stays scrolled
correctly when using Home/End, inserting or deleting characters,
and cutting text. Hopefully this results in fewer problems,
although there are likely still cases I have forgotten to check,
or where the checks prove insufficient. I've also changed the
formatting to match the style that the rest of nano's source code
uses, and removed the currently unnecessary flag, toggle and nanorc
option, instead setting the fullscrolling option by default. A new
version of the patch is attached.

"Also, I don't like the huge jump to the right when the cursor
comes close to the right edge of the screen: it is disorienting."

I agree that it's a bit jarring to scroll the entire screen when
reaching the next page, but that's how nano already handles
single-line horizontal scrolling, so I don't see why full-screen
horizontal scrolling should behave any differently. It'd likely be
better to have these as two separate toggles, with the smooth
horizontal scroll toggle affecting both single-line and full-screen
scrolling, as otherwise, the behavior would be inconsistent.

Also, because of the current behavior, if full horizontal scrolling
were implemented with smooth scrolling, and if the current line was
already scrolled before enabling it, the scrolling of the current line
would change upon enable, which I don't think makes any sense.

"I like its horizontal-scrolling behavior much better: one character
at a time as the cursor moves further to the right."

I tried out the 2.3.5 patch, and while it may look nicer, I don't
think it's as useful to scroll in that way, as you'll many times
have to scroll past what you're editing and then back to have it
fully in view. When jumping a page, while it may be jarring, you
can see everything on the next page immediately. Again, either
way, I think if implemented, it would be better as a separate option.

"Allegretta...  You are related to Chris?"

Yes.

Oh, and sorry for not making this an actual reply in the same
thread. I was idiotically not subscribed to the nano-devel mailing
list, so I don't actually have the email to reply to. Oh well. At
least I can change the name to be more sensible.

-Xylia Allegretta.
fullscrolling-2.patch (text/x-patch, 8.7 KB)
From 0b80d32349a9325de11ca246d146178e1a353af0 Mon Sep 17 00:00:00 2001
From: Xylia Allegretta <[email protected]>
Date: Sun, 4 Jan 2026 22:15:15 -0500
Subject: [PATCH] new feature: horizontal scrolling of full screen

I've added more checks to ensure that the screen stays scrolled
correctly when using Home/End, inserting or deleting characters,
and cutting text. Hopefully this results in fewer problems,
although there are likely still cases I have forgotten to check,
or where the checks prove insufficient. I've also changed the
formatting to match the style that the rest of nano's source code
uses, and removed the currently unnecessary flag, toggle and nanorc
option, instead setting the fullscrolling option by default.

Signed-off-by: Xylia Allegretta <[email protected]>
---
 src/cut.c         |  6 +++--
 src/definitions.h |  3 ++-
 src/move.c        | 20 ++++++++++++---
 src/nano.c        | 13 +++++++---
 src/prototypes.h  |  1 +
 src/winio.c       | 62 ++++++++++++++++++++++++++++++++++++++++++++---
 6 files changed, 91 insertions(+), 14 deletions(-)

diff --git a/src/cut.c b/src/cut.c
index a2d4aecf..7ccc8ef2 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -50,8 +50,10 @@ void expunge(undo_type action)
 					&openfile->current->data[openfile->current_x + charlen],
 					line_len - charlen + 1);
 #ifndef NANO_TINY
-		/* When softwrapping, a changed number of chunks requires a refresh. */
-		if (ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount)
+		/* When softwrapping, a changed number of chunks requires a refresh.
+		 * The same is true of a page change when fullscrolling. */
+		if ((ISSET(SOFTWRAP) && extra_chunks_in(openfile->current) != old_amount) ||
+					(ISSET(FULL_SCROLLING) && fullsc_update()))
 			refresh_needed = TRUE;
 
 		/* Adjust the mark if it is after the cursor on the current line. */
diff --git a/src/definitions.h b/src/definitions.h
index dfe2106f..3262821f 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -376,7 +376,8 @@ enum {
 	USE_MAGIC,
 	MINIBAR,
 	ZERO,
-	MODERN_BINDINGS
+	MODERN_BINDINGS,
+	FULL_SCROLLING
 };
 
 /* Structure types. */
diff --git a/src/move.c b/src/move.c
index f79107ab..e87ba002 100644
--- a/src/move.c
+++ b/src/move.c
@@ -511,8 +511,14 @@ void do_home(void)
 
 	/* If we changed chunk, we might be offscreen.  Otherwise,
 	 * update current if the mark is on or we changed "page". */
-	if (ISSET(SOFTWRAP) && moved_off_chunk)
-		edit_redraw(was_current, FLOWING);
+	if(moved_off_chunk) {
+		if (ISSET(SOFTWRAP))
+			edit_redraw(was_current, FLOWING);
+		else if (ISSET(FULL_SCROLLING)) {
+			fullsc_update();
+			edit_refresh();
+		}
+	}
 	else if (line_needs_update(was_column, openfile->placewewant))
 		update_line(openfile->current, openfile->current_x);
 }
@@ -563,8 +569,14 @@ void do_end(void)
 
 	/* If we changed chunk, we might be offscreen.  Otherwise,
 	 * update current if the mark is on or we changed "page". */
-	if (ISSET(SOFTWRAP) && moved_off_chunk)
-		edit_redraw(was_current, FLOWING);
+	if(moved_off_chunk) {
+		if (ISSET(SOFTWRAP))
+			edit_redraw(was_current, FLOWING);
+		else if (ISSET(FULL_SCROLLING)) {
+			fullsc_update();
+			edit_refresh();
+		}
+	}
 	else if (line_needs_update(was_column, openfile->placewewant))
 		update_line(openfile->current, openfile->current_x);
 }
diff --git a/src/nano.c b/src/nano.c
index 2b45ca76..ef0bcb59 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -1554,10 +1554,11 @@ void inject(char *burst, size_t count)
 #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,
-	 * we need a full refresh. */
-	if (ISSET(SOFTWRAP) && (extra_chunks_in(openfile->current) != old_amount ||
+	 * we need a full refresh. This is also true for full scrolling. */
+	if ((ISSET(SOFTWRAP) && (extra_chunks_in(openfile->current) != old_amount ||
 					(openfile->cursor_row == editwinrows - 1 &&
-					chunk_for(openfile->placewewant, openfile->current) > original_row))) {
+					chunk_for(openfile->placewewant, openfile->current) > original_row))) ||
+					(ISSET(FULL_SCROLLING) && fullsc_update())) {
 		refresh_needed = TRUE;
 		focusing = FALSE;
 	}
@@ -2642,6 +2643,12 @@ int main(int argc, char **argv)
 		die(_("Can open just one file\n"));
 #endif
 
+	/* Enabled by default for testing. */
+	SET(FULL_SCROLLING);
+	/* These options should be mutually exclusive. */
+	if (ISSET(SOFTWRAP))
+		UNSET(SOFTWRAP);
+
 	prepare_for_display();
 
 #ifdef ENABLE_NANORC
diff --git a/src/prototypes.h b/src/prototypes.h
index c83380df..a6fe8bd2 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -687,3 +687,4 @@ void flip_newbuffer(void);
 #endif
 void discard_buffer(void);
 void do_cancel(void);
+bool fullsc_update(void);
diff --git a/src/winio.c b/src/winio.c
index 277851d9..56af39cb 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -81,6 +81,8 @@ static size_t macro_length = 0;
 		/* The current length of the macro. */
 static size_t milestone = 0;
 		/* Where the last burst of recorded keystrokes started. */
+static bool fullsc_culled = FALSE;
+		/* If a line has been culled due to being offscreen. */
 
 /* Add the given code to the macro buffer. */
 void add_to_macrobuffer(int code)
@@ -1787,6 +1789,25 @@ char *display_string(const char *text, size_t column, size_t span,
 	size_t beyond = column + span;
 		/* The column number just beyond the last shown character. */
 
+	/* If fullscrolling is enabled, cull offscreen text. */
+	if (isdata && ISSET(FULL_SCROLLING)) {
+		size_t cur_page = get_page_start(xplustabs());
+		/* Is text offscreen? */
+		if (cur_page > get_page_start(breadth(text))) {
+			/* Create blank string. */
+			/* This has the side effect of the mark highlighting the
+			 * entire line, which could be a good or bad thing. */
+			size_t i;
+			for (i=0; i<allocsize-1; i++)
+				converted[i]=' ';
+			converted[i]='\0';
+			has_more = FALSE;
+			fullsc_culled = TRUE;
+			return converted;
+		} else
+			fullsc_culled = FALSE;
+	}
+
 	text += start_x;
 
 #ifndef NANO_TINY
@@ -2838,14 +2859,17 @@ 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(FULL_SCROLLING))
+		from_col = get_page_start(xplustabs());
+	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);
 	draw_row(row, converted, line, from_col);
 	free(converted);
 
-	if (from_col > 0) {
+	if (from_col > 0 || (ISSET(FULL_SCROLLING) && fullsc_culled)) {
 		wattron(midwin, hilite_attribute);
 		mvwaddch(midwin, row, margin, '<');
 		wattroff(midwin, hilite_attribute);
@@ -3067,6 +3091,22 @@ void draw_scrollbar(void)
 }
 #endif
 
+/* If fullscrolling is on and the "page" has changed, return one, indicating
+ * that a screen refresh is needed. */
+bool fullsc_update(void)
+{
+	static size_t oldxplustabs = 0;
+	if (ISSET(FULL_SCROLLING)) {
+		if(get_page_start(oldxplustabs) != get_page_start(xplustabs())) {
+			oldxplustabs = xplustabs();
+			return 1;
+		}
+	}
+	oldxplustabs = xplustabs();
+
+	return 0;
+}
+
 /* Scroll the edit window one row in the given direction, and
  * draw the relevant content on the resultant blank row. */
 void edit_scroll(bool direction)
@@ -3086,6 +3126,12 @@ void edit_scroll(bool direction)
 	wscrl(midwin, (direction == BACKWARD) ? -1 : 1);
 	scrollok(midwin, FALSE);
 
+	/* If fullscrolling, and the page changed, refresh. */
+	if(fullsc_update()) {
+		refresh_needed = 1;
+		return;
+	}
+
 	/* If we're not on the first "page" (when not softwrapping), or the mark
 	 * is on, the row next to the scrolled region needs to be redrawn too. */
 	if (line_needs_update(openfile->placewewant, 0) && nrows < editwinrows)
@@ -3344,6 +3390,12 @@ void edit_redraw(linestruct *old_current, update_type manner)
 		return;
 	}
 
+	/* If fullscrolling, and the page changed, refresh. */
+	if(fullsc_update()) {
+		refresh_needed = TRUE;
+		return;
+	}
+
 #ifndef NANO_TINY
 	/* If the mark is on, update all lines between old_current and current. */
 	if (openfile->mark) {
@@ -3358,8 +3410,10 @@ void edit_redraw(linestruct *old_current, update_type manner)
 	} else
 #endif
 		/* Otherwise, update old_current only if it differs from current
-		 * and was horizontally scrolled. */
-		if (old_current != openfile->current && get_page_start(was_pww) > 0)
+		 * and was horizontally scrolled. When fullscrolled, there's no
+		 * reason to do this. */
+		if (old_current != openfile->current && (get_page_start(was_pww) > 0
+						&& !ISSET(FULL_SCROLLING)))
 			update_line(old_current, 0);
 
 	/* Update current if the mark is on or it has changed "page", or if it
-- 
2.52.0