branch: master commit 28e88ab23a191420a93a4f920ca076674ee53f94 Author: Oleh Krehel <ohwoeo...@gmail.com> Commit: Oleh Krehel <ohwoeo...@gmail.com>
Allow to mark/unmark candidates with "m", "u", "DEL", "t" These key bindings are now available in the "C-o" hydra. * ivy.el (ivy-marked-candidates): (ivy-mark-prefix): New defvar. (ivy-call): Use `ivy-marked-candidates' when non-nil. (ivy-read): Reset `ivy-marked-candidates' to nil. (ivy-mark): (ivy-unmark): (ivy-unmark-backward): (ivy-toggle-marks): New command. Fixes #561 --- ivy-hydra.el | 16 +++++++---- ivy.el | 94 +++++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 89 insertions(+), 21 deletions(-) diff --git a/ivy-hydra.el b/ivy-hydra.el index 5b0054e..89d8b81 100644 --- a/ivy-hydra.el +++ b/ivy-hydra.el @@ -46,8 +46,8 @@ " ^ ^ ^ ^ ^ ^ | ^Call^ ^ ^ | ^Cancel^ | ^Options^ | Action _w_/_s_/_a_: %-14s(ivy-action-name) ^-^-^-^-^-^-+-^-^---------^-^--+-^-^------+-^-^-------+-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--------------------------- -^ ^ _k_ ^ ^ | _f_ollow occ_u_r | _i_nsert | _c_: calling %-5s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search -_h_ ^+^ _l_ | _d_one ^ ^ | _o_ops | _m_: matcher %-5s(ivy--matcher-desc)^^^^^^^^^^^^ _t_runcate: %-11`truncate-lines +^ ^ _k_ ^ ^ | _f_ollow occ_U_r | _i_nsert | _c_: calling %-5s(if ivy-calling \"on\" \"off\") _C_ase-fold: %-10`ivy-case-fold-search +_h_ ^+^ _l_ | _d_one ^ ^ | _o_ops | _M_: matcher %-5s(ivy--matcher-desc)^^^^^^^^^^^^ _T_runcate: %-11`truncate-lines ^ ^ _j_ ^ ^ | _g_o ^ ^ | ^ ^ | _<_/_>_: shrink/grow^^^^^^^^^^^^^^^^^^^^^^^^^^^^ _D_efinition of this menu " ;; arrows @@ -55,8 +55,14 @@ _h_ ^+^ _l_ | _d_one ^ ^ | _o_ops | _m_: matcher %-5s(ivy--matcher-desc) ("j" ivy-next-line) ("k" ivy-previous-line) ("l" ivy-end-of-buffer) + ;; mark + ("m" ivy-mark) + ("u" ivy-unmark) + ("DEL" ivy-unmark-backward) + ("t" ivy-toggle-marks) ;; actions ("o" keyboard-escape-quit :exit t) + ("r" ivy-dispatching-done-hydra :exit t) ("C-g" keyboard-escape-quit :exit t) ("i" nil) ("C-o" nil) @@ -66,15 +72,15 @@ _h_ ^+^ _l_ | _d_one ^ ^ | _o_ops | _m_: matcher %-5s(ivy--matcher-desc) ("g" ivy-call) ("C-m" ivy-done :exit t) ("c" ivy-toggle-calling) - ("m" ivy-rotate-preferred-builders) + ("M" ivy-rotate-preferred-builders) (">" ivy-minibuffer-grow) ("<" ivy-minibuffer-shrink) ("w" ivy-prev-action) ("s" ivy-next-action) ("a" ivy-read-action) - ("t" (setq truncate-lines (not truncate-lines))) + ("T" (setq truncate-lines (not truncate-lines))) ("C" ivy-toggle-case-fold) - ("u" ivy-occur :exit t) + ("U" ivy-occur :exit t) ("D" (ivy-exit-with-action (lambda (_) (find-function 'hydra-ivy/body))) :exit t)) diff --git a/ivy.el b/ivy.el index 184acf4..f7a997f 100644 --- a/ivy.el +++ b/ivy.el @@ -1265,6 +1265,16 @@ See variable `ivy-recursive-restore' for further information." (not (eq ivy-last ivy-recursive-last))) (ivy--reset-state (setq ivy-last ivy-recursive-last)))) +(defvar ivy-marked-candidates nil + "List of marked candidates. +Use `ivy-mark' to populate this. + +When this list is non-nil at the end of the session, the action +will be called for each element of this list.") + +(defvar ivy-mark-prefix ">" + "Prefix used by `ivy-mark'.") + (defun ivy-call () "Call the current action without exiting completion." (interactive) @@ -1281,26 +1291,30 @@ See variable `ivy-recursive-restore' for further information." (let* ((collection (ivy-state-collection ivy-last)) (current (ivy-state-current ivy-last)) (x (cond - ;; Alist type. - ((and (consp (car-safe collection)) - ;; Previously, the cdr of the selected - ;; candidate would be returned. Now, the - ;; whole candidate is returned. - (let ((idx (get-text-property 0 'idx current))) - (if idx - (nth idx collection) - (assoc current collection))))) - (ivy--directory - (expand-file-name current ivy--directory)) - ((equal current "") - ivy-text) - (t - current)))) + ;; Alist type. + ((and (consp (car-safe collection)) + ;; Previously, the cdr of the selected + ;; candidate would be returned. Now, the + ;; whole candidate is returned. + (let ((idx (get-text-property 0 'idx current))) + (if idx + (nth idx collection) + (assoc current collection))))) + (ivy--directory + (expand-file-name current ivy--directory)) + ((equal current "") + ivy-text) + (t + current)))) (if (eq action #'identity) (funcall action x) (select-window (ivy--get-window ivy-last)) (set-buffer (ivy-state-buffer ivy-last)) - (prog1 (unwind-protect (funcall action x) + (prog1 (unwind-protect + (if ivy-marked-candidates + (dolist (c ivy-marked-candidates) + (funcall action (substring c (length ivy-mark-prefix)))) + (funcall action x)) (ivy-recursive-restore)) (unless (or (eq ivy-exit 'done) (minibuffer-window-active-p (selected-window)) @@ -1828,6 +1842,7 @@ customizations apply to the current completion session." ,@extra-actions)) (t (delete-dups (append action extra-actions))))))) + (setq ivy-marked-candidates nil) (unless caller (setq caller this-command)) (let ((extra-sources (plist-get ivy--sources-list caller))) @@ -4441,6 +4456,53 @@ EVENT gives the mouse position." (ivy-occur-press) (select-window (ivy--get-window ivy-occur-last))) +(defun ivy--marked-p () + (member (ivy-state-current ivy-last) ivy-marked-candidates)) + +(defun ivy--unmark (cand) + (setcar (member cand ivy--all-candidates) + (setcar (member cand ivy--old-cands) + (substring cand (length ivy-mark-prefix)))) + (setq ivy-marked-candidates + (delete cand ivy-marked-candidates))) + +(defun ivy--mark (cand) + (let ((marked-cand (concat ivy-mark-prefix cand))) + (setcar (member cand ivy--all-candidates) + (setcar (member cand ivy--old-cands) marked-cand)) + (setq ivy-marked-candidates + (append ivy-marked-candidates (list marked-cand))))) + +(defun ivy-mark () + "Mark the selected candidate and move to the next one." + (interactive) + (unless (ivy--marked-p) + (ivy--mark (ivy-state-current ivy-last))) + (ivy-next-line)) + +(defun ivy-unmark () + "Unmark the selected candidate and move to the next one." + (interactive) + (when (ivy--marked-p) + (ivy--unmark (ivy-state-current ivy-last))) + (ivy-next-line)) + +(defun ivy-unmark-backward () + "Move to the previous candidate and unmark it." + (interactive) + (ivy-previous-line) + (ivy--exhibit) + (when (ivy--marked-p) + (ivy--unmark (ivy-state-current ivy-last)))) + +(defun ivy-toggle-marks () + "Toggle mark for all narrowed candidates." + (interactive) + (dolist (cand ivy--old-cands) + (if (member cand ivy-marked-candidates) + (ivy--unmark cand) + (ivy--mark cand)))) + (defconst ivy-help-file (let ((default-directory (if load-file-name (file-name-directory load-file-name)