From c95731ec5121ac1674e86af9b6d3b43680e8a5fb Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Wed, 15 Sep 2021 12:56:27 +0200
Subject: [PATCH V2] cutting: copy anchors into the cutbuffer, so that undo can
 restore them

When a segment of text is extracted, copy any anchors that it has
into the cutbuffer, so that they get saved in the undo item, so that
an undo will put these anchors back on the lines where they were,
instead of leaving an inherited anchor at the top of the segment.

And when text is pasted, clear any anchors in it, so that they will
not travel nor multiply.

This fixes https://savannah.gnu.org/bugs/?61162.

Bug existed since version 5.0, since anchors were introduced.


V2: - fix case of anchor on last line of region
    - fix case of cut-to-EOF
    - add two comments

---
 src/cut.c         | 22 ++++++++++++++++++++--
 src/definitions.h |  1 +
 src/nano.c        |  2 +-
 src/text.c        |  9 +++++++++
 4 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/src/cut.c b/src/cut.c
index 231853a3..8a6158bd 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -249,6 +249,7 @@ void extract_segment(linestruct *top, size_t top_x, linestruct *bot, size_t bot_
 	bool same_line = (openfile->mark == top);
 	bool post_marked = (openfile->mark && (openfile->mark->lineno > top->lineno ||
 						(same_line && openfile->mark_x > top_x)));
+	static bool inherited_anchor = FALSE;
 	bool had_anchor = top->has_anchor;
 
 	if (top == bot && top_x == bot_x)
@@ -269,7 +270,9 @@ void extract_segment(linestruct *top, size_t top_x, linestruct *bot, size_t bot_
 		taken = top;
 		last = make_new_node(NULL);
 		last->data = copy_of("");
-
+#ifndef NANO_TINY
+		last->has_anchor = bot->has_anchor;
+#endif
 		last->prev = bot->prev;
 		bot->prev->next = last;
 		last->next = NULL;
@@ -309,11 +312,17 @@ void extract_segment(linestruct *top, size_t top_x, linestruct *bot, size_t bot_
 	if (cutbuffer == NULL) {
 		cutbuffer = taken;
 		cutbottom = last;
+#ifndef NANO_TINY
+		inherited_anchor = taken->has_anchor;
+#endif
 	} else {
 		cutbottom->data = nrealloc(cutbottom->data,
 							strlen(cutbottom->data) + strlen(taken->data) + 1);
 		strcat(cutbottom->data, taken->data);
-
+#ifndef NANO_TINY
+		cutbottom->has_anchor = taken->has_anchor && !inherited_anchor;
+		inherited_anchor |= taken->has_anchor;
+#endif
 		cutbottom->next = taken->next;
 		delete_node(taken);
 
@@ -686,6 +695,9 @@ void copy_text(void)
 /* Copy text from the cutbuffer into the current buffer. */
 void paste_text(void)
 {
+	/* Remember where the paste started. */
+	linestruct *was_current = openfile->current;
+	bool had_anchor = was_current->has_anchor;
 	ssize_t was_lineno = openfile->current->lineno;
 		/* The line number where we started the paste. */
 	size_t was_leftedge = 0;
@@ -708,6 +720,12 @@ void paste_text(void)
 	copy_from_buffer(cutbuffer);
 
 #ifndef NANO_TINY
+	/* Wipe any anchors in the pasted text, so that they don't move around. */
+	for (linestruct *line = was_current; line != openfile->current->next; line = line->next)
+		line->has_anchor = FALSE;
+
+	was_current->has_anchor = had_anchor;
+
 	update_undo(PASTE);
 #endif
 
diff --git a/src/definitions.h b/src/definitions.h
index 9a9c8550..cb864a7d 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -220,6 +220,7 @@
 #define INCLUDED_LAST_LINE    (1<<3)
 #define MARK_WAS_SET          (1<<4)
 #define CURSOR_WAS_AT_HEAD    (1<<5)
+#define HAD_ANCHOR_AT_START   (1<<6)
 #endif /* !NANO_TINY */
 
 /* Identifiers for the different menus. */
diff --git a/src/nano.c b/src/nano.c
index 3dd9f917..40409784 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -154,7 +154,7 @@ linestruct *copy_node(const linestruct *src)
 #endif
 	dst->lineno = src->lineno;
 #ifndef NANO_TINY
-	dst->has_anchor = FALSE;
+	dst->has_anchor = src->has_anchor;
 #endif
 
 	return dst;
diff --git a/src/text.c b/src/text.c
index 41d362cd..cd2f77fc 100644
--- a/src/text.c
+++ b/src/text.c
@@ -463,6 +463,10 @@ void undo_cut(undostruct *u)
 {
 	goto_line_posx(u->head_lineno, (u->xflags & WAS_WHOLE_LINE) ? 0 : u->head_x);
 
+	/* Clear an inherited anchor but not a user-placed one. */
+	if (!(u->xflags & HAD_ANCHOR_AT_START))
+		openfile->current->has_anchor = FALSE;
+
 	if (u->cutbuffer)
 		copy_from_buffer(u->cutbuffer);
 
@@ -1023,6 +1027,8 @@ void add_undo(undo_type action, const char *message)
 #endif
 	case CUT_TO_EOF:
 		u->xflags |= (INCLUDED_LAST_LINE | CURSOR_WAS_AT_HEAD);
+		if (openfile->current->has_anchor)
+			u->xflags |= HAD_ANCHOR_AT_START;
 		break;
 	case ZAP:
 	case CUT:
@@ -1044,6 +1050,9 @@ void add_undo(undo_type action, const char *message)
 			u->tail_x = 0;
 		} else
 			u->xflags |= CURSOR_WAS_AT_HEAD;
+		if ((openfile->mark && mark_is_before_cursor() && openfile->mark->has_anchor) ||
+				((!openfile->mark || !mark_is_before_cursor()) && openfile->current->has_anchor))
+			u->xflags |= HAD_ANCHOR_AT_START;
 		break;
 	case PASTE:
 		u->cutbuffer = copy_buffer(cutbuffer);
-- 
2.29.3

