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: Mon, 11 Jun 2007 11:33:40 -0700

>     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.
>
> It is not out of the question to do this.  However, it would be nicer
> to find a cleaner way.  Can you do most of these changes through key
> bindings?  Which changes are left, that can't be done that way (or not
> cleanly)?  Can we find non-ad-hoc ways of implementing them?

I'd propose adding Icicles first as an optional library, without trying to
integrate any of it deeply with Emacs. Later, we could look into a possible
tighter integration. We might even want later to move some of the behavior
to vanilla Emacs completion.

The `completing-read' code is here:
http://www.emacswiki.org/cgi-bin/wiki/icicles-fn.el. Perhaps others more
familiar with Emacs implementation can advise whether some of this
redefinition could or should be moved to the vanilla definition of
`completing-read' or what might be a "cleaner way" to do what is done now
this way. I would advise just leaving this code as is, at least for now.

>     it can replace some top-level commands with Icicles
>     versions (e.g. `find-file' with `icicle-find-file').
>

I should be clear that this is not about redefining commands; it is about
remapping their bindings: the bindings for `find-file' are mapped to
`icicle-find-file'.

> Why does it do this?

Because those replacements are enhanced versions. They are typically
multi-commands that 1) keep the same behavior as the original, as long as
you don't use any special Icicles minibuffer bindings, but also 2) let you
take advantage of those minibuffer bindings (e.g. `C-RET') to get the
multi-command behavior.

With the replacement, for instance, you can open any number of files, using
completion, with one invocation of `C-x C-f', and you can optionally delete
selected matching files on the fly (with `S-delete'). You can of course
change your minibuffer input during that one invocation, to match different
file names. (`M-k' clears the minibuffer.)

Again, this binding replacement is, in any case, optional for a user (option
`icicle-bind-top-level-commands-flag'), and it is only for
`icicle-mode-map'.

>     >     * 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.
>
> How does it know what to call?  Is it the same command that invoked
> the minibuffer?  Do you have to define a new function for this,
> one for each command that uses the feature?

A global variable, `icicle-candidate-fn', is bound to the function to use
for the action. If it is bound to nil, then `C-RET' etc. are associated with
the default action, which is to simply display individual help about the
candidate. (The help action is also always available on `C-M-RET' etc.)
Candidate help tries to DTRT, depending on the type of candidate (see
command `icicle-help-on-candidate').

Yes, you must define a new command, in order to adapt a given existing
command to multi-command use. This is a good thing, as some commands might
not lend themselves well to multi-command use (i.e. it might make no sense
for them, adding nothing). And, more often, it is appropriate for some
commands to have a slightly different action function for candidates during
completion than after completion (for instance, because the
buffer/window/frame context or the use context might be different).

The command used for the `C-RET' action can be any command. Sometimes
`C-RET' performs the same action as the command invoking completion would
carry out on the candidate chosen finally. Sometimes the action performed is
a bit different or even quite different.

The particular command and its context of use determines what is appropriate
(most useful). The devil is in the details of the particular command and its
use cases. We can generalize to some extent (that is what the
multi-command-defining macros are for), but the best definition of a given
multi-command should be decided case by case.

Also, the command that invoked completion is not the same thing as the part
of that command that actually acts on the candidate that is chosen - what I
call the action function, bound to `icicle-candidate-action-fn'. Also,
because of the difference in context (completion has not yet ended when the
action is carried out), some window, buffer, etc. management needs to be
taken into account.

You can take a look at the multi-commands that I have defined. They are all
in file icicles-cmd.el. You can find them by searching for any of these:

1. icicle-define-command
2. icicle-define-file-command
3. icicle-candidate-action-fn

#1 and #2 are those multi-commands that use the same action function for
final action and action during completion. #3 are other commands, which bind
`icicle-candidate-action-fn' explicitly.

The macro expansion of a multi-command defined using #1 or #2 is quite
complex. Some of its definition implements context management (window,
frame, buffer), and some of it implements treatment of possible errors from
application of the action function. For example, the original buffer,
window, or frame, to which we often want to return, might have been deleted
by application of the action to a particular candidate (e.g. if the action
is to kill the buffer, and the candidate acted on is the original buffer).
And we usually want to keep the focus after action application on the
minibuffer's frame, so that we can possibly keep using `C-RET' etc. on other
candidates.

To see what I mean about the complex definition, load icicles-cmd.el (not
.elc) and then use `symbol-function' on a command defined with one of the
macros. Or take a look at the macro definitions themselves, in
icicles-mac.el.

The optional alternative action function and the optional deletion-action
are treated similarly. For instance, binding
`icicle-candidate-alternative-action-fn' provides an alternative action,
bound to `C-S-RET' etc.

See
http://www.emacswiki.org/cgi-bin/wiki/Icicles_-_Defining_Icicles_Commands
for more about defining multi-commands. See
http://www.emacswiki.org/cgi-bin/wiki/Icicles_-_Multi-Commands_the_Hard_Way
for more about how the macros work.

> It sounds like an ugly mechanism,

What does? Why?

> so if this feature is worth adding,
> I would rather it be implemented differently.  The cleanest way I can
> think of is to set a flag telling call-interactively to call the
> function then loop around, keeping the same prefix argument.

Sorry, I don't understand this. Please elaborate. It "sounds like" you are
redesigning without even being familiar with the design.

I am not into redesigning Icicles or re-implementing the basic design. If
others wish to do that later, that's another matter. A lot of time, testing,
and use has gone into the current design and implementation. It works well,
and I think it is quite clean. When  not in Icicle mode, a user cannot tell
that s?he was ever in it. And when in Icicle mode, s?he can still use Emacs
in the same way as usual, with no real perception of a difference, as long
as s?he doesn't use any of the extra minibuffer bindings.

In some ways, the implementation is non-trivial - you don't want to make
cavalier changes, and you must retest lots of scenarios after a fundamental
change. Icicles is very general, which means that it has lots of different
use scenarios. There are some global variables that hold various kinds of
state, which means that some parts of the code are tightly coupled with
other parts. I haven't done that gratuitously, but only when I felt it was
necessary. That doesn't mean that there might not sometimes be a better way;
it means only that one must be careful making changes to the core
implementation.

I'm not against making minor changes that I become convinced would be
improvements, but I don't want to start over or possibly jeopardize things
that work well by making fundamental changes that are not simple. Vague "do
it this way instead" or "n'y a qu'a" suggestions won't convince me. If you
want to discuss concrete implementation details, that's fine. I'm open to
change, but I don't want to break things just because someone thinks s?he
has a better idea (without making an effor to try out that idea or discuss
it concretely).

If Icicles does not break anything, and it does not have a lasting effect
when you leave Icicle mode, then I think how it is implemented should not be
a concern. How it is implemented can of course be examined to help determine
if it breaks something.

After FSF owns the code, others can take responsibility for breaking
Icicles, if they like. Or I can continue to maintain it for FSF - which I
prefer. I intend anyway to maintain and enhance my own version of Icicles,
which I will continue to make available on Emacs Wiki. If my own version and
the Emacs version begin to diverge so much that I cannot work on the former,
then I'll go back to working on just the latter.

> keeping the same prefix argument.

Wrt prefix args: C-u is treated specially during completion, so that, for
instance, you can apply it to individual candidates that you act on using
`C-RET'. That is, you can use a different prefix argument each time you act
on a candidate, if you like. Remember, that, by default, you can act on the
same candidate multiple times. Sometimes that makes sense, sometimes it does
not. You can bind `icicle-use-candidates-only-once-flag' to control this.

See `icicles-mcmd.el' for the code (e.g. `icicle-universal-argument').

> This should work with ALL commands and avoid the need to define
> separate multi-commands.

Sorry, I don't agree.

1. It's not equally appropriate for all commands. It's not necessary for all
commands.

2. It's appropriate, for many commands, to specialize the action behavior
during completion. See the commands that explicitly bind
`icicle-candidate-action-fn', as opposed to just using
`icicle-define-command'. Command `icicle-search', for instance, could not be
defined using `icicle-define-command'.

> Once multi-commands are gone, does Icicles need to
> redefine any commands?  Is it just a matter of changes
> inside completing-read?

Sorry, I don't understand what you mean. Multi-commands will not be "gone".
Multi-commandness will not be extended automatically to all commands. That
is not appropriate.

Are you asking for a list of Emacs commands that Icicles redefines when you
are in Icicle mode (and restores when you exit that mode)? This is the
list - it is also in the header of `icicles.el':

 completing-read, display-completion-list, exit-minibuffer,
 minibuffer-complete-and-exit, read-file-name,
 read-from-minibuffer, read-string, dabbrev-completion,
 lisp-complete-symbol, mouse-choose-completion,
 choose-completion-string, completion-setup-function,
 switch-to-completions, next-history-element (advised only),
 repeat-complex-command

It's possible that some of these redefinitions could be moved to vanilla
Emacs permanently. Typically, the original definition is extended only
slightly. For instance, the `repeat-complex-command' definition is extended
to read the command to be repeated using `completing-read', so that you can
use completion on the history list and use `C-,' to sort the candidate list
(in *Completions*) in different ways.

If `icicle-bind-top-level-commands-flag' is non-nil, then various top-level
bindings are redefined for use in Icicle mode - the list is in
`icicles-mode.el'. In `icicle-mode-map', for example, `abort-recursive-edit'
is remapped to `icicle-abort-minibuffer-input'. The original binding is
restored when Icicle minor mode is exited.

As another example, `completion-setup-function' is modified so that it does
not print the help lines at the top of *Completions*. That printing is done
instead in `display-completion-list', so that the *Completions* window can
be fit to the buffer. Icicles formats the *Completions* display
"intelligently", determining a useful number of columns and height, and it
fits the window to the formatted buffer. (This formatting, e.g. column
spread and default window width, is under the control of user options.) That
is an enhancement that we might want to consider for vanilla Emacs.

As a third example, `switch-to-completions' is extended to always select the
*Completions* window, even when it is on another frame.

In sum, any attempt to integrate such changes tightly with the vanilla Emacs
code needs to be done on a case-by-case basis. I suggest that there is no
need to do that at all now, but it could be done over time, if some such
extensions were deemed useful to vanilla Emacs also.







reply via email to

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