Subject: Re: bug#69097: [PATCH] Add 'kill-region-or-word' command

From: Philip Kaludercic <[email-masked]>
Date: Mon, 09 Sep 2024 19:23:32 +0000

Sean Whitton <[email-masked]> writes:

> Hello Eli,
>
> Any comments on v4?  I'd like to commit to unblock Philip.  Thanks!

Here is my updated patch.  If you want to, you can push both at once so
that we can close the issue more quickly as soon as Eli has time to take
a look:

>From 0f19dadb75a01873cb3f40a6addd825e63c266ce Mon Sep 17 00:00:00 2001
From: Philip Kaludercic <[email-masked]>
Date: Tue, 3 Sep 2024 18:29:56 +0200
Subject: [PATCH] Allow 'kill-region' kill the last word when there is no
 region

* etc/NEWS: Document the new user option.
* lisp/simple.el (kill-region-dwim): Add new option.
(kill-region): Respect 'kill-region-dwim'.  (Bug#69097)
---
 etc/NEWS       |  7 +++++++
 lisp/simple.el | 41 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index add438e8b6a..8cde2c294a9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -129,6 +129,13 @@ Unix-words are words separated by whitespace regardless of the buffer's
 syntax table.  In a Unix terminal or shell, C-w kills by Unix-word.
 The new commands 'unix-word-rubout' and 'unix-filename-rubout' allow
 you to bind keys to operate more similarly to the terminal.
+
+---
+** New user option 'kill-word-dwim'.
+This option will modify the fall-back behaviour of 'kill-region' if no
+region is active, and will kill the last word instead of raising an
+error.
+
 
 * Changes in Specialized Modes and Packages in Emacs 31.1
 
diff --git a/lisp/simple.el b/lisp/simple.el
index bbb13c1b471..d1be33ce87d 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5817,6 +5817,19 @@ kill-read-only-ok
   :type 'boolean
   :group 'killing)
 
+(defcustom kill-region-dwim nil
+  "Behaviour when `kill-region' is invoked without an active region.
+If set to nil (default), the behaviour of `kill-region' stays the same.
+If set to `emacs-word', then kill the last word as defined by the
+current major mode.  If set to `unix-word', then kill the last word in
+the style of a shell like Bash, disregarding the major mode like with
+`unix-word-rubout'."
+  :type '(choice (const :tag "Kill a word like `backward-kill-word'" emacs-word)
+                 (const :tag "Kill a word like Bash would" unix-word)
+                 (const :tag "Do not kill anything" nil))
+  :group 'killing
+  :version "31.1")
+
 (defun kill-region (beg end &optional region)
   "Kill (\"cut\") text between point and mark.
 This deletes the text from the buffer and saves it in the kill ring.
@@ -5843,21 +5856,35 @@ kill-region
  (To delete text, use `delete-region'.)
 Supply two arguments, character positions BEG and END indicating the
  stretch of text to be killed.  If the optional argument REGION is
- non-nil, the function ignores BEG and END, and kills the current
+ `region', the function ignores BEG and END, and kills the current
  region instead.  Interactively, REGION is always non-nil, and so
- this command always kills the current region."
+ this command always kills the current region.  It is possible to
+ override this behaviour by customising the user option
+ `kill-region-dwim'."
   ;; Pass mark first, then point, because the order matters when
   ;; calling `kill-append'.
   (interactive (progn
                  (let ((beg (mark))
                        (end (point)))
-                   (unless (and beg end)
+                   (cond
+                    ((and kill-region-dwim (not (use-region-p)))
+                     (list beg end kill-region-dwim))
+                    ((not (or beg end))
                      (user-error "The mark is not set now, so there is no region"))
-                   (list beg end 'region))))
+                    ((list beg end 'region))))))
+
   (condition-case nil
-      (let ((string (if region
-                        (funcall region-extract-function 'delete)
-                      (filter-buffer-substring beg end 'delete))))
+      (let ((string (cond
+                     ((memq region '(unix-word emacs-word))
+                      (let ((end (point)))
+                        (save-excursion
+                          (if (eq region 'emacs-word)
+                              (forward-word -1)
+                            (forward-unix-word -1))
+                          (filter-buffer-substring (point) end 'delete))))
+                     (region
+                      (funcall region-extract-function 'delete))
+                     ((filter-buffer-substring beg end 'delete)))))
 	(when string			;STRING is nil if BEG = END
 	  ;; Add that string to the kill ring, one way or another.
 	  (if (eq last-command 'kill-region)
-- 
2.45.2

-- 
	Philip Kaludercic on siskin