emacs-diffs
[Top][All Lists]
Advanced

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

master 83b5fcb: Yank items selected from kill-ring using completion and


From: Juri Linkov
Subject: master 83b5fcb: Yank items selected from kill-ring using completion and minibuffer history
Date: Tue, 24 Nov 2020 14:24:13 -0500 (EST)

branch: master
commit 83b5fcb0014896feda98917bcf198094131e82a6
Author: Juri Linkov <juri@linkov.net>
Commit: Juri Linkov <juri@linkov.net>

    Yank items selected from kill-ring using completion and minibuffer history
    
    * doc/emacs/killing.texi (Earlier Kills): Document standalone M-y.
    
    * doc/emacs/search.texi (Isearch Yank): Explain standalone M-y.
    
    * doc/lispref/text.texi (Yank Commands): Soften the wording of
    yank after another yank.
    
    * lisp/delsel.el: Put 'yank' property on yank-pop and yank-from-kill-ring.
    
    * lisp/isearch.el (isearch-yank-pop): Use with-isearch-suspended
    and read-from-kill-ring to read a string from the kill-ring and
    append it to the search string.
    
    * lisp/simple.el (yank-pop): Call yank-from-kill-ring and
    read-from-kill-ring when last-command is not 'yank' instead of
    signaling an error.  Remove "*" from interactive spec.  Update docstring.
    (read-from-kill-ring): New function.
    (yank-from-kill-ring): New command.
    
    https://lists.gnu.org/archive/html/emacs-devel/2020-11/msg00801.html
---
 doc/emacs/killing.texi |  11 ++++-
 doc/emacs/search.texi  |   9 ++--
 doc/lispref/text.texi  |   2 +-
 etc/NEWS               |   7 +++
 lisp/delsel.el         |   2 +
 lisp/isearch.el        |  17 +++++--
 lisp/simple.el         | 124 +++++++++++++++++++++++++++++++++++++++----------
 7 files changed, 138 insertions(+), 34 deletions(-)

diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi
index bd7dbb6..0bd18fd 100644
--- a/doc/emacs/killing.texi
+++ b/doc/emacs/killing.texi
@@ -362,7 +362,7 @@ through the possibilities.
 that was yanked and replaces it with the text from an earlier kill.
 So, to recover the text of the next-to-the-last kill, first use
 @kbd{C-y} to yank the last kill, and then use @kbd{M-y} to replace it
-with the previous kill.  @kbd{M-y} is allowed only after a @kbd{C-y}
+with the previous kill.  This works only after a @kbd{C-y}
 or another @kbd{M-y}.
 
   You can understand @kbd{M-y} in terms of a last-yank pointer which
@@ -394,6 +394,15 @@ pointer remains at the same place in the kill ring, so 
repeating
   When you call @kbd{C-y} with a numeric argument, that also sets the
 last-yank pointer to the entry that it yanks.
 
+  Alternatively, when the previous command was not a yank command,
+@kbd{M-y} activates the minibuffer where you can browse previous kills
+using the minibuffer history commands (@pxref{Minibuffer History}), or
+you can use completion commands (@pxref{Completion}) on a list of
+previously killed blocks of text from the kill ring.  Exiting the
+minibuffer will insert the selected text to the buffer.  With a plain
+prefix argument (@kbd{C-u M-y}), this command leaves the cursor in
+front of the inserted text, and sets the mark at the end.
+
 @node Appending Kills
 @subsection Appending Kills
 
diff --git a/doc/emacs/search.texi b/doc/emacs/search.texi
index 0612c13..5be45ce 100644
--- a/doc/emacs/search.texi
+++ b/doc/emacs/search.texi
@@ -295,9 +295,12 @@ from point to the @var{n}th occurrence of the specified 
character.
 appends the current kill to the search string.  @kbd{M-y}
 (@code{isearch-yank-pop}), if called after @kbd{C-y}, replaces that
 appended text with an earlier kill, similar to the usual @kbd{M-y}
-(@code{yank-pop}) command (@pxref{Yanking}).  Clicking @kbd{mouse-2}
-in the echo area appends the current X selection (@pxref{Primary
-Selection}) to the search string (@code{isearch-yank-x-selection}).
+(@code{yank-pop}) command.  When @kbd{M-y} is called not after
+@kbd{C-y}, then it activates the minibuffer where you can select
+a previous kill to append to the search string (@pxref{Earlier
+Kills}).  Clicking @kbd{mouse-2} in the echo area appends the current
+X selection (@pxref{Primary Selection}) to the search string
+(@code{isearch-yank-x-selection}).
 
 @kindex C-M-d @r{(Incremental search)}
 @kindex C-M-y @r{(Incremental search)}
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 550e7fe..c6ca4ee 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -1100,7 +1100,7 @@ one, it rotates the kill ring to place the yanked string 
at the front.
 This command replaces the just-yanked entry from the kill ring with a
 different entry from the kill ring.
 
-This is allowed only immediately after a @code{yank} or another
+This works only immediately after a @code{yank} or another
 @code{yank-pop}.  At such a time, the region contains text that was just
 inserted by yanking.  @code{yank-pop} deletes that text and inserts in
 its place a different piece of killed text.  It does not add the deleted
diff --git a/etc/NEWS b/etc/NEWS
index 135452b..95f801f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -208,6 +208,13 @@ This command would previously not redefine values defined 
by these
 forms, but this command has now been changed to work more like
 'eval-defun', and reset the values as specified.
 
