Icicles - Defining Icicles Commands

Last edit

Added:

> [:icicle-bookmark-tagged]

Changed:

< * '''`icicle-bookmark-some-tags'''' -- Jump: bookmark with some of its tags matching a given set
< * '''`icicle-bookmark-some-tags-regexp'''' -- Jump: bookmark with some of its tags matching a given [[regexp]]
< * '''`icicle-bookmark-specific-buffers'''' -- Jump: specific-buffer bookmark
< * '''`icicle-bookmark-specific-files'''' -- Jump: specific-file bookmark
< * '''`icicle-bookmark-this-buffer'''' -- Jump: bookmark for this buffer

to

> * '''`icicle-bookmark-some-tags'''' -- Jump to a bookmark with some of its tags matching a given set
> * '''`icicle-bookmark-some-tags-regexp'''' -- Jump to a bookmark with some of its tags matching a given [[regexp]]
> * '''`icicle-bookmark-specific-buffers'''' -- Jump to a specific-buffer bookmark
> * '''`icicle-bookmark-specific-files'''' -- Jump to a specific-file bookmark
> * '''`icicle-bookmark-tagged'''' -- Jump to a bookmark with matching tags
> * '''`icicle-bookmark-
this-buffer'''' -- Jump to a bookmark for this buffer


iciclesimage
Previous: Icicles - Candidates with Text PropertiesIciclesIciclesIndexNext: Icicles - Defining Tripping Commands

Defining Icicles Commands (Including Multi-Commands)

This section is for EmacsLisp programmers.

Nothing To It!

Defining a command that uses Icicles completion and cycling is simple: just call ‘completing-read’ or ‘read-file-name’ to read input, then act on that input.

Nothing could be simpler – just use ‘completing-read’ or ‘read-file-name’! Icicles does the rest. This is the most important thing to learn about defining Icicles commands: you do not need to do anything except call ‘completing-read’ or ‘read-file-name’ as you would normally anyway.

Or at least as I hope you would normally. I fear that many Emacs-Lisp programmers do not take sufficient advantage of ‘completing-read’ when they could, using instead a function such as (quel horreur !) ‘read-string’ to read user input.

Multi-Commands Are Easy To Define Too

If defining an Icicles command is trivial, so is defining an Icicles multi-command. For the same effort it takes to define a command that acts on a single input choice, you can have a command that acts on any number of input choices. A multi-command takes advantage of one or more action functions when cycling candidates, as described in Icicles - Multi-Commands, Icicles - More About Multi-Commands, and Icicles - Choose All Candidates.

In fact, there is no reason not to define your commands as multi-commands – you lose nothing, and you gain a lot. Whenever it is appropriate for a user to possibly want to act on multiple objects, define a multi-command that does that.

An anecdote, to make the point. An Icicles user sent me an email saying how much he appreciated Icicles multi-commands, and asking if I would add a multi-command version of ‘insert-buffer’. I did so, but I replied to him that the definition is trivial: it is identical to the definition of ‘icicle-buffer’, except that the action function is ‘insert-buffer’ instead of ‘switch-to-buffer’.

The point is to not be afraid of defining multi-commands yourself. You do not really need to have me add a multi-command to Icicles in most cases; you can easily define it yourself. Here is a simple definition of ‘icicle-insert-buffer’. You will understand it in detail after reading the next section.

  (icicle-define-command icicle-insert-buffer
    "Multi-command version of `insert-buffer'." ; Doc string
    insert-buffer                               ;  Action function
    "Buffer: "                                  ; `completing-read' args
    (mapcar #'(lambda (buf) (list (buffer-name buf))) (buffer-list))
    nil t nil 'buffer-name-history
    (icicle-default-buffer-names current-prefix-arg) nil)

Macros ‘icicle-define-command’ and ‘icicle-define-file-command’ make it easy to define a multi-command. Without them, it is sometimes not so easy, depending on the complexity of your action functions. See Icicles - Multi-Commands the Hard Way for a taste of what is involved. If you read that page first, make sure you come back here to see how easy things can be.

Here is how you might define a multi-command to delete one or more files or directories:

1. Define the multi-command, ‘my-delete-file’:

  (icicle-define-file-command
   my-delete-file                  ; Command name
   "Delete a file or directory."   ; Doc string
   my-delete-file-or-directory     ; Function to perform the action
   "Delete file or directory: "    ; `read-file-name' arguments...
   default-directory nil t)

2. Define the action function that deletes a single file:

  (defun my-delete-file-or-directory (file)
    "Delete file (or directory) FILE."
    (condition-case i-delete-file
        (if (eq t (car (file-attributes file)))
            (delete-directory file)
          (delete-file file))
      (error (message "%s" (error-message-string i-delete-file))
             (error "%s" (error-message-string i-delete-file)))))

There are two parts to the definition of ‘my-delete-file’:

1. The definition of the command itself, using ‘icicle-define-file-command’.

2. The definition of an action function, ‘my-delete-file-or-directory’, which deletes a single file (or directory), given its name.

It is #1 that is of interest here, because that is essentially what you do to define any multi-command.

The details of #2 are less interesting, even if more complex in this case: ‘my-delete-file-or-directory’ checks whether its argument is a file or directory, and then tries to delete it. If an error occurs, it prints the error message and then returns the message, so that the calling command can report on all deletion errors.

In #1, the arguments to ‘icicle-define-file-command’ are straightforward:

These are the same things you would need if you were defining a simple command to delete a single file or directory. The only differences here are that you:

When you use ‘icicle-define-file-command’, the action function is called on the result of ‘read-file-name’, and it is also bound to ‘icicle-candidate-action-fn’, so that it will be applied to the current candidate via ‘C-RET’ or ‘C-mouse-2’.

