commit | author | age
|
5cb5f7
|
1 |
;;; helm-ring.el --- kill-ring, mark-ring, and register browsers for helm. -*- lexical-binding: t -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2012 ~ 2018 Thierry Volpiatto <thierry.volpiatto@gmail.com> |
|
4 |
|
|
5 |
;; This program is free software; you can redistribute it and/or modify |
|
6 |
;; it under the terms of the GNU General Public License as published by |
|
7 |
;; the Free Software Foundation, either version 3 of the License, or |
|
8 |
;; (at your option) any later version. |
|
9 |
|
|
10 |
;; This program is distributed in the hope that it will be useful, |
|
11 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 |
;; GNU General Public License for more details. |
|
14 |
|
|
15 |
;; You should have received a copy of the GNU General Public License |
|
16 |
;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
17 |
|
|
18 |
;;; Code: |
|
19 |
|
|
20 |
(require 'cl-lib) |
|
21 |
(require 'helm) |
|
22 |
(require 'helm-utils) |
|
23 |
(require 'helm-help) |
|
24 |
(require 'helm-elisp) |
|
25 |
|
|
26 |
(declare-function undo-tree-restore-state-from-register "ext:undo-tree.el" (register)) |
|
27 |
|
|
28 |
|
|
29 |
(defgroup helm-ring nil |
|
30 |
"Ring related Applications and libraries for Helm." |
|
31 |
:group 'helm) |
|
32 |
|
|
33 |
(defcustom helm-kill-ring-threshold 3 |
|
34 |
"Minimum length of a candidate to be listed by `helm-source-kill-ring'." |
|
35 |
:type 'integer |
|
36 |
:group 'helm-ring) |
|
37 |
|
|
38 |
(defcustom helm-kill-ring-max-offset 400 |
|
39 |
"Max number of chars displayed per candidate in kill-ring browser. |
|
40 |
When `t', don't truncate candidate, show all. |
|
41 |
By default it is approximatively the number of bits contained in five lines |
|
42 |
of 80 chars each i.e 80*5. |
|
43 |
Note that if you set this to nil multiline will be disabled, i.e you |
|
44 |
will not have anymore separators between candidates." |
|
45 |
:type '(choice (const :tag "Disabled" t) |
|
46 |
(integer :tag "Max candidate offset")) |
|
47 |
:group 'helm-ring) |
|
48 |
|
|
49 |
(defcustom helm-kill-ring-actions |
|
50 |
'(("Yank marked" . helm-kill-ring-action-yank) |
|
51 |
("Delete marked" . helm-kill-ring-action-delete)) |
|
52 |
"List of actions for kill ring source." |
|
53 |
:group 'helm-ring |
|
54 |
:type '(alist :key-type string :value-type function)) |
|
55 |
|
|
56 |
(defcustom helm-kill-ring-separator "\n" |
|
57 |
"The separator used to separate marked candidates when yanking." |
|
58 |
:group 'helm-ring |
|
59 |
:type 'string) |
|
60 |
|
|
61 |
(defcustom helm-register-max-offset 160 |
|
62 |
"Max size of string register entries before truncating." |
|
63 |
:group 'helm-ring |
|
64 |
:type 'integer) |
|
65 |
|
|
66 |
;;; Kill ring |
|
67 |
;; |
|
68 |
;; |
|
69 |
(defvar helm-kill-ring-map |
|
70 |
(let ((map (make-sparse-keymap))) |
|
71 |
(set-keymap-parent map helm-map) |
|
72 |
(define-key map (kbd "M-y") 'helm-next-line) |
|
73 |
(define-key map (kbd "M-u") 'helm-previous-line) |
|
74 |
(define-key map (kbd "M-D") 'helm-kill-ring-delete) |
|
75 |
(define-key map (kbd "C-]") 'helm-kill-ring-toggle-truncated) |
|
76 |
(define-key map (kbd "C-c C-k") 'helm-kill-ring-kill-selection) |
|
77 |
map) |
|
78 |
"Keymap for `helm-show-kill-ring'.") |
|
79 |
|
|
80 |
(defvar helm-source-kill-ring |
|
81 |
(helm-build-sync-source "Kill Ring" |
|
82 |
:init (lambda () |
|
83 |
(helm-attrset 'last-command last-command) |
|
84 |
(helm-attrset 'multiline helm-kill-ring-max-offset)) |
|
85 |
:candidates #'helm-kill-ring-candidates |
|
86 |
:filtered-candidate-transformer #'helm-kill-ring-transformer |
|
87 |
:action 'helm-kill-ring-actions |
|
88 |
:persistent-action 'ignore |
|
89 |
:help-message 'helm-kill-ring-help-message |
|
90 |
:persistent-help "DoNothing" |
|
91 |
:keymap helm-kill-ring-map |
|
92 |
:migemo t |
|
93 |
:multiline 'helm-kill-ring-max-offset |
|
94 |
:group 'helm-ring) |
|
95 |
"Source for browse and insert contents of kill-ring.") |
|
96 |
|
|
97 |
(defun helm-kill-ring-candidates () |
|
98 |
(cl-loop for kill in (helm-fast-remove-dups kill-ring :test 'equal) |
|
99 |
unless (or (< (length kill) helm-kill-ring-threshold) |
|
100 |
(string-match "\\`[\n[:blank:]]+\\'" kill)) |
|
101 |
collect kill)) |
|
102 |
|
|
103 |
(defun helm-kill-ring-transformer (candidates _source) |
|
104 |
"Ensure CANDIDATES are not read-only." |
|
105 |
(cl-loop for i in candidates |
|
106 |
when (get-text-property 0 'read-only i) |
|
107 |
do (set-text-properties 0 (length i) '(read-only nil) i) |
|
108 |
collect i)) |
|
109 |
|
|
110 |
(defvar helm-kill-ring--truncated-flag nil) |
|
111 |
(defun helm-kill-ring-toggle-truncated () |
|
112 |
"Toggle truncated view of candidates in helm kill-ring browser." |
|
113 |
(interactive) |
|
114 |
(with-helm-alive-p |
|
115 |
(setq helm-kill-ring--truncated-flag (not helm-kill-ring--truncated-flag)) |
|
116 |
(let* ((cur-cand (helm-get-selection)) |
|
117 |
(presel-fn (lambda () |
|
118 |
(helm-kill-ring--preselect-fn cur-cand)))) |
|
119 |
(helm-attrset 'multiline |
|
120 |
(if helm-kill-ring--truncated-flag |
|
121 |
15000000 |
|
122 |
helm-kill-ring-max-offset)) |
|
123 |
(helm-update presel-fn)))) |
|
124 |
(put 'helm-kill-ring-toggle-truncated 'helm-only t) |
|
125 |
|
|
126 |
(defun helm-kill-ring-kill-selection () |
|
127 |
"Store the real value of candidate in kill-ring. |
|
128 |
Same as `helm-kill-selection-and-quit' called with a prefix arg." |
|
129 |
(interactive) |
|
130 |
(helm-kill-selection-and-quit t)) |
|
131 |
(put 'helm-kill-ring-kill-selection 'helm-only t) |
|
132 |
|
|
133 |
(defun helm-kill-ring--preselect-fn (candidate) |
|
134 |
"Internal, used to preselect CANDIDATE when toggling truncated view." |
|
135 |
;; Preselection by regexp may not work if candidate is huge, so walk |
|
136 |
;; the helm buffer until selection is on CANDIDATE. |
|
137 |
(helm-awhile (condition-case-unless-debug nil |
|
138 |
(and (not (helm-pos-header-line-p)) |
|
139 |
(helm-get-selection)) |
|
140 |
(error nil)) |
|
141 |
(if (string= it candidate) |
|
142 |
(cl-return) |
|
143 |
(helm-next-line)))) |
|
144 |
|
|
145 |
(defun helm-kill-ring-action-yank (_str) |
|
146 |
"Insert concatenated marked candidates in current-buffer. |
|
147 |
|
|
148 |
When two prefix args are given prompt to choose separator, otherwise |
|
149 |
use `helm-kill-ring-separator' as default." |
|
150 |
(let ((marked (helm-marked-candidates)) |
|
151 |
(sep (if (equal helm-current-prefix-arg '(16)) |
|
152 |
(read-string "Separator: ") |
|
153 |
helm-kill-ring-separator))) |
|
154 |
(helm-kill-ring-action-yank-1 |
|
155 |
(cl-loop for c in (butlast marked) |
|
156 |
concat (concat c sep) into str |
|
157 |
finally return (concat str (car (last marked))))))) |
|
158 |
|
|
159 |
(defun helm-kill-ring-action-yank-1 (str) |
|
160 |
"Insert STR in `kill-ring' and set STR to the head. |
|
161 |
|
|
162 |
When called with a prefix arg, point and mark are exchanged without |
|
163 |
activating region. |
|
164 |
If this action is executed just after `yank', |
|
165 |
replace with STR as yanked string." |
|
166 |
(let ((yank-fn (lambda (&optional before yank-pop) |
|
167 |
(insert-for-yank str) |
|
168 |
;; Set the window start back where it was in |
|
169 |
;; the yank command, if possible. |
|
170 |
(when yank-pop |
|
171 |
(set-window-start (selected-window) yank-window-start t)) |
|
172 |
(when (or (equal helm-current-prefix-arg '(4)) before) |
|
173 |
;; Same as exchange-point-and-mark but without |
|
174 |
;; activating region. |
|
175 |
(goto-char (prog1 (mark t) |
|
176 |
(set-marker (mark-marker) |
|
177 |
(point) |
|
178 |
helm-current-buffer))))))) |
|
179 |
;; Prevent inserting and saving highlighted items. |
|
180 |
(set-text-properties 0 (length str) nil str) |
|
181 |
(with-helm-current-buffer |
|
182 |
(unwind-protect |
|
183 |
(progn |
|
184 |
(setq kill-ring (delete str kill-ring)) |
|
185 |
;; Adding a `delete-selection' property |
|
186 |
;; to `helm-kill-ring-action' is not working |
|
187 |
;; because `this-command' will be `helm-maybe-exit-minibuffer', |
|
188 |
;; so use this workaround (Issue #1520). |
|
189 |
(when (and (region-active-p) delete-selection-mode) |
|
190 |
(delete-region (region-beginning) (region-end))) |
|
191 |
(if (not (eq (helm-attr 'last-command helm-source-kill-ring) 'yank)) |
|
192 |
(progn |
|
193 |
;; Ensure mark is at beginning of inserted text. |
|
194 |
(push-mark) |
|
195 |
;; When yanking in a helm minibuffer we need a small |
|
196 |
;; delay to detect the mark in previous minibuffer. [1] |
|
197 |
(run-with-timer 0.01 nil yank-fn)) |
|
198 |
;; from `yank-pop' |
|
199 |
(let ((inhibit-read-only t) |
|
200 |
(before (< (point) (mark t)))) |
|
201 |
(if before |
|
202 |
(funcall (or yank-undo-function 'delete-region) (point) (mark t)) |
|
203 |
(funcall (or yank-undo-function 'delete-region) (mark t) (point))) |
|
204 |
(setq yank-undo-function nil) |
|
205 |
(set-marker (mark-marker) (point) helm-current-buffer) |
|
206 |
;; Same as [1] but use the same mark and point as in |
|
207 |
;; the initial yank according to BEFORE even if no |
|
208 |
;; prefix arg is given. |
|
209 |
(run-with-timer 0.01 nil yank-fn before 'pop)))) |
|
210 |
(kill-new str))))) |
|
211 |
(define-obsolete-function-alias 'helm-kill-ring-action 'helm-kill-ring-action-yank "2.4.0") |
|
212 |
|
|
213 |
(defun helm-kill-ring-action-delete (_candidate) |
|
214 |
"Delete marked candidates from `kill-ring'." |
|
215 |
(cl-loop for c in (helm-marked-candidates) |
|
216 |
do (setq kill-ring |
|
217 |
(delete c kill-ring)))) |
|
218 |
|
|
219 |
(defun helm-kill-ring-delete () |
|
220 |
"Delete marked candidates from `kill-ring'. |
|
221 |
|
|
222 |
This is a command for `helm-kill-ring-map'." |
|
223 |
(interactive) |
|
224 |
(with-helm-alive-p |
|
225 |
(helm-exit-and-execute-action 'helm-kill-ring-action-delete))) |
|
226 |
|
|
227 |
|
|
228 |
;;;; <Mark ring> |
|
229 |
;; DO NOT use these sources with other sources use |
|
230 |
;; the commands `helm-mark-ring', `helm-global-mark-ring' or |
|
231 |
;; `helm-all-mark-rings' instead. |
|
232 |
|
|
233 |
(defun helm-mark-ring-line-string-at-pos (pos) |
|
234 |
"Return line string at position POS." |
|
235 |
(save-excursion |
|
236 |
(goto-char pos) |
|
237 |
(forward-line 0) |
|
238 |
(let ((line (car (split-string (thing-at-point 'line) "[\n\r]")))) |
|
239 |
(remove-text-properties 0 (length line) '(read-only) line) |
|
240 |
(if (string= "" line) |
|
241 |
"<EMPTY LINE>" |
|
242 |
line)))) |
|
243 |
|
|
244 |
(defun helm-mark-ring-get-candidates () |
|
245 |
(with-helm-current-buffer |
|
246 |
(cl-loop with marks = (if (mark t) |
|
247 |
(cons (mark-marker) mark-ring) |
|
248 |
mark-ring) |
|
249 |
for marker in marks |
|
250 |
with max-line-number = (line-number-at-pos (point-max)) |
|
251 |
with width = (length (number-to-string max-line-number)) |
|
252 |
for m = (format (concat "%" (number-to-string width) "d: %s") |
|
253 |
(line-number-at-pos marker) |
|
254 |
(helm-mark-ring-line-string-at-pos marker)) |
|
255 |
unless (and recip (assoc m recip)) |
|
256 |
collect (cons m marker) into recip |
|
257 |
finally return recip))) |
|
258 |
|
|
259 |
(defun helm-mark-ring-default-action (candidate) |
|
260 |
(let ((target (copy-marker candidate))) |
|
261 |
(helm-aif (marker-buffer candidate) |
|
262 |
(progn |
|
263 |
(switch-to-buffer it) |
|
264 |
(helm-log-run-hook 'helm-goto-line-before-hook) |
|
265 |
(helm-match-line-cleanup) |
|
266 |
(with-helm-current-buffer |
|
267 |
(unless helm-yank-point (setq helm-yank-point (point)))) |
|
268 |
(helm-goto-char target) |
|
269 |
(helm-highlight-current-line)) |
|
270 |
;; marker points to no buffer, no need to dereference it, just |
|
271 |
;; delete it. |
|
272 |
(setq mark-ring (delete target mark-ring)) |
|
273 |
(error "Marker points to no buffer")))) |
|
274 |
|
|
275 |
(defvar helm-source-mark-ring |
|
276 |
(helm-build-sync-source "mark-ring" |
|
277 |
:candidates #'helm-mark-ring-get-candidates |
|
278 |
:action '(("Goto line" . helm-mark-ring-default-action)) |
|
279 |
:persistent-help "Show this line" |
|
280 |
:group 'helm-ring)) |
|
281 |
|
|
282 |
;;; Global-mark-ring |
|
283 |
(defvar helm-source-global-mark-ring |
|
284 |
(helm-build-sync-source "global-mark-ring" |
|
285 |
:candidates #'helm-global-mark-ring-get-candidates |
|
286 |
:action '(("Goto line" . helm-mark-ring-default-action)) |
|
287 |
:persistent-help "Show this line" |
|
288 |
:group 'helm-ring)) |
|
289 |
|
|
290 |
(defun helm-global-mark-ring-format-buffer (marker) |
|
291 |
(with-current-buffer (marker-buffer marker) |
|
292 |
(goto-char marker) |
|
293 |
(forward-line 0) |
|
294 |
(let ((line (pcase (thing-at-point 'line) |
|
295 |
((and line (pred stringp) |
|
296 |
(guard (not (string-match-p "\\`\n?\\'" line)))) |
|
297 |
(car (split-string line "[\n\r]"))) |
|
298 |
(_ "<EMPTY LINE>")))) |
|
299 |
(remove-text-properties 0 (length line) '(read-only) line) |
|
300 |
(format "%7d:%s: %s" |
|
301 |
(line-number-at-pos) (marker-buffer marker) line)))) |
|
302 |
|
|
303 |
(defun helm-global-mark-ring-get-candidates () |
|
304 |
(let ((marks global-mark-ring)) |
|
305 |
(when marks |
|
306 |
(cl-loop for marker in marks |
|
307 |
for mb = (marker-buffer marker) |
|
308 |
for gm = (unless (or (string-match "^ " (format "%s" mb)) |
|
309 |
(null mb)) |
|
310 |
(helm-global-mark-ring-format-buffer marker)) |
|
311 |
when (and gm (not (assoc gm recip))) |
|
312 |
collect (cons gm marker) into recip |
|
313 |
finally return recip)))) |
|
314 |
|
|
315 |
;;;; <Register> |
|
316 |
;;; Insert from register |
|
317 |
(defvar helm-source-register |
|
318 |
(helm-build-sync-source "Registers" |
|
319 |
:candidates #'helm-register-candidates |
|
320 |
:action-transformer #'helm-register-action-transformer |
|
321 |
:persistent-help "" |
|
322 |
:multiline t |
|
323 |
:action '(("Delete Register(s)" . |
|
324 |
(lambda (_candidate) |
|
325 |
(cl-loop for candidate in (helm-marked-candidates) |
|
326 |
for register = (car candidate) |
|
327 |
do (setq register-alist |
|
328 |
(delq (assoc register register-alist) |
|
329 |
register-alist)))))) |
|
330 |
:group 'helm-ring) |
|
331 |
"See (info \"(emacs)Registers\")") |
|
332 |
|
|
333 |
(defun helm-register-candidates () |
|
334 |
"Collecting register contents and appropriate commands." |
|
335 |
(cl-loop for (char . val) in register-alist |
|
336 |
for key = (single-key-description char) |
|
337 |
for string-actions = |
|
338 |
(cond |
|
339 |
((numberp val) |
|
340 |
(list (int-to-string val) |
|
341 |
'insert-register |
|
342 |
'increment-register)) |
|
343 |
((markerp val) |
|
344 |
(let ((buf (marker-buffer val))) |
|
345 |
(if (null buf) |
|
346 |
(list "a marker in no buffer") |
|
347 |
(list (concat |
|
348 |
"a buffer position:" |
|
349 |
(buffer-name buf) |
|
350 |
", position " |
|
351 |
(int-to-string (marker-position val))) |
|
352 |
'jump-to-register |
|
353 |
'insert-register)))) |
|
354 |
((and (consp val) (window-configuration-p (car val))) |
|
355 |
(list "window configuration." |
|
356 |
'jump-to-register)) |
|
357 |
((and (vectorp val) |
|
358 |
(fboundp 'undo-tree-register-data-p) |
|
359 |
(undo-tree-register-data-p (elt val 1))) |
|
360 |
(list |
|
361 |
"Undo-tree entry." |
|
362 |
'undo-tree-restore-state-from-register)) |
|
363 |
((or (and (vectorp val) (eq 'registerv (aref val 0))) |
|
364 |
(and (consp val) (frame-configuration-p (car val)))) |
|
365 |
(list "frame configuration." |
|
366 |
'jump-to-register)) |
|
367 |
((and (consp val) (eq (car val) 'file)) |
|
368 |
(list (concat "file:" |
|
369 |
(prin1-to-string (cdr val)) |
|
370 |
".") |
|
371 |
'jump-to-register)) |
|
372 |
((and (consp val) (eq (car val) 'file-query)) |
|
373 |
(list (concat "file:a file-query reference: file " |
|
374 |
(car (cdr val)) |
|
375 |
", position " |
|
376 |
(int-to-string (car (cdr (cdr val)))) |
|
377 |
".") |
|
378 |
'jump-to-register)) |
|
379 |
((consp val) |
|
380 |
(let ((lines (format "%4d" (length val)))) |
|
381 |
(list (format "%s: %s\n" lines |
|
382 |
(truncate-string-to-width |
|
383 |
(mapconcat 'identity (list (car val)) |
|
384 |
"^J") (- (window-width) 15))) |
|
385 |
'insert-register))) |
|
386 |
((stringp val) |
|
387 |
(list |
|
388 |
;; without properties |
|
389 |
(concat (substring-no-properties |
|
390 |
val 0 (min (length val) helm-register-max-offset)) |
|
391 |
(if (> (length val) helm-register-max-offset) |
|
392 |
"[...]" "")) |
|
393 |
'insert-register |
|
394 |
'append-to-register |
|
395 |
'prepend-to-register))) |
|
396 |
unless (null string-actions) ; Fix Issue #1107. |
|
397 |
collect (cons (format "Register %3s:\n %s" key (car string-actions)) |
|
398 |
(cons char (cdr string-actions))))) |
|
399 |
|
|
400 |
(defun helm-register-action-transformer (actions register-and-functions) |
|
401 |
"Decide actions by the contents of register." |
|
402 |
(cl-loop with func-actions = |
|
403 |
'((insert-register |
|
404 |
"Insert Register" . |
|
405 |
(lambda (c) (insert-register (car c)))) |
|
406 |
(jump-to-register |
|
407 |
"Jump to Register" . |
|
408 |
(lambda (c) (jump-to-register (car c)))) |
|
409 |
(append-to-register |
|
410 |
"Append Region to Register" . |
|
411 |
(lambda (c) (append-to-register |
|
412 |
(car c) (region-beginning) (region-end)))) |
|
413 |
(prepend-to-register |
|
414 |
"Prepend Region to Register" . |
|
415 |
(lambda (c) (prepend-to-register |
|
416 |
(car c) (region-beginning) (region-end)))) |
|
417 |
(increment-register |
|
418 |
"Increment Prefix Arg to Register" . |
|
419 |
(lambda (c) (increment-register |
|
420 |
helm-current-prefix-arg (car c)))) |
|
421 |
(undo-tree-restore-state-from-register |
|
422 |
"Restore Undo-tree register" . |
|
423 |
(lambda (c) (and (fboundp 'undo-tree-restore-state-from-register) |
|
424 |
(undo-tree-restore-state-from-register (car c)))))) |
|
425 |
for func in (cdr register-and-functions) |
|
426 |
when (assq func func-actions) |
|
427 |
collect (cdr it) into transformer-actions |
|
428 |
finally return (append transformer-actions actions))) |
|
429 |
|
|
430 |
;;;###autoload |
|
431 |
(defun helm-mark-ring () |
|
432 |
"Preconfigured `helm' for `helm-source-mark-ring'." |
|
433 |
(interactive) |
|
434 |
(helm :sources 'helm-source-mark-ring |
|
435 |
:resume 'noresume |
|
436 |
:buffer "*helm mark*")) |
|
437 |
|
|
438 |
;;;###autoload |
|
439 |
(defun helm-global-mark-ring () |
|
440 |
"Preconfigured `helm' for `helm-source-global-mark-ring'." |
|
441 |
(interactive) |
|
442 |
(helm :sources 'helm-source-global-mark-ring |
|
443 |
:resume 'noresume |
|
444 |
:buffer "*helm global mark*")) |
|
445 |
|
|
446 |
;;;###autoload |
|
447 |
(defun helm-all-mark-rings () |
|
448 |
"Preconfigured `helm' for `helm-source-global-mark-ring' and \ |
|
449 |
`helm-source-mark-ring'." |
|
450 |
(interactive) |
|
451 |
(helm :sources '(helm-source-mark-ring |
|
452 |
helm-source-global-mark-ring) |
|
453 |
:resume 'noresume |
|
454 |
:buffer "*helm mark ring*")) |
|
455 |
|
|
456 |
;;;###autoload |
|
457 |
(defun helm-register () |
|
458 |
"Preconfigured `helm' for Emacs registers." |
|
459 |
(interactive) |
|
460 |
(helm :sources 'helm-source-register |
|
461 |
:resume 'noresume |
|
462 |
:buffer "*helm register*")) |
|
463 |
|
|
464 |
;;;###autoload |
|
465 |
(defun helm-show-kill-ring () |
|
466 |
"Preconfigured `helm' for `kill-ring'. |
|
467 |
It is drop-in replacement of `yank-pop'. |
|
468 |
|
|
469 |
First call open the kill-ring browser, next calls move to next line." |
|
470 |
(interactive) |
|
471 |
(setq helm-kill-ring--truncated-flag nil) |
|
472 |
(let ((enable-recursive-minibuffers t)) |
|
473 |
(helm :sources helm-source-kill-ring |
|
474 |
:buffer "*helm kill ring*" |
|
475 |
:resume 'noresume |
|
476 |
:allow-nest t))) |
|
477 |
|
|
478 |
;;;###autoload |
|
479 |
(defun helm-execute-kmacro () |
|
480 |
"Preconfigured helm for keyboard macros. |
|
481 |
Define your macros with `f3' and `f4'. |
|
482 |
See (info \"(emacs) Keyboard Macros\") for detailed infos. |
|
483 |
This command is useful when used with persistent action." |
|
484 |
(interactive) |
|
485 |
(let ((helm-quit-if-no-candidate |
|
486 |
(lambda () (message "No kbd macro has been defined")))) |
|
487 |
(helm :sources |
|
488 |
(helm-build-sync-source "Kmacro" |
|
489 |
:candidates (lambda () |
|
490 |
(helm-fast-remove-dups |
|
491 |
(cons (kmacro-ring-head) |
|
492 |
kmacro-ring) |
|
493 |
:test 'equal)) |
|
494 |
:multiline t |
|
495 |
:candidate-transformer |
|
496 |
(lambda (candidates) |
|
497 |
(cl-loop for c in candidates collect |
|
498 |
(propertize (help-key-description (car c) nil) |
|
499 |
'helm-realvalue c))) |
|
500 |
:persistent-help "Execute kmacro" |
|
501 |
:help-message 'helm-kmacro-help-message |
|
502 |
:action |
|
503 |
(helm-make-actions |
|
504 |
"Execute kmacro (`C-u <n>' to execute <n> times)" |
|
505 |
'helm-kbd-macro-execute |
|
506 |
"Concat marked macros" |
|
507 |
'helm-kbd-macro-concat-macros |
|
508 |
"Delete marked macros" |
|
509 |
'helm-kbd-macro-delete-macro |
|
510 |
"Edit marked macro" |
|
511 |
'helm-kbd-macro-edit-macro) |
|
512 |
:group 'helm-ring) |
|
513 |
:buffer "*helm kmacro*"))) |
|
514 |
|
|
515 |
(defun helm-kbd-macro-execute (candidate) |
|
516 |
;; Move candidate on top of list for next use. |
|
517 |
(setq kmacro-ring (delete candidate kmacro-ring)) |
|
518 |
(kmacro-push-ring) |
|
519 |
(kmacro-split-ring-element candidate) |
|
520 |
(kmacro-exec-ring-item |
|
521 |
candidate helm-current-prefix-arg)) |
|
522 |
|
|
523 |
(defun helm-kbd-macro-concat-macros (_candidate) |
|
524 |
(let ((mkd (helm-marked-candidates))) |
|
525 |
(when (cdr mkd) |
|
526 |
(kmacro-push-ring) |
|
527 |
(setq last-kbd-macro |
|
528 |
(mapconcat 'identity |
|
529 |
(cl-loop for km in mkd |
|
530 |
if (vectorp km) |
|
531 |
append (cl-loop for k across km collect |
|
532 |
(key-description (vector k))) |
|
533 |
into result |
|
534 |
else collect (car km) into result |
|
535 |
finally return result) |
|
536 |
""))))) |
|
537 |
|
|
538 |
(defun helm-kbd-macro-delete-macro (_candidate) |
|
539 |
(let ((mkd (helm-marked-candidates))) |
|
540 |
(kmacro-push-ring) |
|
541 |
(cl-loop for km in mkd |
|
542 |
do (setq kmacro-ring (delete km kmacro-ring))) |
|
543 |
(kmacro-pop-ring1))) |
|
544 |
|
|
545 |
(defun helm-kbd-macro-edit-macro (candidate) |
|
546 |
(kmacro-push-ring) |
|
547 |
(setq kmacro-ring (delete candidate kmacro-ring)) |
|
548 |
(kmacro-split-ring-element candidate) |
|
549 |
(kmacro-edit-macro)) |
|
550 |
|
|
551 |
(provide 'helm-ring) |
|
552 |
|
|
553 |
;; Local Variables: |
|
554 |
;; byte-compile-warnings: (not obsolete) |
|
555 |
;; coding: utf-8 |
|
556 |
;; indent-tabs-mode: nil |
|
557 |
;; End: |
|
558 |
|
|
559 |
;;; helm-ring.el ends here |