From c6dbcf91c307e6c0126ddc4acec95d981f4d1cc8 Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@justemail.net>
Date: Sat, 25 Jun 2016 15:16:52 +0200
Subject: [PATCH] new feature: functions to jump to previous or following block
 of text

And hard-bind the keys Ctrl+Up and Ctrl+Down to these functions.

Unlike the paragraph-jumping code, these new functions disregard
any indentation, treating only blank lines as separators.  They
also do an automatic smart home when that option is set.

This fulfills https://savannah.gnu.org/bugs/?48291.
---
 doc/man/nanorc.5      |  7 +++++++
 doc/texinfo/nano.texi |  7 +++++++
 src/global.c          | 21 +++++++++++++++++++++
 src/move.c            | 42 ++++++++++++++++++++++++++++++++++++++++++
 src/nano.c            |  8 +++++++-
 src/nano.h            |  2 ++
 src/proto.h           |  5 +++++
 src/winio.c           | 16 ++++++++++++----
 8 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/doc/man/nanorc.5 b/doc/man/nanorc.5
index 7c26b59c..c9279c46 100644
--- a/doc/man/nanorc.5
+++ b/doc/man/nanorc.5
@@ -510,6 +510,13 @@ Moves the cursor to the beginning of the current paragraph.
 .B endpara
 Moves the cursor to the end of the current paragraph.
 .TP
+.B prevblock
+Moves the cursor to the beginning of the current or preceding block of text.
+(Blocks are separated by one or more blank lines.)
+.TP
+.B nextblock
+Moves the cursor to the beginning of the next block of text.
+.TP
 .B prevpage
 Goes up one screenful.
 .TP
diff --git a/doc/texinfo/nano.texi b/doc/texinfo/nano.texi
index 509d749a..fdc5ae33 100644
--- a/doc/texinfo/nano.texi
+++ b/doc/texinfo/nano.texi
@@ -1102,6 +1102,13 @@ Moves the cursor to the beginning of the current paragraph.
 @item endpara
 Moves the cursor to the end of the current paragraph.
 
+@item prevblock
+Moves the cursor to the beginning of the current or preceding block of text.
+(Blocks are separated by one or more blank lines.)
+
+@item nextblock
+Moves the cursor to the beginning of the next block of text.
+
 @item prevpage
 Goes up one screenful.
 
diff --git a/src/global.c b/src/global.c
index ca7d4e1e..e2d8749f 100644
--- a/src/global.c
+++ b/src/global.c
@@ -46,6 +46,8 @@ message_type lastmessage = HUSH;
 #ifndef NANO_TINY
 int controlleft = CONTROL_LEFT;
 int controlright = CONTROL_RIGHT;
+int controlup = CONTROL_UP;
+int controldown = CONTROL_DOWN;
 #endif
 
 #ifndef DISABLE_WRAPJUSTIFY
@@ -573,6 +575,10 @@ void shortcut_init(void)
     const char *nano_nextline_msg = N_("Go to next line");
     const char *nano_home_msg = N_("Go to beginning of current line");
     const char *nano_end_msg = N_("Go to end of current line");
+#ifndef NANO_TINY
+    const char *nano_prevblock_msg = N_("Go to previous block of text");
+    const char *nano_nextblock_msg = N_("Go to next block of text");
+#endif
 #ifndef DISABLE_JUSTIFY
     const char *nano_parabegin_msg =
 	N_("Go to beginning of paragraph; then of previous paragraph");
@@ -872,6 +878,13 @@ void shortcut_init(void)
     add_to_funcs(do_down_void, MMAIN|MBROWSER,
 	next_line_tag, IFSCHELP(nano_nextline_msg), BLANKAFTER, VIEW);
 
+#ifndef NANO_TINY
+    add_to_funcs(do_prev_block, MMAIN,
+	N_("Prev Block"), IFSCHELP(nano_prevblock_msg), TOGETHER, VIEW);
+    add_to_funcs(do_next_block, MMAIN,
+	N_("Next Block"), IFSCHELP(nano_nextblock_msg), TOGETHER, VIEW);
+#endif
+
 #ifndef DISABLE_JUSTIFY
     add_to_funcs(do_para_begin_void, MMAIN|MWHEREIS,
 	N_("Beg of Par"), IFSCHELP(nano_parabegin_msg), TOGETHER, VIEW);
@@ -1133,6 +1146,10 @@ void shortcut_init(void)
     add_to_sclist(MMAIN|MHELP|MBROWSER, "Up", do_up_void, 0);
     add_to_sclist(MMAIN|MHELP|MBROWSER, "^N", do_down_void, 0);
     add_to_sclist(MMAIN|MHELP|MBROWSER, "Down", do_down_void, 0);
