From 9e034d3921653cef9211e2be6f37cb5aa2bf4db2 Mon Sep 17 00:00:00 2001
From: bw
Date: Sat, 16 May 2026 17:24:56 -0400
Subject: [PATCH 1/2] hui:copy-message-length - Add this defcustom to limit
length of msg
If `hui:copy-message-length' is set to 0, disables messages when {C-w}
or {M-w} is called interactively and Hyperbole is active.
Do not copy or kill a 'thing' if point is on a whitespace character.
Update doc of hui and kotl-mode copy and kill commands to reflect operation
on any kind of 'thing', not just delimited things.
hui:indicate-copied-region - Add to centralize display of copied text.
---
ChangeLog | 17 +++++++++++
hui.el | 72 ++++++++++++++++++++++++++++++++--------------
kotl/kotl-mode.el | 15 ++++++----
man/hyperbole.texi | 19 ++++++------
4 files changed, 87 insertions(+), 36 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index bb89895f..85c48f2a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
2026-05-16 Bob Weiner
+* man/hyperbole.texi (Default Hyperbole Bindings): Update doc for {M-w} to copy
+ things, not just delimited things.
+ (Koutliner Keys): Update doc for {C-w} to kill/copy things,
+ not just delimited things.
+
+* hui.el (hui:copy-to-register, hui:kill-ring-save): Change "Saved selectable
+ thing" to "Copied selectable thing" to better match regurlar emacs. Also
+ call 'query-replace-descr' to prevent newline display within 'thing'.
+ (hui:indicate-copied-region): Add to centralize display of copied
+ text and call from the above two functions.
+ (hui:copy-message-length): Add this defcustom with default of 40
+ and use in the prior function.
+ (hui:kill-ring-save, hui:copy-to-register, hui:kill-region): Don't
+ copy a 'thing' if point is on whitespace. Update doc strings.
+ kotl/kotl-mode.el (kotl-mode:kill-region): Don't kill a 'thing' if point
+ is on whitespace.
+
* kotl/kotl-mode.el (kotl-mode:split-cell): Fix to ensure point within a
valid cell position and fix to not modify label-separator and leave
a stray character in the original cell when splitting at the first
diff --git a/hui.el b/hui.el
index 7b34b23d..66122505 100644
--- a/hui.el
+++ b/hui.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 19-Sep-91 at 21:42:03
-;; Last-Mod: 15-Mar-26 at 17:46:14 by Bob Weiner
+;; Last-Mod: 16-May-26 at 17:37:57 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -62,6 +62,10 @@
;;; Public variables
;;; ************************************************************************
+(defcustom hui:copy-message-length 40
+ "Maximum character length of the message displayed after copying text.
+ Set to 0 for no message.")
+
(defcustom hui:ebut-prompt-for-action nil
"Non-nil prompts for a button-specific action on explicit button creation."
:type 'boolean
@@ -108,9 +112,14 @@ copying some text between START and END, but we're copying the region.
Interactively, reads the register using `register-read-with-preview'.
-If called interactively, `transient-mark-mode' is non-nil, and
-there is no active region, copy any delimited selectable thing at
-point; see `hui:delimited-selectable-thing'."
+If called interactively, `transient-mark-mode' is non-nil, there is no
+active region, and point is not on a whitespace character, then copy the
+selectable thing at point including any delimiters; see
+`hui:selectable-thing-and-bounds'.
+
+Interactively, display a message with `hui:copy-message-length' characters
+of the text copied unless that variable is set to 0, in which case no
+message is shown."
(interactive (list (register-read-with-preview "Copy to register: ")
(when mark-active (region-beginning))
(when mark-active (region-end))
@@ -133,6 +142,7 @@ point; see `hui:delimited-selectable-thing'."
((and interactive-flag
transient-mark-mode
(not (use-region-p))
+ (not (looking-at "\\s-"))
(prog1 (setq thing-and-bounds
(hui:selectable-thing-and-bounds)
thing (nth 1 thing-and-bounds)
@@ -151,10 +161,7 @@ point; see `hui:delimited-selectable-thing'."
(setq deactivate-mark t)
(cond (delete-flag)
(interactive-flag
- (cond (thing
- (message "Saved selectable thing: %s" thing))
- ((mark t)
- (indicate-copied-region))))))))
+ (hui:indicate-copied-region thing))))))
;; In "hyperbole.el", use this to override the {C-w} command from
;; either "completion.el" or "simple.el" when hyperbole-mode is active
@@ -174,7 +181,11 @@ Any command that calls this function is a \"kill command\".
If the previous command was also a kill command,
the text killed this time appends to the text killed last time
to make one entry in the kill ring.
-Patched to remove the most recent completion."
+Patched to remove the most recent completion.
+
+When `transient-mark-mode' is non-nil, there is no active region, and point
+is not on a whitespace character, then kill the selectable thing at point
+including any delimiters; see `hui:selectable-thing-and-bounds'."
;; Pass mark first, then point, because the order matters when
;; calling `kill-append'.
(interactive (list (when mark-active (mark))
@@ -191,7 +202,8 @@ Patched to remove the most recent completion."
;; if in one of `hui-select-ignore-quoted-sexp-modes'.
((let* ((major-mode 'fundamental-mode)
thing-and-bounds)
- (when (setq thing-and-bounds (hui:selectable-thing-and-bounds))
+ (when (and (not (looking-at "\\s-"))
+ (setq thing-and-bounds (hui:selectable-thing-and-bounds)))
(setq beg (nth 2 thing-and-bounds)
end (nth 3 thing-and-bounds)
region nil)
@@ -203,8 +215,8 @@ Patched to remove the most recent completion."
(hui:kill-region-internal beg end region)))
;; In "hyperbole.el", use this to override the {M-w} command from
-;; "simple.el" when hyperbole-mode is active to allow copying kcell
-;; references, active regions and delimited areas (like sexpressions).
+;; "simple.el" when `hyperbole-mode' is active to allow copying kcell
+;; references, active regions and things (like sexpressions).
;;;###autoload
(defun hui:kill-ring-save (beg end &optional region)
"Save the active region or thing at point as if killed, but don't kill it.
@@ -212,9 +224,10 @@ In Transient Mark mode, deactivate the mark.
If `interprogram-cut-function' is non-nil, also save the text for a window
system cut and paste.
-If called interactively, `transient-mark-mode' is non-nil, and
-there is no active region, copy any delimited selectable thing at
-point; see `hui:delimited-selectable-thing'.
+If called interactively, `transient-mark-mode' is non-nil, there is no
+active region, and point is not on a whitespace character, then copy the
+selectable thing at point including any delimiters; see
+`hui:selectable-thing-and-bounds'.
If you want to append the killed region to the last killed text,
use \\[append-next-kill] before \\[kill-ring-save].
@@ -229,7 +242,11 @@ non-nil, in which case ignore BEG and END, and save the current
region instead.
This command is similar to `copy-region-as-kill', except that it gives
-visual feedback indicating the extent of the region being copied."
+visual feedback indicating the extent of the region being copied.
+
+Interactively, display a message with `hui:copy-message-length' characters
+of the text copied unless that variable is set to 0, in which case no
+message is shown."
;; Pass mark first, then point, because the order matters when
;; calling `kill-append'.
(interactive (list (when mark-active (mark))
@@ -254,7 +271,8 @@ visual feedback indicating the extent of the region being copied."
;; suppressing use of `hui-select-syntax-table'
;; if in one of `hui-select-ignore-quoted-sexp-modes'.
(let ((major-mode 'fundamental-mode))
- (setq thing (nth 1 (hui:selectable-thing-and-bounds))))
+ (unless (looking-at "\\s-")
+ (setq thing (nth 1 (hui:selectable-thing-and-bounds)))))
(if (stringp thing)
(progn (kill-new thing)
(setq deactivate-mark t))
@@ -267,10 +285,7 @@ visual feedback indicating the extent of the region being copied."
;; This use of `called-interactively-p' is correct because the
;; code it controls just gives the user visual feedback.
(when (called-interactively-p 'interactive)
- (cond (thing
- (message "Saved selectable thing: %s" thing))
- ((mark t)
- (indicate-copied-region))))))
+ (hui:indicate-copied-region thing))))
;;; ************************************************************************
;;; Public functions
@@ -1522,6 +1537,21 @@ runs this command."
(hui:ibut-message edit-flag))
edit-flag)))
+(defun hui:indicate-copied-region (thing &optional message-len)
+ "Indicate that string THING or the region text has been copied.
+Should be used when a command is called interactively."
+ (unless message-len
+ (setq message-len hui:copy-message-length))
+ (cond ((zerop message-len)
+ nil)
+ (thing
+ ;; Don't say "killed" or "saved"; that is misleading.
+ (message "Copied selectable thing: %s"
+ ;; Don't show newlines literally
+ (query-replace-descr
+ (seq-take thing message-len))))
+ ((mark t)
+ (indicate-copied-region message-len))))
;;; ************************************************************************
;;; Private functions - used only within Hyperbole
diff --git a/kotl/kotl-mode.el b/kotl/kotl-mode.el
index 8c363eb6..f29c1636 100644
--- a/kotl/kotl-mode.el
+++ b/kotl/kotl-mode.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 6/30/93
-;; Last-Mod: 16-May-26 at 11:55:50 by Bob Weiner
+;; Last-Mod: 16-May-26 at 17:18:12 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -791,12 +791,14 @@ With optional COPY-FLAG equal to t, copy region to kill ring but does not
kill it. With COPY-FLAG any other non-nil value, return region as a
string without affecting kill ring.
-If called interactively, `transient-mark-mode' is non-nil, and
-there is no active region, copy any delimited selectable thing at
-point; see `hui:delimited-selectable-thing'.
+If called interactively, `transient-mark-mode' is non-nil, there is no
+active region, and point is not on a whitespace character, then kill/copy
+the selectable thing at point including any delimiters; see
+`hui:selectable-thing-and-bounds'.
-If the buffer is read-only and COPY-FLAG is nil, the region will not be deleted
-but it will be copied to the kill ring and then an error will be signaled.
+If the buffer is read-only and COPY-FLAG is nil, the region will not be
+deleted but it will be copied to the kill ring and then an error will be
+signaled.
If a completion is active, this aborts the completion only."
(interactive
@@ -819,6 +821,7 @@ If a completion is active, this aborts the completion only."
((and (memq this-command kill-commands)
transient-mark-mode
(not (use-region-p))
+ (not (looking-at "\\s-"))
(setq thing-and-bounds (hui:selectable-thing-and-bounds)
thing (nth 1 thing-and-bounds)
start (nth 2 thing-and-bounds)
diff --git a/man/hyperbole.texi b/man/hyperbole.texi
index 666392c8..425348c1 100644
--- a/man/hyperbole.texi
+++ b/man/hyperbole.texi
@@ -7,7 +7,7 @@
@c Author: Bob Weiner
@c
@c Orig-Date: 6-Nov-91 at 11:18:03
-@c Last-Mod: 29-Mar-26 at 23:39:42 by Bob Weiner
+@c Last-Mod: 16-May-26 at 17:20:08 by Bob Weiner
@c %**start of header (This is for running Texinfo on a region.)
@setfilename hyperbole.info
@@ -30,8 +30,8 @@
@set txicodequoteundirected
@set txicodequotebacktick
-@set UPDATED March 29, 2026
-@set UPDATED-MONTH March 2026
+@set UPDATED May 16, 2026
+@set UPDATED-MONTH May 2026
@set EDITION 9.0.2pre
@set VERSION 9.0.2pre
@@ -171,7 +171,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Edition 9.0.2pre
-Printed March 29, 2026.
+Printed May 16, 2026.
Published by the Free Software Foundation, Inc.
Author: Bob Weiner
@@ -213,7 +213,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@example
Edition 9.0.2pre
-March 29, 2026 @c AUTO-REPLACE-ON-SAVE
+May 16, 2026 @c AUTO-REPLACE-ON-SAVE
Published by the Free Software Foundation, Inc.
@@ -9506,7 +9506,7 @@ region only when it is active/highlighted. When there is no active region,
@item in a Koutline klink, copies the klink;
@item in a Koutline cell, outside any klink, copies a klink reference to the current cell;
@item on a Hyperbole button, copies the text of the button excluding delimiters;
-@item at the start of a paired delimiter, copy the text including the delimiters.
+@item on a thing (not at whitespace), copy the text of the thing including any delimiters.
@end enumerate
@cindex key binding, C-x r s
@@ -9935,9 +9935,10 @@ With optional COPY-P equal to t, copy region to kill ring but don't
kill it. With COPY-P any other non-nil value, return region as a
string without affecting the kill ring.
-If called interactively and there is no active region, copy any
-delimited selectable thing at point; see the documentation for
-@code{hui:delimited-selectable-thing}.
+If called interactively, @code{transient-mark-mode} is non-nil,
+there is no active region, and point is not on a whitespace character,
+then kill/copy the selectable thing at point including any delimiters;
+see @code{hui:selectable-thing-and-bounds}.
If the buffer is read-only and COPY-P is nil, the region will not be
deleted but it will be copied to the kill ring and then an error will be
From aff25ef74a013ca2aa6704f7489c18b402b6c9da Mon Sep 17 00:00:00 2001
From: bw
Date: Sat, 16 May 2026 17:50:41 -0400
Subject: [PATCH 2/2] hui:indicate-copied-region - Double quote 'thing' and add
ellipses
---
hui.el | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/hui.el b/hui.el
index 66122505..0d2e7f16 100644
--- a/hui.el
+++ b/hui.el
@@ -3,7 +3,7 @@
;; Author: Bob Weiner
;;
;; Orig-Date: 19-Sep-91 at 21:42:03
-;; Last-Mod: 16-May-26 at 17:37:57 by Bob Weiner
+;; Last-Mod: 16-May-26 at 17:49:30 by Bob Weiner
;;
;; SPDX-License-Identifier: GPL-3.0-or-later
;;
@@ -1545,11 +1545,14 @@ Should be used when a command is called interactively."
(cond ((zerop message-len)
nil)
(thing
- ;; Don't say "killed" or "saved"; that is misleading.
- (message "Copied selectable thing: %s"
- ;; Don't show newlines literally
- (query-replace-descr
- (seq-take thing message-len))))
+ (let ((thing-excerpt (seq-take thing message-len)))
+ ;; Don't say "killed" or "saved"; that is misleading.
+ (message "Copied selectable thing: \"%s%s\""
+ ;; Don't show newlines literally
+ (query-replace-descr thing-excerpt)
+ (if (< (length thing-excerpt) (length thing))
+ "..."
+ ""))))
((mark t)
(indicate-copied-region message-len))))