Add cp-modify-action

This commit is contained in:
Eric Danan 2017-12-23 16:09:47 +01:00
parent 710daa327b
commit 2e0be045a1
2 changed files with 157 additions and 6 deletions

View file

@ -163,7 +163,7 @@ This command lets you capture something (a note, todo item, ...) into the curren
## Enabling counsel-projectile mode when emacs starts
To automatically enable counsel-projectile mode when emacs starts, you can either use the Customize interface to toggle on the variable `counsel-projectile-mode` and save your customization, or add `(counsel-projectile-mode)` to your init file.
## Customizing action lists
The lists of available actions (including the default action) for most of the commands above are stored in custom variables. If you set one of these variables, either through the Customize interface or directly with `setq`, the new value will be picked up the next time you invoke the corresponding commmand.
The lists of available actions (including the default action) for most of the commands above are stored in custom variables. If you set one of these variables, either directly or through the through the Customize interface, the new value will be picked up the next time you invoke the corresponding commmand.
The variable holding the action list for `<command>` is named `<command>-action`. The following action list variables are defined:
- `counsel-projectile-action`
@ -192,7 +192,11 @@ For instance, the default value of `counsel-projectile-action` is:
The first element is the index of the default action, and the remainig ones are the available actions (a key, an action function, and a name for each action). Thus the default action in this list is the first one (<kbd>o</kbd> key).
Extra actions can be added to these lists or, alternatively, can be set through ivy's `ivy-set-actions` mechanism. If you prefer setting all actions (except the default one) through this mechanism, you can set the action list variable to a single action (e.g. `counsel-projectile-action`) instead of a list.
Extra actions can be added to these lists or, alternatively, can be set through ivy's `ivy-set-actions` mechanism. If you prefer setting all actions (except the default one) through this mechanism, you can set the action list variable to a single action (e.g. `counsel-projectile-action`) instead of a list. If you are not using the Customize interface and want to amend the value of one of these lists rather than setting it from scratch, you can use the function `counsel-projectile-modify-action`, which lets you easily:
- add, remove, or move an action,
- change an action key, function, or name,
- change the index of the default action.
See its docstring for details.
Although ivy does not support this natively, it is in fact possible to include actions with a two-character key in the list. To do so, however, it is necessary to also include an action whose key is the first of these two characters and whose action function is `counsel-projectile-prefix-action`. For instance, the default value of `counsel-projectile-switch-project-action` includes the following actions:
@ -205,7 +209,6 @@ Although ivy does not support this natively, it is in fact possible to include a
"search project with ag")
("sr" counsel-projectile-switch-project-action-rg
"search project with rg")
```
## Setting `counsel-projectile-org-capture` templates
The available capture templates for `counsel-projectile-org-capture` are read from the variable `counsel-projectile-org-capture-templates`. This variable has the same format as the variable `org-capture-templates`, except that in all strings of in an entrys target slot, all instances of `${root}` and `${name}` are replaced with the current project root and name, respectively.

View file

@ -1,4 +1,4 @@
;;; counsel-projectile.el --- Ivy integration for Projectile
;; counsel-projectile.el --- Ivy integration for Projectile
;; Copyright (C) 2016-2017 Eric Danan
@ -45,7 +45,7 @@
(require 'counsel)
(require 'projectile)
;;;; utility
;;;; global
(defgroup counsel-projectile nil
"Ivy integration for Projectile."
@ -60,7 +60,7 @@ COMMAND's `ivy-read' call.
This variable holds either a single action function, or an action
list whose first element is the index of the default action in
the list and the remaining elements are the actions (a key, a
function, and a name for each action."
function, and a name for each action)."
(eval
`(defcustom ,(intern (format "%s-action" command))
',action
@ -100,6 +100,154 @@ and whose action function is `counsel-projectile-prefix-action'." command)
(string :tag " name")))))
:group ',group)))
(defun counsel-projectile--action-index (action-item action-list)
"Return the index in ACTION-LIST of the action whose key,
function, name, or index in the list (1 for the first action,
etc) is ACTION-ITEM. If there is no such action, throw an error.
ACTION-LIST is an action list whose first element is the index of
the default action in the list and the remaining elements are the
actions (a key, a function, and a name for each action)."
(let (index)
(if (integerp action-item)
(when (and (> action-item 0)
(< action-item (length action-list)))
(setq index action-item))
(setq index (cl-position-if
(cond
((functionp action-item)
(lambda (action)
(equal action-item
(cadr action))))
((stringp action-item)
(lambda (action)
(member action-item
(list (car action) (caddr action))))))
(cdr action-list)))
(when index
(setq index (1+ index))))
(or index
(error "Action not found: %s" action-item))))
(defun counsel-projectile-modify-action (action-var modifications)
"Make MODIFICATIONS to ACTION-VAR.
ACTION-VAR is a variable holding an action list whose first
element is the index of the default action in the list and the
remaining elements are the actions (a key, a function, and a name
for each action).
MODIFICATIONS is a list of modifications to be applied
sequentially to ACTION-LIST. Each modification has one of the
following formats:
(remove ACTION-ITEM)
Remove the action whose key, function, name, or index in
the list (1 for the first action, etc) is ACTION-ITEM
from the list.
(add ACTION TARGET-ITEM)
Add ACTION (a list containing a key, a function, and a
name) to the list, just before the action whose key,
function, name, or index in the list (1 for the first
action, etc) is TARGET-ITEM. If TARGET-ITEM is omitted,
add the action at the end of the list.
(move ACTION-ITEM TARGET-ITEM)
Move the action whose key, function, name, or index in
the list (1 for the first action, etc) is ACTION-ITEM
just before the action whose key, function, name, or
index in the list (1 for the first action, etc) is
TARGET-ITEM. If TARGET-ITEM is omitted, move the action
to the end of the list.
(setkey ACTION-ITEM KEY)
Set the key of the action whose key, function, name, or
index in the list (1 for the first action, etc) is
ACTION-ITEM to KEY.
(setfun ACTION-ITEM FUNCTION)
Set the function of the action whose key, function, name,
or index in the list (1 for the first action, etc) is
ACTION-ITEM to FUNCTION.
(setname ACTION-ITEM NAME)
Set the name of the action whose key, function, name, or
index in the list (1 for the first action, etc) is
ACTION-ITEM to NAME.
(default ACTION-ITEM)
Set the index of the default action in the list to that
of the action whose key, function, name, or index in the
list (1 for the first action, etc) is ACTION-ITEM.
If anything goes wrong, throw an error and do not modify ACTION-VAR."
(let ((action-list (symbol-value action-var))
mod)
;; Make sure ACTION-VAR actually holds a list and not a single
;; action function
(unless (listp action-list)
(error "%s's value is not a list" action-var))
(while (setq mod (pop modifications))
(pcase mod
(`(remove ,action-item)
(setq action-list
(remove (nth (counsel-projectile--action-index action-item action-list)
action-list)
action-list)))
(`(add ,action ,target-item)
(let ((index (counsel-projectile--action-index target-item action-list)))
;; copied from `helm-append-at-nth'
(setq action-list (cl-loop for a in action-list
for count from 1
collect a
when (= count index)
collect action))))
(`(add ,action)
(setq action-list (append action-list (list action))))
(`(move ,action-item ,target-item)
(push `(add ,(nth (counsel-projectile--action-index action-item action-list)
action-list)
,target-item)
modifications)
(push `(remove ,action-item)
modifications))
(`(move ,action-item)
(push `(add ,(nth (counsel-projectile--action-index action-item action-list)
action-list))
modifications)
(push `(remove ,action-item)
modifications))
(`(setkey ,action-item ,key)
(let ((index (counsel-projectile--action-index action-item action-list)))
(setq action-list (cl-loop for a in action-list
for count from 0
if (= count index)
collect (cons key (cdr a))
else
collect a))))
(`(setfun ,action-item ,fun)
(let ((index (counsel-projectile--action-index action-item action-list)))
(setq action-list (cl-loop for a in action-list
for count from 0
if (= count index)
collect (list (car a) fun (caddr a))
else
collect a))))
(`(setname ,action-item ,name)
(let ((index (counsel-projectile--action-index action-item action-list)))
(setq action-list (cl-loop for a in action-list
for count from 0
if (= count index)
collect (list (car a) (cadr a) name)
else
collect a))))
(`(default ,action-item)
(setq action-list
(cons (counsel-projectile--action-index action-item action-list)
(cdr action-list))))))
(set action-var action-list)))
(defun counsel-projectile-prefix-action (cand)
"Generic action for a prefix key in any counsel-projectile command.