[PATCH] feature: relative line numbers

bsandro <[email protected]> Mon, 30 Jun 2025 01:26:05 +0300
Newsgroups gmane.editors.nano.devel
Message-ID <aGG9fViu2JCV7pFO@fedora>
Greetings!

nano is my go-to code editor for several years now, but I still miss the
relative line numbers feature from vim that I was using before that.

p.s. Sorry for my somewhat broken english.

-- 
Brian Sandro.
0001-new-feature-relative-line-numbers.patch (text/plain, 7.1 KB)
From c33e5f78f29ad35e77eaf1a99e1454f6c8b2f2dd Mon Sep 17 00:00:00 2001
From: bsandro <[email protected]>
Date: Mon, 30 Jun 2025 01:10:25 +0300
Subject: [PATCH] new feature: relative line numbers.

When a config option "linerelnumbers" is used with the default line numbers
feature enabled (both on build and in config/command line) display line numbers
relative to the "active" line under the cursor. The active line number is the
actual one from top to avoid confusion and constant displaying "0" for it.

Modeled by the same vim/neovim feature.

Signed-off-by: bsandro <[email protected]>
---
 doc/nano.texi        |  3 +++
 doc/sample.nanorc.in |  3 +++
 src/definitions.h    |  1 +
 src/rcfile.c         |  3 +++
 src/winio.c          | 11 ++++++++++-
 syntax/nanorc.nanorc |  4 ++--
 6 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/doc/nano.texi b/doc/nano.texi
index 9bfe049e..4324885f 100644
--- a/doc/nano.texi
+++ b/doc/nano.texi
@@ -1037,6 +1037,9 @@ if your terminal can do those.
 Display line numbers to the left of the text area.
 (Any line with an anchor additionally gets a mark in the margin.)
 
+@item set linerelnumbers
+Display relative line numbers (only works with linenumbers option enabled).
+
 @item set locking
 Enable vim-style lock-files for when editing files.
 
diff --git a/doc/sample.nanorc.in b/doc/sample.nanorc.in
index e559356f..efe43c49 100644
--- a/doc/sample.nanorc.in
+++ b/doc/sample.nanorc.in
@@ -84,6 +84,9 @@
 ## Display line numbers to the left (and any anchors in the margin).
 # set linenumbers
 
+## Display relative line numbers (works only with linenumbers enabled)
+# set linerelnumbers
+
 ## Enable vim-style lock-files.  This is just to let a vim user know you
 ## are editing a file [s]he is trying to edit and vice versa.  There are
 ## no plans to implement vim-style undo state in these files.