Command ‘icicle-all-candidates-action’ (`C-!' – see Icicles - Choose All Candidates) can report in buffer ‘*Help*’ on the objects that it did not act upon successfully. For this reporting to work, the function bound to ‘icicle-candidate-action-fn’ (e.g. ‘my-delete-file-or-directory’, above) should return ‘nil’ for “success” and non-‘nil’ (for example, an error message) for “failure”, whatever “success” and “failure” might mean in the particular context of use. This is not a requirement, except if you want to take advantage of such reporting. For a command that deletes files, it is important to let the user know which deletions failed when s?he tries to delete all matching candidates at once.

If the command you want to define acts on objects other than files, then use ‘icicle-define-command’ instead of ‘icicle-define-file-command’ – the only difference is that you then supply the arguments for ‘completing-read’ instead of those for ‘read-file-name’.

To let users know that a command is a multi-command, and how to use it as such, ‘icicle-define-command’ and ‘icicle-define-file-command’ automatically add this explanation to the doc string you provide for the multi-command:

  Read input, then call `<your action function name>' to act on it.
  Input-candidate completion and cycling are available.  While
  cycling, these keys with prefix `C-' are active:
  `C-mouse-2, `C-RET' - Act on current completion candidate only
  `C-down', `C-wheel-down' - Move to next completion candidate and act
  `C-up', `C-wheel-up' - Move to previous completion candidate and act
  `C-next'  - Move to next apropos-completion candidate and act
  `C-prior' - Move to previous apropos-completion candidate and act
  `C-end'   - Move to next prefix-completion candidate and act
  `C-home'  - Move to previous prefix-completion candidate and act
  `C-!'     - Act on *all* candidates, successively (careful!)
  When candidate action and cycling are combined (e.g. `C-next'), user
  option `icicle-act-before-cycle-flag' determines which occurs first.
  With prefix `C-M-' instead of `C-', the same keys (`C-M-mouse-2',
  `C-M-RET', `C-M-down', and so on) provide help about candidates.
  Use `mouse-2', `RET' or `S-RET' to finally choose a candidate, or
  `C-g' to quit.
  This is an Icicles command - see `icicle-mode'.

Notice that the doc string of your new multi-command references your action function (e.g. ‘my-delete-file-or-directory’). The doc string you provide for the multi-command can thus be a little more abstract, leaving any detailed explanation of the action to the doc string of your action function.

To provide more flexibility, ‘icicle-define-command’ and ‘icicle-define-file-command’ provide some predefined key bindings and allow for additional arguments.

Here is a definition of a multi-command, ‘change-font’, that reads a font name and changes the selected frame to use that font.

   1 (icicle-define-command
   2  change-font "Change font of current frame."
   3  (lambda (font)
   4    (modify-frame-parameters orig-frame
   5                             (list (cons 'font font))))
   6  "Font: " (mapcar #'list (x-list-fonts "*"))
   7  nil t nil nil nil nil
   8  ((orig-frame  (selected-frame))
   9   (orig-font   (frame-parameter nil 'font)))
  10  nil
  11  (modify-frame-parameters orig-frame
  12                           (list (cons 'font orig-font)))
  13  nil)

The arguments to ‘icicle-define-command’ here are as follows:

The following bindings are predefined – you can refer to them in the command body:

Before running any “undo” code that you supply, the original buffer is restored, in case of error or user quit (‘C-g’).

Most of the arguments to ‘icicle-define-command’ are optional. In this case, optional arguments were provided to save (lines 8-9) and then restore (lines 11-12) the original font and frame.

Several top-level Icicles commands have been defined using ‘icicle-define-command’ and ‘icicle-define-file-command’. You can use their definitions as models for your own multi-commands.

For simplicity, the descriptions of most of these commands are singular actions (e.g. “kill a buffer”), but each of them can be used to act on any number of items any number of times (e.g. kill one or more buffers). I recommend that you follow a similar naming convention – remember that the doc string will let users know that the command can be used on multiple objects.

Macros ‘icicle-define-command’ and ‘icicle-define-file-command’ define a multi-command in a simple way. Sometimes you will need a little more flexibility. In that case, you can use higher-order functions ‘icicle-explore’ and ‘icicle-apply’ to define a multi-command. See Icicles - Defining Tripping Commands.

Are Users Dependent on Icicles To Use Multi-Commands?

For users to be able to take advantage of the Icicles features that your multi-command provides, they must load Icicles. You can do this for them, by adding (require 'icicles nil t) to your code. The last two arguments mean that no error will be raised if for some reason Icicles cannot be found or successfully loaded.

But that brings up another question: What happens to your multi-command if Icicles is not available for a user, or s?he does not want to load it? No problem – your multi-command then automatically turns into a normal, single-choice command -- graceful degradation.

Similarly, users can always turn off ‘icicle-mode’ at any time, to return to the standard Emacs behavior.

Users will, in any case, need to load Icicles at compile time, in order to byte-compile your library that calls macro ‘icicle-define-command’ or ‘icicle-define-file-command’ – either that, or you can duplicate the definitions of the macros in your library. To let users load Icicles at (only) compile time, add this to your library that defines multi-commands:

  (eval-when-compile '(require icicles))

See also:


Previous: Icicles - Candidates with Text PropertiesIciclesIciclesIndexNext: Icicles - Defining Tripping Commands

DrewsElispLibraries referenced here: Lisp:icicles.el, Lisp:synonyms.el

CategoryCommands CategoryBufferSwitching CategoryCompletion CategoryRegexp CategoryDirectories CategoryFiles CategoryProgrammerUtils CategoryCode