Simplify sub-action mechanism

This commit is contained in:
Eric Danan 2017-12-22 21:48:10 +01:00
parent 323152fee7
commit 2fc8dca257
2 changed files with 70 additions and 111 deletions

View file

@ -88,7 +88,7 @@ Default key binding: <kbd>C-c p p</kbd>.
This command is a replacement for `projectile-switch-project`. It adds the possibility to select from a list of switch-project actions to apply to the selected project:
| Key | Action |
| :------------- | :-------------------------------------------------------------------------------------- |
| :------------ | :-------------------------------------------------------------------------------------- |
| <kbd>o</kbd> | Jump to a project buffer or file: call `counsel-projectile` (default action; see above) |
| <kbd>f</kbd> | Jump to a project file: call `counsel-projectile-find-file` (see below) |
| <kbd>d</kbd> | Jump to a project directory: call `counsel-projectile-find-dir` (see below) |
@ -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 correspodiding 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 through the Customize interface or directly with `setq`, 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`
@ -194,20 +194,19 @@ The first element is the index of the default action, and the remainig ones are
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.
Note that ivy only supports one-character keys for actions. Hence, for instance, it is not possible to directly set the keys <kbd>s g</kbd>, <kbd>s s</kbd>, and <kbd>s r</kbd> for the three project search commands in `projectile-switch-project-action`. Instead, the key <kbd>s</kbd> is set for a prefix action `counsel-projectile-switch-project-action-prefix-search` that reads a secondary one-character key and calls the corresponding search command as a sub-action. The list of available sub-actions is read from the variable `counsel-projectile-switch-project-action-prefix-search-sub-action`, which can be customized separately. This variable has the same format as an action list, except that the index is not present. Its default value is:
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:
```emacs-lisp
'(("g" counsel-projectile-switch-project-action-grep
"Search project with grep")
("s" counsel-projectile-switch-project-action-ag
"Search project with ag")
("r" counsel-projectile-switch-project-action-rg
"Search project with rg"))
```
("s" counsel-projectile-prefix-action
"search project with grep / ag / rg...")
("sg" counsel-projectile-switch-project-action-grep
"search project with grep")
("ss" counsel-projectile-switch-project-action-ag
"search project with ag")
("sr" counsel-projectile-switch-project-action-rg
"search project with rg")
The following sub-action variables are defined:
- `counsel-projectile-switch-project-action-prefix-search-sub-action`
- `counsel-projectile-switch-project-action-prefix-shell-sub-action`
```
## 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

