From: Juri Linkov Date: Tue, 24 Nov 2020 19:23:41 +0000 (+0200) Subject: Yank items selected from kill-ring using completion and minibuffer history X-Git-Tag: emacs-28.0.90~5036 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=83b5fcb0014896feda98917bcf198094131e82a6;p=emacs.git 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 --- diff --git a/doc/emacs/killing.texi b/doc/emacs/killing.texi index bd7dbb6f515..0bd18fd0d77 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 0612c134d1b..5be45ce6a92 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 550e7feee2e..c6ca4eed2e1 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 135452bc303..95f801f60cb 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 df2adc7aeba..e1087fb3919 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 4fba4370d98..a0aa250c4b6 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 bb28145502b..69b4639292a 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.