emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: propose adding Icicles to Emacs


From: Drew Adams
Subject: RE: propose adding Icicles to Emacs
Date: Sun, 10 Jun 2007 10:16:27 -0700

>     Icicles extends `completing-read', `read-file-name' etc. in
>     various ways. It provides users with yet another way to
>     complete minibuffer input (like ido, ibuffer,...). I propose
>     adding it to Emacs in a way similar to how ido and
>     ibuffer are offered: for optional use.
>
> Does Icicles provide all its features in a modular way, by extending
> the completion mechanism?

It uses a minor mode, `icicle-mode'. It redefines some primitives such as
`completing-read' for use when the mode is active, and it restores them to
the original definitions when the mode is turned off.

If integrated with Emacs, this could be done by changing `completing-read'
etc. to act differently when `icicle-mode' is on, unless that is a no-no.

Icicles also makes additional minibuffer bindings, and, depending on the
value of a user option, it can replace some top-level commands with Icicles
versions (e.g. `find-file' with `icicle-find-file'). These actions are
reversed when the minor mode is exited, so you get back the vanilla Emacs
key bindings and commands.

Icicles does not change global bindings, with this exception: `S-TAB' is
bound in several keymaps to provide key completion. Any existing binding of
`S-TAB' is never replaced, however.

>     * Multi-commands: Define a command that acts on one foobar,
>       and be able to use it also on multiple foobars in a single
>       invocation.
>
> How is that implemented?

A minibuffer key binding, `C-RET' (or `C-mouse-2' in
`completion-list-mode-map'), calls a command that acts on the current
completion candidate, where "current" is determined by cycling or matching.

If you don't use `C-RET', and you just pick a single candidate with `RET' or
`mouse-2', then only that candidate is acted on, and completion exits. So,
you define a command that acts on a single candidate, and Icicles lets you
also use that command to act on multiple candidates (or on the same
candidate multiple times) during a single completion invocation.

A multi-command binds the action function to `C-RET' in the minibuffer to
perform the appropriate action on the current candidate (e.g. during
cycling).

You can use macro `icicle-define-command' to do that for you, to define a
multi-command that uses the same action function to (1) act on the current
candidate during completion (without exiting) and (2) act on the final
candidate chosen with RET. In general, the action on the current candidate
during completion need not be the same action that the command performs on
the candidate finally chosen, but in practice it is often the same.

Example - here is the definition of `icicle-find-tag', which is a
multi-command version of `find-tag'. It uses `find-tag' as the action
function, so `find-tag' is applied to the final candidate you choose with
`RET' and also to other candidates if you use `C-RET' before ending
completion with `RET' or `C-g'.

1 (icicle-define-command icicle-find-tag "Find tag..."
2   find-tag "Find tag: " 'tags-complete-tag nil nil nil nil
3   (funcall (or find-tag-default-function
4                (get major-mode 'find-tag-default-function)
5                'find-tag-default))
6   nil
7   ((completion-ignore-case
8     (if (and (boundp 'tags-case-fold-search)
9              (memq tags-case-fold-search '(t nil)))
10        tags-case-fold-search
11      case-fold-search)))
12  (require 'etags))

line argument
---- --------
1    name of the new command,
     doc string (here, it is the same as `find-tag')
2-6  arguments to completing-read - nothing different here
7-11 any extra bindings needed - `completion-ignore-case' here
12   any initial code executed after binding, before completing
     - here, we just want to be sure `etags' is loaded

The doc string you supply, which is the same here as that for `find-tag', is
supplemented by the macro to inform users that this is a multi-command and
they can use `C-RET' etc. Here is the resulting doc string for
`icicle-find-tag':

 Find tag (in current tags table) whose name...
 [same as `find-tag' doc]

 Read input, then call `find-tag' to act on it.

 Input-candidate completion and cycling are available.  While cycling,
 these keys with prefix `C-' act on the current candidate:

 `C-mouse-2', `C-RET' - Act on current completion candidate only
 `C-down'  - Act, then move to next prefix-completion candidate
 `C-up'    - Act, then move to previous prefix-completion candidate
 `C-next'  - Act, then move to next apropos-completion candidate
 `C-prior' - Act, then move to previous apropos-completion candidate
 `C-!'     - Act on *all* candidates, successively (careful!)

 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 the current
 candidate.

 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'.

In general, defining a multi-command is no more difficult than defining an
ordinary command that performs completion: command name, doc string, args to
completing-read, maybe some bindings, and the (same) action code.

Here is the definition of `icicle-bookmark':

(icicle-define-command icicle-bookmark
  "Jump to a bookmark.
You can use `S-delete' on any candidate bookmark to delete it instead."
  bookmark-jump "Bookmark: " (mapcar #'list (bookmark-all-names))
  nil t nil 'bookmark-history
  (or (and (boundp 'bookmark-current-bookmark)
           bookmark-current-bookmark)
      (bookmark-buffer-name))
  nil
  ((icicle-delete-candidate-object 'bookmark-delete)))

The doc string and extra binding here illustrate another feature. Not only
can you act on multiple candidates with the same action function (here,
`bookmark-jump'), but, if defined for a given multi-command (as here), you
can also act on multiple candidates with an appropriate deletion function
(here, `bookmark-delete'), which is invoked using `S-delete'.

Similarly, while using `C-x C-f' (`icicle-find-file), you can use `S-delete'
on selected candidate files to delete them. (There is a similar macro,
`icicle-define-file-command', for multi-commands that use `read-file-name'
instead of `completing-read'.)

You don't need to use a macro to define a multi-command. It is enough to
bind `icicle-candidate-action-fn' to the action function you want, in your
command that calls `completing-read'. Here, for example, is the definition
of `icicle-Info-index', which I referred to at the start of this thread.

(defun icicle-Info-index ()
  "Like `Info-index', but you can use `C-RET', `C-up' etc."
  (interactive)
  (let ((info-buf (current-buffer))
        (info-window (selected-window))
        (icicle-candidate-action-fn 'icicle-Info-index-action))
    (call-interactively 'Info-index)))

(defun icicle-Info-index-action (topic)
  "Completion action function for `icicle-Info-index'."
  (let ((minibuf-win (selected-window)))
    (set-buffer info-buf)
    (select-window info-window)
    (Info-index topic)
    (select-window minibuf-win)))

It is the predefined command `Info-index' that calls `completing-read'. All
this code does is bind the action function for `C-RET' and ensure that the
right window and buffer are used for that action.

There can also be an alternative action function, in addition to the action
function and the (optional) deletion function. You invoke the alternative
action on selected candidates by using `C-S-RET' instead of `C-RET'
(likewise, `C-S-mouse-2', `C-S-down',...). For example, for `icicle-search',
the action function takes you to the current search hit (via `C-RET' etc.).
The alternative action function performs text replacement on that search hit
(via `C-S-RET').

Keep in mind too that if you just want the other Icicles features (regexp
matching, cycling etc.) and you don't care about having a multi-command,
then you don't need to do anything special when you define a command that
uses completion - the Icicles redefinitions of `completing-read' and
`read-file-name' take care of everything.

> Does Icicles do advice?

Icicles does not use advice. Well, there is one tiny exception, which could
be replaced:

(defadvice next-history-element
  (after icicle-select-minibuffer-contents activate)
  "Select minibuffer contents and leave point at its beginning."
  (when (and icicle-mode
             (memq icicle-init-value-flag
                   '(preselect-start preselect-end)))
    (icicle-select-minibuffer-contents)
    (setq deactivate-mark nil)))

I don't like to use advice (I find it makes debugging harder), but in this
case I did.

If Icicles were integrated with Emacs, either `next-history-element' itself
could be patched to be Icicles-aware or, if that is a no-no, Icicles could
redefine `next-history-element' for `icicle-mode' use only. All this code
does, BTW, is preselect part of the current candidate in the minibuffer, for
easy manipulation or deletion. This preselection is optional.

I'll be glad to answer more questions on the list, but this is all also
explained in the Icicles doc, which is available as HTML on Emacs Wiki or as
plain text in the library files themselves.







reply via email to

[Prev in Thread] Current Thread [Next in Thread]