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: Thu, 19 Nov 2020 09:54:34 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (x86_64-pc-linux-gnu)

>> BTW, there is another TODO item in minibuffer.el:
>>
>> ;;   - indicate how to display the completions in *Completions* (turn
>> ;;     \n into something else, add special boundaries between
>> ;;     completions).  E.g. when completing from the kill-ring.
>>
>> Such pre-processing can be performed by the caller.
>> For a long time I used the command 'yank-from-kill-ring'
>> bound to 'C-M-y' that relies on query-replace-descr
>> to display newlines as ^J and displays long completions
>> with an ellipsis at the end and an ellipsis on leading whitespace.
>
> Looks great, thanks.  Any reason you bind it to C-M-y instead
> of to "M-y that doesn't immediately follow C-y"?

Oops, I don't remember why I forgot about no-op M-y.

Here is a better patch.  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?

diff --git a/lisp/simple.el b/lisp/simple.el
index bb28145502..32850b4b7c 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5362,27 +5362,29 @@ yank-pop
 property, in the way that `yank' does."
   (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)
 
 (defun yank (&optional arg)
   "Reinsert (\"paste\") the last stretch of killed text.
@@ -5449,6 +5451,56 @@ rotate-yank-pointer
 With ARG, rotate that many kills forward (or backward, if negative)."
   (interactive "p")
   (current-kill arg))
+
+(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)
+                ;; 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 "…") s))
+                             (when (> (length s) (- 40 b))
+                               (add-text-properties
+                                (min (+ b 40) (length s)) (length s)
+                                `(display "…") s))
+                             s))
+                         yank-from-kill-ring-history)))
+           (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)
+
 
 ;; Some kill commands.
 

reply via email to

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