diff --git a/src/definitions.h b/src/definitions.h
index c73b4a82..a04b3b07 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -363,6 +363,7 @@ enum {
 	TRIM_BLANKS,
 	SHOW_CURSOR,
 	LINE_NUMBERS,
+	LINE_REL_NUMBERS,
 	AT_BLANKS,
 	AFTER_ENDS,
 	LET_THEM_ZAP,
diff --git a/src/rcfile.c b/src/rcfile.c
index 6143f5fc..66cd91d6 100644
--- a/src/rcfile.c
+++ b/src/rcfile.c
@@ -56,6 +56,7 @@ static const rcoption rcopts[] = {
 #endif
 #ifdef ENABLE_LINENUMBERS
 	{"linenumbers", LINE_NUMBERS},
+	{"linerelnumbers", LINE_REL_NUMBERS},
 #endif
 #ifdef HAVE_LIBMAGIC
 	{"magic", USE_MAGIC},
@@ -457,6 +458,8 @@ keystruct *strtosc(const char *input)
 #ifdef ENABLE_LINENUMBERS
 		else if (!strcmp(input, "linenumbers"))
 			s->toggle = LINE_NUMBERS;
+		else if (!strcmp(input, "linerelnumbers"))
+			s->toggle = LINE_REL_NUMBERS;
 #endif
 		else if (!strcmp(input, "whitespacedisplay"))
 			s->toggle = WHITESPACE_DISPLAY;
diff --git a/src/winio.c b/src/winio.c
index baaa96f4..b3bb1c0f 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2548,12 +2548,15 @@ void draw_row(int row, const char *converted, linestruct *line, size_t from_col)
 	 * the text -- but only for the parts that are not softwrapped. */
 	if (margin > 0) {
 		wattron(midwin, interface_color_pair[LINE_NUMBER]);
+		long int lineno = line->lineno;
+		if (ISSET(LINE_REL_NUMBERS) && openfile->current->lineno != line->lineno)
+			lineno = labs(openfile->current->lineno - line->lineno);
 #ifndef NANO_TINY
 		if (ISSET(SOFTWRAP) && from_col != 0)
 			mvwprintw(midwin, row, 0, "%*s", margin - 1, " ");
 		else
 #endif
-			mvwprintw(midwin, row, 0, "%*zd", margin - 1, line->lineno);
+			mvwprintw(midwin, row, 0, "%*zd", margin - 1, lineno);
 		wattroff(midwin, interface_color_pair[LINE_NUMBER]);
 #ifndef NANO_TINY
 		if (line->has_anchor && (from_col == 0 || !ISSET(SOFTWRAP)))
@@ -3336,6 +3339,12 @@ void edit_redraw(linestruct *old_current, update_type manner)
 
 	openfile->placewewant = xplustabs();
 
+	#ifdef ENABLE_LINENUMBERS
+	/* Full update is needed if relative line numbers are on */
+	if (ISSET(LINE_NUMBERS) && ISSET(LINE_REL_NUMBERS) && old_current != openfile->current)
+		refresh_needed = TRUE;
+	#endif
+
 	/* If the current line is offscreen, scroll until it's onscreen. */
 	if (current_is_offscreen()) {
 		adjust_viewport(ISSET(JUMPY_SCROLLING) ? CENTERING : manner);
diff --git a/syntax/nanorc.nanorc b/syntax/nanorc.nanorc
index 18c55292..0de04f84 100644
--- a/syntax/nanorc.nanorc
+++ b/syntax/nanorc.nanorc
@@ -14,7 +14,7 @@ color bold,purple "^[[:blank:]]*include[[:blank:]]+[^[:blank:]"]+"
 color lime "^[[:blank:]]*extendsyntax[[:blank:]]+[[:alpha:]]+[[:blank:]]+(i?color|header|magic|comment|formatter|linter|tabgives)[[:blank:]]+.*"
 
 # The arguments of commands
-color brightgreen "^[[:blank:]]*(set|unset)[[:blank:]]+(afterends|allow_insecure_backup|atblanks|autoindent|backup|boldtext|bookstyle|breaklonglines|casesensitive|colonparsing|constantshow|cutfromcursor|emptyline|historylog|indicator|jumpyscrolling|linenumbers|locking|magic|minibar|mouse|multibuffer|noconvert|nohelp|nonewlines|positionlog|preserve|quickblank|rawsequences|rebinddelete|regexp|saveonexit|showcursor|smarthome|softwrap|stateflags|tabstospaces|trimblanks|unix|whitespacedisplay|wordbounds|zap|zero)\>"
+color brightgreen "^[[:blank:]]*(set|unset)[[:blank:]]+(afterends|allow_insecure_backup|atblanks|autoindent|backup|boldtext|bookstyle|breaklonglines|casesensitive|colonparsing|constantshow|cutfromcursor|emptyline|historylog|indicator|jumpyscrolling|linenumbers|linerelnumbers|locking|magic|minibar|mouse|multibuffer|noconvert|nohelp|nonewlines|positionlog|preserve|quickblank|rawsequences|rebinddelete|regexp|saveonexit|showcursor|smarthome|softwrap|stateflags|tabstospaces|trimblanks|unix|whitespacedisplay|wordbounds|zap|zero)\>"
 color brightgreen "^[[:blank:]]*set[[:blank:]]+(backupdir|brackets|errorcolor|functioncolor|keycolor|matchbrackets|minicolor|numbercolor|operatingdir|promptcolor|punct|quotestr|scrollercolor|selectedcolor|speller|spotlightcolor|statuscolor|stripecolor|titlecolor|whitespace|wordchars)[[:blank:]]+"
 color brightgreen "^[[:blank:]]*set[[:blank:]]+(fill[[:blank:]]+-?[[:digit:]]+|(guidestripe|tabsize)[[:blank:]]+[1-9][0-9]*)\>"
 color brightgreen "^[[:blank:]]*bind[[:blank:]]+((\^([[:alpha:]]|[]/@\^_`-]|Space)|([Ss][Hh]-)?[Mm]-[[:alpha:]]|[Mm]-([][!"#$%&'()*+,./0-9:;<=>?@\^_`{|}~-]|Space))|F([1-9]|1[0-9]|2[0-4])|Ins|Del)[[:blank:]]+([[:lower:]]+|".*")[[:blank:]]+(main|help|search|replace(with)?|yesno|gotoline|writeout|insert|execute|browser|whereisfile|gotodir|spell|linter|all)\>"
@@ -32,7 +32,7 @@ color crimson "\{(location|gotoline|(begin|end)para|comment|complete|(un)?indent
 color crimson "\{(left|right|up|down|home|end|(scroll|page)(up|down)|(top|bottom)row|center|cycle|(prev|next)(word|block|anchor|buf))\}"
 color crimson "\{(tab|enter|delete|backspace|verbatim|refresh|suspend|casesens|regexp|backwards|older|newer|(dos|mac)format)\}"
 color crimson "\{(append|prepend|backup|flip(goto|replace|execute|pipe|convert|newbuffer)|browser|gotodir|(first|last)(file|line))\}"
-color crimson "\{(nohelp|constantshow|softwrap|linenumbers|whitespacedisplay|nosyntax|zero)\}"
+color crimson "\{(nohelp|constantshow|softwrap|linenumbers|linerelnumbers|whitespacedisplay|nosyntax|zero)\}"
 color crimson "\{(smarthome|autoindent|cutfromcursor|breaklonglines|tabstospaces|mouse|\{)\}"
 
 # Commands
-- 
2.50.0
signature.asc (application/pgp-signature, 862 B)
-----BEGIN PGP SIGNATURE-----

iQJHBAEBCgAxFiEEKtcSzGJIWxEmOgE0Y6arLGv7p6cFAmhhvX0THGVtYWlsQGJz
YW5kcm8udGVjaAAKCRBjpqssa/unpwiXD/46tfZ/1T4QI5Bh1lU1BcSb+6BC6XZm
TRfmoy63Ew2CENiMG1IppKa87ckANPj8G37FeXlEWZSx6jsn8pyATzePML1PWn1U
oEZK7aSA04ZbHTOb4Itkzwg7TKKrX1U4vXQU2sP4a2S68aUPxSgyLoRlZrqcI3aJ
0DFwBNpV++4ipUYaxD26WUbyN3kSzHEbFFHsvZa3BpWUUV57k0wkxE2MixVW1wjo
GHU/wa9zZ+NQJx1zru+Oaqv8EzpQ7OoOcZr45NeRxK2LjceYM+6ZBZ8R+m8GZcww
gyxRoX2k1oKiOxsXIkyOnynndzGWer7NT+94DQh9Z6jGXQBHsRodK1AmVZRf8lVE
coEC1YSfxVcKSfLS8QqiNpJ4dgZqfo4MxeyvkqnP3wKo5bnWuHSWwk789ti0HlgU
UOKJqE7bwL8NZ1E2bcJ3/L2v50ZrDENKqE5T/FLfcACp7Fg83mzS/5Ine3qi05+P
jzSG7sFAbn4KTTdVREoUQRnij48I902HzZ9aphF0RRGuj01TRrH8OMPhiMNWhUEV
n+sPLwSzv8NsDBMvScPv5M6vKToTSXfO+aUlS59YQrVufd6uy6Ea4pJFudggzL59
EWKwC4MgjS0sDiv5zrGo/eivWDlakcKa5zI3EO/pR5DsYOS1vjPHBAvpyrbbL1Ur
pZgcU6oPUbfC+Q==
=PpYd
-----END PGP SIGNATURE-----