From bd2b89fbe5a0373eb6f164f3275056cb7340a5f0 Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Sun, 17 May 2020 13:45:40 +0200
Subject: [PATCH] editing: an attempt to shy away from the middle of a multibye
 character

Deleting, cutting, and pasting now work fine.  And undoing and
redoing too.  But... this gets too complicated for something
that should normally never occur, so let's just forget it.
(When fiddling around with invalid bytes, the user will soon
enough understand that the cursor can sometimes be elsewhere
than where it appears to be.  And anyway, the same happens
when sitting on a character followed by a combining one:
the cursor does not move when you press <Right>.)

This would address https://savannah.gnu.org/bugs/?48489.
---
 src/cut.c  | 30 ++++++++++++++++++++++++++++++
 src/text.c | 10 ++++++++++
 2 files changed, 40 insertions(+)

diff --git a/src/cut.c b/src/cut.c
index 435a9919..1dfbaacb 100644
--- a/src/cut.c
+++ b/src/cut.c
@@ -24,6 +24,28 @@
 
 #include <string.h>
 
+#ifndef NANO_TINY
+/* When in the middle of a multibyte sequence, step to its beginning. */
+void shun_midway_multibyte(void)
+{
+#ifdef ENABLE_UTF8
+	/* If this could be a multibyte sequence, ensure we are not halfway. */
+	if ((signed char)openfile->current->data[openfile->current_x] < 0) {
+		size_t was_x = openfile->current_x;
+
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
+
+		if (openfile->current_x != was_x) {
+			openfile->totsize += openfile->current_x - was_x;
+			openfile->current_undo->newsize = openfile->totsize;
+			openfile->placewewant = xplustabs();
+		}
+	}
+#endif
+}
+#endif /* NANO_TINY */
+
 /* Delete the character under the cursor. */
 void do_deletion(undo_type action)
 {
@@ -101,6 +123,10 @@ void do_deletion(undo_type action)
 		/* We're at the end-of-file: nothing to do. */
 		return;
 
+#ifndef NANO_TINY
+	shun_midway_multibyte();
+#endif
+
 	/* Adjust the file size, and remember it for a possible redo. */
 	openfile->totsize--;
 #ifndef NANO_TINY
@@ -207,6 +233,7 @@ void chop_word(bool forward)
 	/* Now kill the marked region and a word is gone. */
 	add_undo(CUT, NULL);
 	do_snip(TRUE, FALSE, FALSE);
+	shun_midway_multibyte();
 	update_undo(CUT);
 
 	/* Discard the cut word and restore the cutbuffer. */
@@ -512,6 +539,7 @@ void cut_text(void)
 	}
 
 	do_snip(openfile->mark != NULL, FALSE, FALSE);
+	shun_midway_multibyte();
 
 	update_undo(CUT);
 #else
@@ -557,6 +585,7 @@ void zap_text(void)
 	cutbuffer = openfile->current_undo->cutbuffer;
 
 	do_snip(openfile->mark != NULL, FALSE, TRUE);
+	shun_midway_multibyte();
 
 	update_undo(ZAP);
 	wipe_statusbar();
@@ -701,6 +730,7 @@ void paste_text(void)
 
 #ifndef NANO_TINY
 	update_undo(PASTE);
+	shun_midway_multibyte();
 #endif
 
 	/* If we pasted less than a screenful, don't center the cursor. */
diff --git a/src/text.c b/src/text.c
index 088f3012..e7ed6a8c 100644
--- a/src/text.c
+++ b/src/text.c
@@ -718,6 +718,8 @@ void do_redo(void)
 		memmove(line->data + u->head_x, line->data + u->head_x + strlen(u->strdata),
 						strlen(line->data + u->head_x) - strlen(u->strdata) + 1);
 		goto_line_posx(u->head_lineno, u->head_x);
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
 		break;
 	case JOIN:
 		redidmsg = _("line join");
@@ -733,6 +735,8 @@ void do_redo(void)
 		unlink_node(line->next);
 		renumber_from(line);
 		goto_line_posx(u->tail_lineno, u->tail_x);
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
 		break;
 	case REPLACE:
 		redidmsg = _("replacement");
@@ -756,15 +760,21 @@ void do_redo(void)
 	case ZAP:
 		redidmsg = _("erasure");
 		redo_cut(u);
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
 		break;
 	case CUT_TO_EOF:
 	case CUT:
 		redidmsg = _("cut");
 		redo_cut(u);
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
 		break;
 	case PASTE:
 		redidmsg = _("paste");
 		redo_paste(u);
+		openfile->current_x = step_right(openfile->current->data, openfile->current_x);
+		openfile->current_x = step_left(openfile->current->data, openfile->current_x);
 		break;
 	case INSERT:
 		redidmsg = _("insertion");
-- 
2.25.4