+#ifndef NANO_TINY
+    add_to_sclist(MMAIN, "M-7", do_prev_block, 0);
+    add_to_sclist(MMAIN, "M-8", do_next_block, 0);
+#endif
 #ifndef DISABLE_JUSTIFY
     add_to_sclist(MMAIN, "M-(", do_para_begin_void, 0);
     add_to_sclist(MMAIN, "M-9", do_para_begin_void, 0);
@@ -1453,6 +1470,10 @@ sc *strtosc(const char *input)
 	s->scfunc = do_cut_prev_word;
     else if (!strcasecmp(input, "cutwordright"))
 	s->scfunc = do_cut_next_word;
+    else if (!strcasecmp(input, "prevblock"))
+	s->scfunc = do_prev_block;
+    else if (!strcasecmp(input, "nextblock"))
+	s->scfunc = do_next_block;
     else if (!strcasecmp(input, "findbracket"))
 	s->scfunc = do_find_bracket;
     else if (!strcasecmp(input, "wordcount"))
diff --git a/src/move.c b/src/move.c
index 2f234e51..9d3bd797 100644
--- a/src/move.c
+++ b/src/move.c
@@ -210,6 +210,48 @@ void do_para_end_void(void)
 #endif /* !DISABLE_JUSTIFY */
 
 #ifndef NANO_TINY
+/* Move to the preceding block of text in the file. */
+void do_prev_block(void)
+{
+    filestruct *was_current = openfile->current;
+    bool is_text = FALSE, seen_text = FALSE;
+
+    /* Skip backward until first blank line after some nonblank line(s). */
+    while (openfile->current->prev != NULL && (!seen_text || is_text)) {
+	openfile->current = openfile->current->prev;
+	is_text = !white_string(openfile->current->data);
+	seen_text = seen_text || is_text;
+    }
+
+    /* Step forward one line again if this one is blank. */
+    if (openfile->current->next != NULL &&
+		white_string(openfile->current->data))
+	openfile->current = openfile->current->next;
+
+    openfile->current_x = 0;
+    edit_redraw(was_current);
+    do_home();
+}
+
+/* Move to the next block of text in the file. */
+void do_next_block(void)
+{
+    filestruct *was_current = openfile->current;
+    bool is_white = white_string(openfile->current->data);
+    bool seen_white = is_white;
+
+    /* Skip forward until first nonblank line after some blank line(s). */
+    while (openfile->current->next != NULL && (!seen_white || is_white)) {
+	openfile->current = openfile->current->next;
+	is_white = white_string(openfile->current->data);
+	seen_white = seen_white || is_white;
+    }
+
+    openfile->current_x = 0;
+    edit_redraw(was_current);
+    do_home();
+}
+
 /* Move to the previous word in the file.  If allow_punct is TRUE, treat
  * punctuation as part of a word.  If allow_update is TRUE, update the
  * screen afterwards. */
diff --git a/src/nano.c b/src/nano.c
index d6846b74..74e1e60a 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -2527,13 +2527,19 @@ int main(int argc, char **argv)
 
 #if !defined(NANO_TINY) && defined(HAVE_KEY_DEFINED)
     const char *keyvalue;
-    /* Ask ncurses for the key codes for Control+Left and Control+Right. */
+    /* Ask ncurses for the key codes for Control+Left/Right/Up/Down. */
     keyvalue = tigetstr("kLFT5");
     if (keyvalue != 0 && keyvalue != (char *)-1)
 	controlleft = key_defined(keyvalue);
     keyvalue = tigetstr("kRIT5");
     if (keyvalue != 0 && keyvalue != (char *)-1)
 	controlright = key_defined(keyvalue);
+    keyvalue = tigetstr("kUP5");
+    if (keyvalue != 0 && keyvalue != (char *)-1)
+	controlup = key_defined(keyvalue);
+    keyvalue = tigetstr("kDN5");
+    if (keyvalue != 0 && keyvalue != (char *)-1)
+	controldown = key_defined(keyvalue);
 #endif
 
 #ifndef USE_SLANG
diff --git a/src/nano.h b/src/nano.h
index b2e154a1..7403833d 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -564,6 +564,8 @@ enum
 /* Codes for "modified" Arrow keys, beyond KEY_MAX of ncurses. */
 #define CONTROL_LEFT 0x401
 #define CONTROL_RIGHT 0x402
