]> code.delx.au - gnu-emacs-elpa/blob - packages/smart-yank/smart-yank.el
smart-yank version 0.1.1
[gnu-emacs-elpa] / packages / smart-yank / smart-yank.el
1 ;;; smart-yank.el --- A different approach of yank pointer handling -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2016 Free Software Foundation, Inc
4
5 ;; Author: Michael Heerdegen <michael_heerdegen@web.de>
6 ;; Maintainer: Michael Heerdegen <michael_heerdegen@web.de>
7 ;; Created: 14 May 2016
8 ;; Keywords: convenience
9 ;; Compatibility: GNU Emacs 24
10 ;; Version: 0.1.1
11 ;; Package-Requires: ((emacs "24"))
12
13
14 ;; This file is not part of GNU Emacs.
15
16 ;; GNU Emacs is free software: you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation, either version 3 of the License, or
19 ;; (at your option) any later version.
20
21 ;; GNU Emacs is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 ;; GNU General Public License for more details.
25
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28
29
30 ;;; Commentary:
31 ;;
32 ;; Introduction
33 ;; ============
34 ;;
35 ;; This library implements the global minor mode `smart-yank-mode'
36 ;; that changes the way Emacs handles the `kill-ring-yank-pointer' in
37 ;; a way that some people prefer over the default behavior.
38 ;;
39 ;; Normally, only a kill command resets the yank pointer. With
40 ;; `smart-yank-mode' enabled, any command except yank commands resets
41 ;; it.
42 ;;
43 ;; In addition, when yanking any "older" element from the kill-ring
44 ;; with yank-pop (and not replacing it with a subsequent yank-pop), it
45 ;; is automatically moved to the "first position" so `yank' invoked
46 ;; later will yank this element again.
47 ;;
48 ;; Finally, `yank-pop' (normally bound to M-y) is replaced with
49 ;; `smart-yank-yank-pop' that is a bit more sophisticated:
50 ;;
51 ;; - When _not_ called after a `yank', instead of raising an error
52 ;; like `yank-pop', yank the next-to-the-last kill.
53 ;;
54 ;; - Hit M-y twice in fast succession (delay < 0.2 secs by default)
55 ;; when you got lost. This will remove the yanked text. If you
56 ;; bind a command to `smart-yank-browse-kill-ring-command', this
57 ;; command will be called too (typically something like
58 ;; `browse-kill-ring').
59 ;;
60 ;;
61 ;; Example: you want to manually replace some words in some buffer
62 ;; with a new word "foo". With `smart-yank-mode' enabled, you can do
63 ;; it like this:
64 ;;
65 ;; 1. Put "foo" into the kill ring.
66 ;; 2. Move to the next word to be replaced.
67 ;; 3. M-d M-y
68 ;; 4. Back to 2, iterate.
69 ;;
70 ;;
71 ;; Setup
72 ;; =====
73 ;;
74 ;; Just enable `smart-yank-mode' and you are done.
75
76
77
78 ;;; Code:
79
80 ;;;; Configuration stuff
81
82 (defgroup smart-yank nil
83 "A different approach of yank pointer handling."
84 :group 'killing)
85
86 (defcustom smart-yank-yank-pop-multikey-delay .2
87 "Max delay between two \\[smart-yank-yank-pop] invocations revealing special behavior.
88 See `smart-yank-yank-pop' for details."
89 :type 'number)
90
91 (defcustom smart-yank-browse-kill-ring-command nil
92 "Command to invoke when hitting \\[smart-yank-yank-pop] twice (fast)."
93 :type '(choice (const :tag "None" nil)
94 (const browse-kill-ring)
95 (const helm-show-kill-ring)
96 (function :tag "Other Function")))
97
98 (defvar smart-yank-mode-map
99 (let ((map (make-sparse-keymap)))
100 (define-key map [remap yank-pop] #'smart-yank-yank-pop)
101 map)
102 "Map used by `smart-yank-mode'.")
103
104
105 ;;;; Internals
106
107 (defun smart-yank--stopwatch ()
108 "Return a fresh stopwatch.
109 This is a function accepting zero arguments that upon each call
110 will return the time difference from its last call in seconds.
111 When called the first time it will return nil."
112 (let ((last-invocation nil))
113 (lambda ()
114 (prog1 (and last-invocation
115 (time-to-seconds (time-subtract (current-time) last-invocation)))
116 (setq last-invocation (current-time))))))
117
118 (defun smart-yank-reset-yank-pointer ()
119 (unless (eq last-command #'yank)
120 (setq kill-ring-yank-pointer kill-ring)))
121
122 (defun smart-yank--before-ad (&rest _args)
123 "Before advice function for `yank'.
124
125 Reset `kill-ring-yank-pointer'. For yank-pop, move the really
126 yanked text \"to the beginning\" of the kill ring."
127 (unless (eq kill-ring kill-ring-yank-pointer)
128 (let ((last-yank (car kill-ring-yank-pointer)))
129 (when last-yank
130 (setq kill-ring (cons last-yank (delete last-yank kill-ring)))
131 (smart-yank-reset-yank-pointer)))))
132
133 (defalias 'smart-yank-yank-pop
134 (let ((r (smart-yank--stopwatch)))
135 (lambda (&optional arg)
136 "\"smart-yank\"'s private version of `yank-pop'.
137
138 When called directly after a `yank' command (including itself),
139 call `yank-pop'.
140
141 If its key was hit two times in fast succession - i.e. with a
142 delay less than `smart-yank-yank-pop-multikey-delay' - delete any
143 yanked text; in addition call
144 `smart-yank-browse-kill-ring-command' when set.
145
146 When not called after a yank, yank the next-to-the-last
147 `kill-ring' entry; with prefix arg, call the
148 `smart-yank-browse-kill-ring-command'."
149 (interactive "P")
150 (let ((diff (funcall r)))
151 (cond
152 ((not (eq last-command 'yank)) (if arg (call-interactively smart-yank-browse-kill-ring-command)
153 (rotate-yank-pointer 1)
154 (yank)))
155 ((or (not diff)
156 (> diff smart-yank-yank-pop-multikey-delay))
157 (call-interactively #'yank-pop))
158 (t (funcall (or yank-undo-function #'delete-region)
159 (region-beginning) (region-end))
160 (when smart-yank-browse-kill-ring-command
161 (call-interactively smart-yank-browse-kill-ring-command))))))))
162
163 (declare-function smart-yank-yank-pop 'smart-yank)
164
165
166 ;;;; User stuff
167
168 ;;;###autoload
169 (define-minor-mode smart-yank-mode
170 "Alter the behavior of yank commands in several ways.
171
172 Turning on this mode has the following effects:
173
174 - Makes any command except yank commands reset the
175 `kill-ring-yank-pointer', instead of only killing commands.
176
177 - Remaps `yank-pop' to `smart-yank-yank-pop'.
178
179 - When yanking an older element from the `kill-ring' with
180 \\[smart-yank-yank-pop] (and not replacing it with a subsequent \\[smart-yank-yank-pop]), the
181 element is automatically \"moved to the first position\" of
182 the `kill-ring' so that `yank' invoked later will again yank
183 this element."
184 :global t
185 (if smart-yank-mode
186 (advice-add 'yank :before #'smart-yank--before-ad)
187 (advice-remove 'yank #'smart-yank--before-ad)))
188
189
190 (provide 'smart-yank)
191
192 ;;; smart-yank.el ends here