From ffec65b8c3edd1caddfcbbc0e6711d4701210b50 Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Fri, 27 Mar 2020 11:36:07 +0100
Subject: [PATCH 2/3] new feature: "knots" -- a kind of bookmarks, that can be
 cycled through

With M-' a knot is placed at the current line -- or removed when there
already is one.  With M-PageUp / M-PageDown one can jump to the nearest
knot in the upward and downward direction.  The search for a knot wraps
at the top and bottom.  A knot is not visible in any way.

This fulfills https://savannah.gnu.org/bugs/?57577.
Requested-by: Ken Tyler <kent@werple.net.au>
---
 src/global.c | 13 +++++++++++++
 src/nano.c   |  2 ++
 src/nano.h   |  2 ++
 src/proto.h  |  3 +++
 src/text.c   | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 67 insertions(+)

diff --git a/src/global.c b/src/global.c
index e8fa4629..e94255aa 100644
--- a/src/global.c
+++ b/src/global.c
@@ -670,6 +670,9 @@ void shortcut_init(void)
 #ifndef NANO_TINY
 	const char *recordmacro_gist = N_("Start/stop recording a macro");
 	const char *runmacro_gist = N_("Run the last recorded macro");
+	const char *knot_gist = N_("Place or remove a knot at the current line");
+	const char *upknot_gist = N_("Jump backward to the nearest knot");
+	const char *downknot_gist = N_("Jump forward to the nearest knot");
 #endif
 	const char *case_gist = N_("Toggle the case sensitivity of the search");
 	const char *reverse_gist = N_("Reverse the direction of the search");
@@ -1023,6 +1026,13 @@ void shortcut_init(void)
 	add_to_funcs(run_macro, MMAIN,
 		N_("Run Macro"), WITHORSANS(runmacro_gist), BLANKAFTER, VIEW);
 
+	add_to_funcs(make_knot, MMAIN,
+		N_("Knot"), WITHORSANS(knot_gist), TOGETHER, VIEW);
+	add_to_funcs(up_to_knot, MMAIN,
+		N_("Up to knot"), WITHORSANS(upknot_gist), TOGETHER, VIEW);
+	add_to_funcs(down_to_knot, MMAIN,
+		N_("Down to knot"), WITHORSANS(downknot_gist), BLANKAFTER, VIEW);
+
 	add_to_funcs(zap_text, MMAIN,
 		N_("Zap Text"), WITHORSANS(zap_gist), BLANKAFTER, NOVIEW);
 
@@ -1188,6 +1198,9 @@ void shortcut_init(void)
 	add_to_sclist(MMAIN, "Sh-Tab", SHIFT_TAB, do_unindent, 0);
 	add_to_sclist(MMAIN, "M-:", 0, record_macro, 0);
 	add_to_sclist(MMAIN, "M-;", 0, run_macro, 0);
+	add_to_sclist(MMAIN, "M-'", 0, make_knot, 0);
+	add_to_sclist(MMAIN, "M-PgUp", ALT_PAGEUP, up_to_knot, 0);
+	add_to_sclist(MMAIN, "M-PgDn", ALT_PAGEDOWN, down_to_knot, 0);
 	add_to_sclist(MMAIN, "M-U", 0, do_undo, 0);
 	add_to_sclist(MMAIN, "M-E", 0, do_redo, 0);
 	add_to_sclist(MMAIN, "Sh-^Del", CONTROL_SHIFT_DELETE, chop_previous_word, 0);
diff --git a/src/nano.c b/src/nano.c
index cc101680..6fce11cd 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -90,6 +90,7 @@ linestruct *make_new_node(linestruct *prevnode)
 	newnode->multidata = NULL;
 #endif
 	newnode->lineno = (prevnode) ? prevnode->lineno + 1 : 1;
+	newnode->knotted = FALSE;
 
 	return newnode;
 }
@@ -162,6 +163,7 @@ linestruct *copy_node(const linestruct *src)
 	dst->multidata = NULL;
 #endif
 	dst->lineno = src->lineno;
+	dst->knotted = FALSE;
 
 	return dst;
 }
diff --git a/src/nano.h b/src/nano.h
index 3538750e..a0d25a76 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -292,6 +292,8 @@ typedef struct linestruct {
 	short *multidata;
 		/* Array of which multi-line regexes apply to this line. */
 #endif
+	bool knotted;
+		/* Whether the user has placed a knot at this line. */
 } linestruct;
 
 #ifndef NANO_TINY
diff --git a/src/proto.h b/src/proto.h
index 09fcd3de..6d7fc49b 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -491,6 +491,9 @@ void do_find_bracket(void);
 /* Most functions in text.c. */
 #ifndef NANO_TINY
 void do_mark(void);
+void make_knot(void);
+void up_to_knot(void);
+void down_to_knot(void);
 #endif
 void do_tab(void);
 #ifndef NANO_TINY
diff --git a/src/text.c b/src/text.c
index de5df9b5..33013146 100644
--- a/src/text.c
+++ b/src/text.c
@@ -57,6 +57,53 @@ void do_mark(void)
 		refresh_needed = TRUE;
 	}
 }
+
+/* Place a knot at the current line when none exists, otherwise remove it. */
+void make_knot(void)
+{
+	openfile->current->knotted = !openfile->current->knotted;
+
+	if (openfile->current->knotted)
+		statusbar(_("Placed knot"));
+	else
+		statusbar(_("Removed knot"));
+}
+
+/* Make the given line the current line, or report the knottiness. */
+void jump_to(linestruct *line)
+{
+	if (line != openfile->current) {
+		openfile->current = line;
+		openfile->current_x = 0;
+		openfile->placewewant = 0;
+		refresh_needed = TRUE;
+	} else if (openfile->current->knotted)
+		statusbar(_("This is the only knot"));
+	else
+		statusline(ALERT, _("There are no knots"));
+}
+
+/* Jump to the first knot before the current line; wrap around at the top. */
+void up_to_knot(void)
+{
+	linestruct *line = openfile->current;
+
+	do { line = (line->prev) ? line->prev : openfile->filebot;
+	} while (!line->knotted && line != openfile->current);
+
+	jump_to(line);
+}
+
+/* Jump to the first knot after the current line; wrap around at the bottom. */
+void down_to_knot(void)
+{
+	linestruct *line = openfile->current;
+
+	do { line = (line->next) ? line->next : openfile->filetop;
+	} while (!line->knotted && line != openfile->current);
+
+	jump_to(line);
+}
 #endif /* !NANO_TINY */
 
 /* Insert a tab.  If the TABS_TO_SPACES flag is set, insert the number
-- 
2.25.2