@ -83,7 +83,12 @@ An action is triggered for the selected candidate with `M-o
triggered with `M-RET' or `C-M-RET'. If this variable holds a
single action function, this action becomes the default action
and is assigned the key \"o\". For an action list, it is also
usual to assign the key \"o\" to the default action." command)
usual to assign the key \"o\" to the default action.
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'." command)
:type '(choice
(function :tag "Single action function")
(cons :tag "Action list"
@ -95,47 +100,26 @@ usual to assign the key \"o\" to the default action." command)
(string :tag " name")))))
:group ',group)))
(defun counsel-projectile--defcustom-sub-action (action sub-action group)
"Create a custom variable named \"ACTION-sub-action\" in GROUP,
with default value SUB-ACTION, to be passed by ACTION to
`counsel-projectile-sub-action'.
(defun counsel-projectile-prefix-action (cand)
"Generic action for a prefix key in any counsel-projectile command.
This variable holds a list of sub-actions for ACTION (a key, a
function, and a name for each action)."
(eval
`(defcustom ,(intern (format "%s-sub-action" action))
',sub-action
,(format "Sub-action(s) for `%s'.
This variable holds a list of sub-actions, each or which consists
of:
- a key (one-character string) to call the action,
- an action function of one variable,
- a name (string) for the action.
When `%s' is called, a key is read from the minibuffer and the
corresponding sub-action is executed." action action)
:type '(repeat :tag "Sub-actions"
(list :tag "Sub-action"
(string :tag " key")
(function :tag "function")
(string :tag " name")))
:group ',group)))
(defun counsel-projectile-sub-action (cand sub-actions)
"Read a key from the minibuffer and apply the corresponding
action from SUB-ACTIONS to CAND.
SUB-ACTIONS is list of ivy actions: a key, an action function,
and a name for each action. Binding a key to a call to this
function in an ivy action list makes this key behave like a
prefix key and the keys in SUB-ACTIONS like sub-keys."
;; adapted from `ivy-read-action'
(let* ((hint (funcall ivy-read-action-format-function sub-actions))
If used as action function in an action list, the corresponding
key will serve as a prefix key. That is, a secondary key will be
read from the minibuffer and the action from the list whose key
is the concatenation of these two keys will be called."
(let* ((action (ivy-state-action ivy-last))
(prefix (car (nth (car action) action)))
(sub-action (cl-loop
for a in (cdr action)
if (and (string-prefix-p prefix (car a))
(not (string= prefix (car a))))
collect (cons (string-remove-prefix prefix (car a))
(cdr a))))
;; adapted from `ivy-read-action' from here on
(hint (funcall ivy-read-action-format-function sub-action))
(resize-mini-windows t)
(key (string (read-key hint)))
(action-fun (nth 1 (assoc key sub-actions))))
(action-fun (nth 1 (assoc key sub-action))))
(cond ((member key '("" ""))
(when (eq ivy-exit 'done)
(ivy-resume)))
@ -747,34 +731,26 @@ candidates list of `counsel-projectile-switch-project'."
"edit project dir-locals")
("v" counsel-projectile-switch-project-action-vc
"open project in vc-dir / magit / monky")
("s" counsel-projectile-switch-project-action-prefix-search
"search project with ag / rg / grep...")
("x" counsel-projectile-switch-project-action-prefix-shell
("s" counsel-projectile-prefix-action
"search project with grep / ag / rg...")
("sg" counsel-projectile-switch-project-action-grep
"search project with grep")
("ss" counsel-projectile-switch-project-action-ag
"search project with ag")
("sr" counsel-projectile-switch-project-action-rg
"search project with rg")
("x" counsel-projectile-prefix-action
"invoke shell / eshell / term from project root...")
("xs" counsel-projectile-switch-project-action-run-shell
"invoke shell from project root")
("xe" counsel-projectile-switch-project-action-run-eshell
"invoke eshell from project root")
("xt" counsel-projectile-switch-project-action-run-term
"invoke term from project root")
("O" counsel-projectile-switch-project-action-org-capture
"org-capture into project"))
'counsel-projectile)
(counsel-projectile--defcustom-sub-action
'counsel-projectile-switch-project-action-prefix-search
'(("g" counsel-projectile-switch-project-action-grep
"Search project with grep")
("s" counsel-projectile-switch-project-action-ag
"Search project with ag")
("r" counsel-projectile-switch-project-action-rg
"Search project with rg"))
'counsel-projectile)
(counsel-projectile--defcustom-sub-action
'counsel-projectile-switch-project-action-prefix-shell
'(("s" counsel-projectile-switch-project-action-run-shell
"Invoke shell from project root")
("e" counsel-projectile-switch-project-action-run-eshell
"Invoke eshell from project root")
("t" counsel-projectile-switch-project-action-run-term
"Invoke term from project root"))
'counsel-projectile)
(defun counsel-projectile-switch-project-by-name (project)
"Switch to PROJECT.
Invokes the command referenced by
@ -893,14 +869,6 @@ action."
(projectile-run-term nil))))
(counsel-projectile-switch-project-by-name project)))
(defun counsel-projectile-switch-project-action-prefix-shell (project)
"Select a sub-action from
`counsel-projectile-switch-project-action-prefix-shell-sub-action'
and apply it to PROJECT."
(counsel-projectile-sub-action
project
counsel-projectile-switch-project-action-prefix-shell-sub-action))
(defun counsel-projectile-switch-project-action-grep (project)
"Search PROJECT with `grep'."
(let ((projectile-switch-project-action 'counsel-projectile-ag))
@ -916,14 +884,6 @@ and apply it to PROJECT."
(let ((projectile-switch-project-action 'counsel-projectile-rg))
(counsel-projectile-switch-project-by-name project)))
(defun counsel-projectile-switch-project-action-prefix-search (project)
"Select a sub-action from
`counsel-projectile-switch-project-action-prefix-search-sub-action'
and apply it to PROJECT."
(counsel-projectile-sub-action
project
counsel-projectile-switch-project-action-prefix-search-sub-action))
(defun counsel-projectile-switch-project-action-org-capture (project)
"Org-capture into PROJECT."
(let ((projectile-switch-project-action 'counsel-projectile-org-capture))