emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: select yank via completion


From: Juri Linkov
Subject: Re: select yank via completion
Date: Fri, 20 Nov 2020 10:53:39 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (x86_64-pc-linux-gnu)

>> There is still a small problem:
>> sometimes it's handy to slightly edit a previous string
>> from kill-ring before inserting it.  But with completing-read
>> it's not easy to insert the space character, e.g. after 'M-p'
>> while editing, typing 'SPC' errors with "[No matches]"
>> and doesn't insert a space character.
>> 
>> Could you recommend a more lightweight version of 
>> completing-read that doesn't override the 'SPC' key?
>
> It's _always_ handy to be able to edit a kill-ring entry
> before inserting it into the minibuffer.

BTW, sometimes it's also handy to just browse the kill-ring
without immediately inserting an entry, to copy an edited entry
(that adds a new kill-ring entry) to paste later.  To allow
such browsing we need to remove "*" from the interactive spec of
'yank-pop'.

> It took decades to even get `SPC' to be self-inserting for
> `read-file-name'.  I raised the same issue for `completing-read' at
> that time (and before then).

So now a new patch let-binds (minibuffer-completing-file-name t)
around completing-read to allow inserting SPC, problem solved:

diff --git a/lisp/simple.el b/lisp/simple.el
index bb28145502..589b3648c2 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5360,29 +5360,83 @@ yank-pop
 "This command honors the `yank-handled-properties' and
 `yank-excluded-properties' variables, and the `yank-handler' text
 property, in the way that `yank' does."
-  (interactive "*p")
+  (interactive "p")
   (if (not (eq last-command 'yank))
-      (user-error "Previous command was not a yank"))
-  (setq this-command 'yank)
-  (unless arg (setq arg 1))
-  (let ((inhibit-read-only t)
-       (before (< (point) (mark t))))
-    (if before
-       (funcall (or yank-undo-function 'delete-region) (point) (mark t))
-      (funcall (or yank-undo-function 'delete-region) (mark t) (point)))
-    (setq yank-undo-function nil)
-    (set-marker (mark-marker) (point) (current-buffer))
-    (insert-for-yank (current-kill arg))
-    ;; Set the window start back where it was in the yank command,
-    ;; if possible.
-    (set-window-start (selected-window) yank-window-start t)
-    (if before
-       ;; This is like exchange-point-and-mark, but doesn't activate the mark.
-       ;; It is cleaner to avoid activation, even though the command
-       ;; loop would deactivate the mark because we inserted text.
-       (goto-char (prog1 (mark t)
-                    (set-marker (mark-marker) (point) (current-buffer))))))
-  nil)
+      (call-interactively 'yank-from-kill-ring)
+    (setq this-command 'yank)
+    (unless arg (setq arg 1))
+    (let ((inhibit-read-only t)
+          (before (< (point) (mark t))))
+      (if before
+          (funcall (or yank-undo-function 'delete-region) (point) (mark t))
+        (funcall (or yank-undo-function 'delete-region) (mark t) (point)))
+      (setq yank-undo-function nil)
+      (set-marker (mark-marker) (point) (current-buffer))
+      (insert-for-yank (current-kill arg))
+      ;; Set the window start back where it was in the yank command,
+      ;; if possible.
+      (set-window-start (selected-window) yank-window-start t)
+      (if before
+          ;; This is like exchange-point-and-mark, but doesn't activate the 
mark.
+          ;; It is cleaner to avoid activation, even though the command
+          ;; loop would deactivate the mark because we inserted text.
+          (goto-char (prog1 (mark t)
+                       (set-marker (mark-marker) (point) (current-buffer))))))
+    nil))
+
+(put 'yank-pop 'delete-selection t)
+
+(defvar yank-from-kill-ring-history)
+(defun yank-from-kill-ring (string)
+  "Insert the kill-ring item selected from the minibuffer history.
+Use minibuffer navigation and search commands to browse the kill-ring
+in the minibuffer history before typing RET to insert the item,
+or use completion on the elements of the kill-ring."
+  (interactive
+   (list (let* ((history-add-new-input nil)
+                (ellipsis (if (char-displayable-p ?…) "…" "..."))
+                ;; Remove keymaps from text properties of copied string,
+                ;; because typing RET in the minibuffer might call
+                ;; an irrelevant command from the map of copied string.
+                (yank-from-kill-ring-history
+                 (mapcar (lambda (s)
+                           (remove-list-of-text-properties
+                            0 (length s)
+                            '(
+                              keymap local-map action mouse-action
+                              button category help-args)
+                            s)
+                           s)
+                         kill-ring))
+                (completions
+                 (mapcar (lambda (s)
+                           (let* ((s (query-replace-descr s))
+                                  (b 0))
+                             ;; Add ellipsis on leading whitespace
+                             (when (string-match "\\`[[:space:]]+" s)
+                               (setq b (match-end 0))
+                               (add-text-properties 0 b `(display ,ellipsis) 
s))
+                             (when (> (length s) (- 40 b))
+                               (add-text-properties
+                                (min (+ b 40) (length s)) (length s)
+                                `(display ,ellipsis) s))
+                             s))
+                         yank-from-kill-ring-history))
+                ;; Allow ‘SPC’ to be inserted literally.
+                (minibuffer-completing-file-name t))
+           (completing-read "Yank from kill-ring: "
+                            (lambda (string pred action)
+                              (if (eq action 'metadata)
+                                  ;; Keep sorted by recency
+                                  '(metadata (display-sort-function . 
identity))
+                                (complete-with-action action completions 
string pred)))
+                            nil nil nil
+                            'yank-from-kill-ring-history))))
+  (setq yank-window-start (window-start))
+  (push-mark)
+  (insert-for-yank string))
+
+(put 'yank-from-kill-ring 'delete-selection t)
 
 (defun yank (&optional arg)
   "Reinsert (\"paste\") the last stretch of killed text.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]