Subject: [PATCH] Add 'forward-delete-char-hungry' command

From: Elijah Gabe Pérez <[email-masked]>
Date: Thu, 26 Jun 2025 17:58:27 -0600

Tags: patch

Hello,

This patch adds a new command similar to
`backward-delete-char-untabify`, but intended to be used forward.

I had originally planned to add the `untabify` method like
`backward-delete-char-untabify`, but I found a few bugs while testing it
(the reason is annotated in the patch), so I decided to keep the hungry
methods.

>From 7a88ca429fa857ec10b2985e17a3ceaf77d97681 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= <[email-masked]>
Date: Wed, 14 May 2025 17:58:47 -0600
Subject: [PATCH] Add 'forward-delete-char-hungry' command (Bug#)

* etc/NEWS: Announce changes.
* lisp/simple.el (forward-delete-char-hungry-method): New user
option.
(forward-delete-char-hungry): New command.
---
 etc/NEWS       | 16 +++++++++++++++
 lisp/simple.el | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+)

diff --git a/etc/NEWS b/etc/NEWS
index eb85baae341..17d156c3c20 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -619,6 +619,22 @@ sentence.  The new command 'fill-region-as-paragraph-semlf' fills a
 region of text using semantic linefeeds as if the region were a single
 paragraph.
 
+---
+** New command 'forward-delete-char-hungry'.
+This command delete all whitespace forward or only one character.
+The exact behavior depends on 'forward-delete-char-hungry-method'.
+
+---
+** New user option 'forward-delete-char-hungry-method'.
+This option controls the method for used in 'forward-delete-char-hungry'
+and can have the following values:
+
+'hungry' for delete all whitespace, both tabs and spaces.
+
+'all' for delete all whitespace, including tabs, spaces and newlines.
+
+nil (the default) for just delete one character, including tabs.
+
 
 * Changes in Specialized Modes and Packages in Emacs 31.1
 
diff --git a/lisp/simple.el b/lisp/simple.el
index fa173b26289..701597569e1 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -6667,6 +6667,59 @@ backward-delete-char-untabify
       ;; Avoid warning about delete-backward-char
       (with-no-warnings (delete-backward-char n killp)))))
 
+(defcustom forward-delete-char-hungry-method nil
+  "The method for hungry deleting forward.
+Can be `hungry' -- delete all whitespace, both tabs and spaces;
+       `all' -- delete all whitespace, including tabs, spaces and newlines;
+       nil -- just delete one character, including tabs."
+  :type '(choice (const hungry) (const all) (const nil))
+  :version "31.1"
+  :group 'killing)
+
+(defun forward-delete-char-hungry (arg &optional killp)
+  "Delete all whitespace forward or only one character.
+The exact behavior depends on `forward-delete-char-hungry-method'.
+
+Delete ARG chars, and kill (save in kill ring) if KILLP is non-nil.
+
+This is similar to `backward-delete-char-untabify' which see."
+  (interactive "*p\nP")
+  (if (and (use-region-p)
+           delete-active-region
+           (= arg 1))
+      ;; Delete the text of the region and deactivate the mark, in the
+      ;; same way that `delete-forward-char' does.
+      (with-no-warnings (delete-forward-char 1 killp))
+
+    ;; FIXME: The untabify method makes cursor move to end of
+    ;;        tabs/spaces when `undo' or `undo-redo' are used, which is
+    ;;        annoying.
+
+    ;; (when (eq forward-delete-char-hungry-method 'untabify)
+    ;;   (let ((count arg))
+    ;;     (save-excursion
+    ;;       (while (and (> count 0) (not (eobp)))
+    ;;         (if (= (following-char) ?\t)
+    ;;             (let ((col (current-column)))
+    ;;     	  (forward-char 1)
+    ;;     	  (setq col (- (current-column) col))
+    ;;     	  (insert-char ?\s col)
+    ;;     	  (delete-char -1)))
+    ;;         (forward-char 1)
+    ;;         (setq count (1- count))))))
+    (let* ((skip (cond ((eq forward-delete-char-hungry-method 'hungry) " \t")
+                       ((eq forward-delete-char-hungry-method 'all) " \t\n\r")))
+           (n (if skip
+                  (let* ((oldpt (point))
+                         (wh (- (save-excursion
+                                  (skip-chars-forward skip)
+                                  (constrain-to-field nil oldpt))
+                                oldpt)))
+                    (+ arg (if (zerop wh) 0 (1- wh))))
+                arg)))
+      ;; Avoid warning about delete-forward-char
+      (with-no-warnings (delete-forward-char n killp)))))
+
 (defun char-uppercase-p (char)
   "Return non-nil if CHAR is an upper-case character.
 If the Unicode tables are not yet available, e.g. during bootstrap,
-- 
2.50.0

-- 
                                          - E.G via GNU Emacs and Org.