diff --git a/README.md b/README.md index 57c2259..99d33e8 100644 --- a/README.md +++ b/README.md @@ -87,27 +87,27 @@ Default key binding: C-c p p. 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 | -| :------------- | :-------------------------------------------------------------------------------------- | -| o | Jump to a project buffer or file: call `counsel-projectile` (default action; see above) | -| f | Jump to a project file: call `counsel-projectile-find-file` (see below) | -| d | Jump to a project directory: call `counsel-projectile-find-dir` (see below) | -| b | Jump to a project buffer: call `counsel-projectile-switch-to-buffer` (see below) | -| m | Find file manually: call `counsel-find-file` from the project root | -| S | Save all project buffers | -| k | Kill all project buffers | -| K | Remove project from the list of known projects | -| c | Run project compilation command | -| C | Run project configure command | -| E | Edit project directory-local variables | -| v | Open project in vc-dir / magit / monky | -| s g | Search project with grep: call `counsel-projectile-grep` (see below) | -| s s | Search project with ag: call `counsel-projectile-ag` (see below) | -| s r | Search project with rg: call `counsel-projectile-rg` (see below) | -| x s | Invoke shell from the project root | -| x e | Invoke eshell from the project root | -| x t | Invoke term from the project root | -| O | Org-capture into project: call `counsel-projectile-org-capture` (see below) | +| Key | Action | +| :------------ | :-------------------------------------------------------------------------------------- | +| o | Jump to a project buffer or file: call `counsel-projectile` (default action; see above) | +| f | Jump to a project file: call `counsel-projectile-find-file` (see below) | +| d | Jump to a project directory: call `counsel-projectile-find-dir` (see below) | +| b | Jump to a project buffer: call `counsel-projectile-switch-to-buffer` (see below) | +| m | Find file manually: call `counsel-find-file` from the project root | +| S | Save all project buffers | +| k | Kill all project buffers | +| K | Remove project from the list of known projects | +| c | Run project compilation command | +| C | Run project configure command | +| E | Edit project directory-local variables | +| v | Open project in vc-dir / magit / monky | +| sg | Search project with grep: call `counsel-projectile-grep` (see below) | +| ss | Search project with ag: call `counsel-projectile-ag` (see below) | +| sr | Search project with rg: call `counsel-projectile-rg` (see below) | +| xs | Invoke shell from the project root | +| xe | Invoke eshell from the project root | +| xt | Invoke term from the project root | +| O | Org-capture into project: call `counsel-projectile-org-capture` (see below) | ## The `counsel-projectile-find-file` command Default key binding: C-c p f. @@ -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 `` is named `-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 s g, s s, and s r for the three project search commands in `projectile-switch-project-action`. Instead, the key s 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 entry’s target slot, all instances of `${root}` and `${name}` are replaced with the current project root and name, respectively. diff --git a/counsel-projectile.el b/counsel-projectile.el index 733cf0c..4dd5bd1 100644 --- a/counsel-projectile.el +++ b/counsel-projectile.el @@ -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))