From: Eli Zaretskii <eliz@gnu.org>
Date: Sat, 29 Jan 2022 10:19:23 +0000 (+0200)
Subject: Allow <Delete> key to delete entire grapheme clusters
X-Git-Tag: emacs-29.0.90~2672
X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=0a5ece3da157c5a33023dfdf6211fc34015f197d;p=emacs.git

Allow <Delete> key to delete entire grapheme clusters

* lisp/simple.el (delete-forward-char): If deleting forward,
delete complete grapheme clusters as single units.

* etc/NEWS: Announce the change.
---

diff --git a/etc/NEWS b/etc/NEWS
index d1eaf08036b..99e25331942 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -119,6 +119,16 @@ This is to open up the 'C-x 8 .' map to bind further characters there.
 This is for compatibility with the shell versions of these commands,
 which don't handle options like '--help' in any special way.
 
+---
+** The 'delete-forward-char' command now deletes by grapheme clusters.
+This command is by default bound to the <Delete> function key
+(a.k.a. <deletechar>).  When invoked without a prefix argument or with
+a positive prefix numeric argument, the command will now delete
+complete grapheme clusters produced by character composition.  For
+example, if point is before an Emoji sequence, pressing <Delete> will
+delete the entire sequence, not just a single character at its
+beginning.
+
 
 * Changes in Emacs 29.1
 
diff --git a/lisp/simple.el b/lisp/simple.el
index 00669ac6349..3cf3024184b 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1292,6 +1292,11 @@ If Transient Mark mode is enabled, the mark is active, and N is 1,
 delete the text in the region and deactivate the mark instead.
 To disable this, set variable `delete-active-region' to nil.
 
+If N is positive, characters composed into a single grapheme cluster
+count as a single character and are deleted together.  Thus,
+\"\\[universal-argument] 2 \\[delete-forward-char]\" when two grapheme clusters follow point will
+delete the characters composed into both of the grapheme clusters.
+
 Optional second arg KILLFLAG non-nil means to kill (save in kill
 ring) instead of delete.  If called interactively, a numeric
 prefix argument specifies N, and KILLFLAG is also set if a prefix
@@ -1312,6 +1317,21 @@ the actual saved text might be different from what was killed."
 	     (kill-region (region-beginning) (region-end) 'region)
 	   (funcall region-extract-function 'delete-only)))
 
+	;; For forward deletion, treat composed characters as a single
+	;; character to delete.
+        ((>= n 1)
+         (let ((pos (point))
+               start cmp)
+           (setq start pos)
+           (while (> n 0)
+             ;; 'find-composition' will return (FROM TO ....) or nil.
+             (setq cmp (find-composition pos))
+             (if cmp
+                 (setq pos (cadr cmp))
+               (setq pos (1+ pos)))
+             (setq n (1- n)))
+           (delete-char (- pos start) killflag)))
+
 	;; Otherwise, do simple deletion.
 	(t (delete-char n killflag))))