Klimi's new dotfiles with stow.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

591 řádky
23 KiB

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