+#define CONTROL_UP 0x403
+#define CONTROL_DOWN 0x404
 
 #ifndef NANO_TINY
 /* An imaginary key for when we get a SIGWINCH (window resize). */
diff --git a/src/proto.h b/src/proto.h
index 9ce7feea..6b938cb6 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -39,6 +39,8 @@ extern message_type lastmessage;
 #ifndef NANO_TINY
 extern int controlleft;
 extern int controlright;
+extern int controlup;
+extern int controldown;
 #endif
 
 #ifndef DISABLE_WRAPJUSTIFY
@@ -397,6 +399,8 @@ void do_para_end(bool allow_update);
 void do_para_end_void(void);
 #endif
 #ifndef NANO_TINY
+void do_prev_block(void);
+void do_next_block(void);
 void do_prev_word(bool allow_punct, bool allow_update);
 void do_prev_word_void(void);
 bool do_next_word(bool allow_punct, bool allow_update);
@@ -647,6 +651,7 @@ void do_tab(void);
 void do_indent(ssize_t cols);
 void do_indent_void(void);
 void do_unindent(void);
+bool white_string(const char *s);
 void do_undo(void);
 void do_redo(void);
 #endif
diff --git a/src/winio.c b/src/winio.c
index c9786679..1ae8a910 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -635,6 +635,10 @@ int parse_kbinput(WINDOW *win)
 	    retval = sc_seq_or(do_prev_word_void, 0);
 	else if (retval == controlright)
 	    retval = sc_seq_or(do_next_word_void, 0);
+	else if (retval == controlup)
+	    retval = sc_seq_or(do_prev_block, 0);
+	else if (retval == controldown)
+	    retval = sc_seq_or(do_next_block, 0);
 #endif
 
 	/* If our result is an extended keypad value (i.e. a value
@@ -696,8 +700,9 @@ int convert_sequence(const int *seq, size_t seq_len)
 		if (seq_len >= 5) {
 		    switch (seq[4]) {
 			case 'A': /* Esc O 1 ; 5 A == Ctrl-Up on Terminal. */
+			    return CONTROL_UP;
 			case 'B': /* Esc O 1 ; 5 B == Ctrl-Down on Terminal. */
-			    return arrow_from_abcd(seq[4]);
+			    return CONTROL_DOWN;
 			case 'C': /* Esc O 1 ; 5 C == Ctrl-Right on Terminal. */
 			    return CONTROL_RIGHT;
 			case 'D': /* Esc O 1 ; 5 D == Ctrl-Left on Terminal. */
@@ -766,8 +771,9 @@ int convert_sequence(const int *seq, size_t seq_len)
 		    case 'Y': /* Esc O Y == F10 on Mach console. */
 			return KEY_F(10);
 		    case 'a': /* Esc O a == Ctrl-Up on rxvt. */
+			return CONTROL_UP;
 		    case 'b': /* Esc O b == Ctrl-Down on rxvt. */
-			return arrow_from_abcd(seq[1]);
+			return CONTROL_DOWN;
 		    case 'c': /* Esc O c == Ctrl-Right on rxvt. */
 			return CONTROL_RIGHT;
 		    case 'd': /* Esc O d == Ctrl-Left on rxvt. */
@@ -841,8 +847,9 @@ int convert_sequence(const int *seq, size_t seq_len)
 	    case 'o':
 		switch (seq[1]) {
 		    case 'a': /* Esc o a == Ctrl-Up on Eterm. */
+			return CONTROL_UP;
 		    case 'b': /* Esc o b == Ctrl-Down on Eterm. */
-			return arrow_from_abcd(seq[1]);
+			return CONTROL_DOWN;
 		    case 'c': /* Esc o c == Ctrl-Right on Eterm. */
 			return CONTROL_RIGHT;
 		    case 'd': /* Esc o d == Ctrl-Left on Eterm. */
@@ -895,8 +902,9 @@ int convert_sequence(const int *seq, size_t seq_len)
 		if (seq_len >= 5) {
 		    switch (seq[4]) {
 			case 'A': /* Esc [ 1 ; 5 A == Ctrl-Up on xterm. */
+			    return CONTROL_UP;
 			case 'B': /* Esc [ 1 ; 5 B == Ctrl-Down on xterm. */
-			    return arrow_from_abcd(seq[4]);
+			    return CONTROL_DOWN;
 			case 'C': /* Esc [ 1 ; 5 C == Ctrl-Right on xterm. */
 			    return CONTROL_RIGHT;
 			case 'D': /* Esc [ 1 ; 5 D == Ctrl-Left on xterm. */
-- 
GitLab