++++
+** Standalone 'M-y' uses the minibuffer to complete previous kills.
+When 'M-y' is typed not after a yank command, it activates the minibuffer
+where you can browse previous kills using the minibuffer history or
+completion.  In Isearch 'C-s M-y' uses the minibuffer with completion
+on previous kills to read a string and append it to the search string.
+
 ---
 ** New user options 'copy-region-blink-delay' and 'delete-pair-blink-delay'.
 'copy-region-blink-delay' specifies a delay to indicate the region
diff --git a/lisp/delsel.el b/lisp/delsel.el
index df2adc7..e1087fb 100644
--- a/lisp/delsel.el
+++ b/lisp/delsel.el
@@ -274,6 +274,8 @@ to `delete-selection-mode'."
 (put 'quoted-insert 'delete-selection t)
 
 (put 'yank 'delete-selection 'yank)
+(put 'yank-pop 'delete-selection 'yank)
+(put 'yank-from-kill-ring 'delete-selection 'yank)
 (put 'clipboard-yank 'delete-selection 'yank)
 (put 'insert-register 'delete-selection t)
 ;; delete-backward-char and delete-forward-char already delete the selection by
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 4fba437..a0aa250 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -2500,11 +2500,18 @@ If search string is empty, just beep."
   "Replace just-yanked search string with previously killed string."
   (interactive)
   (if (not (memq last-command '(isearch-yank-kill isearch-yank-pop)))
-      ;; Fall back on `isearch-yank-kill' for the benefits of people
-      ;; who are used to the old behavior of `M-y' in isearch mode. In
-      ;; future, this fallback may be changed if we ever change
-      ;; `yank-pop' to do something like the kill-ring-browser.
-      (isearch-yank-kill)
+      ;; Yank string from kill-ring-browser.
+      (with-isearch-suspended
+       (let ((string (read-from-kill-ring)))
+         (if (and isearch-case-fold-search
+                  (eq 'not-yanks search-upper-case))
+             (setq string (downcase string)))
+         (if isearch-regexp (setq string (regexp-quote string)))
+         (setq isearch-yank-flag t)
+         (setq isearch-new-string (concat isearch-string string)
+               isearch-new-message (concat isearch-message
+                                           (mapconcat 
'isearch-text-char-description
+                                                      string "")))))
     (isearch-pop-state)
     (isearch-yank-string (current-kill 1))))
 
diff --git a/lisp/simple.el b/lisp/simple.el
index bb28145..69b4639 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -5344,7 +5344,7 @@ Normally set from the UNDO element of a yank-handler; see 
`insert-for-yank'.")
 
 (defun yank-pop (&optional arg)
   "Replace just-yanked stretch of killed text with a different stretch.
-This command is allowed only immediately after a `yank' or a
+The main use of this command is immediately after a `yank' or a
 `yank-pop'.  At such a time, the region contains a stretch of
 reinserted previously-killed text.  `yank-pop' deletes that text
 and inserts in its place a different stretch of killed text by
@@ -5359,30 +5359,36 @@ comes the newest one.
 
 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")
+property, in the way that `yank' does.
+
+When this command is called not immediately after a `yank' or a
+`yank-pop', then it activates the minibuffer with its completion
+and history filled with previously-killed items from the
+`kill-ring' variable, and reads a string to yank at point.
+See `yank-from-kill-ring' for more details."
+  (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)
+      (yank-from-kill-ring (read-from-kill-ring) current-prefix-arg)
+    (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))
 
 (defun yank (&optional arg)
   "Reinsert (\"paste\") the last stretch of killed text.
@@ -5449,6 +5455,76 @@ See also the command `yank-pop' (\\[yank-pop])."
 With ARG, rotate that many kills forward (or backward, if negative)."
   (interactive "p")
   (current-kill arg))
+
+(defvar read-from-kill-ring-history)
+(defun read-from-kill-ring ()
+  "Read a string from `kill-ring' using completion and minibuffer history."
+  (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.
+         (read-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))
+                      ;; Add ellipsis at the end of a long string
+                      (when (> (length s) (+ 40 b))
+                        (add-text-properties
+                         (min (+ 40 b) (length s)) (length s)
+                         `(display ,ellipsis) s))
+                      s))
+                  read-from-kill-ring-history)))
+    (minibuffer-with-setup-hook
+        (lambda ()
+          ;; Allow ‘SPC’ to be self-inserting
+          (use-local-map
+           (let ((map (make-sparse-keymap)))
+             (set-keymap-parent map (current-local-map))
+             (define-key map " " nil)
+             (define-key map "?" nil)
+             map)))
+      (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
+       'read-from-kill-ring-history))))
+
+(defun yank-from-kill-ring (string &optional arg)
+  "Insert the `kill-ring' item selected from the minibuffer history.
+Use minibuffer navigation and search commands to browse the
+previously-killed items from the `kill-ring' variable in the
+minibuffer history before typing RET to insert the selected item,
+or use completion on the elements of `kill-ring'.  You can edit
+the item in the minibuffer before inserting it.
+
+With \\[universal-argument] as argument, put point at beginning,
+and mark at end, like `yank' does."
+  (interactive (list (read-from-kill-ring) current-prefix-arg))
+  (push-mark)
+  (insert-for-yank string)
+  (if (consp arg)
+      ;; Swap point and mark like in `yank'.
+      (goto-char (prog1 (mark t)
+                   (set-marker (mark-marker) (point) (current-buffer))))))
 
 ;; Some kill commands.
 



reply via email to

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