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: Sat, 16 Jun 2007 15:36:11 -0700

>     2. My `mouse-choose-completion' also records the number of the current
>     completion candidate in a global variable. This is so that
>     Icicles functions that work with the candidate number will also work
>     when you choose a candidate with the mouse, not just by cycling.
>     The candidate number is very important for Icicles, some functionality
>     being dependent on it.
>
> I don't understand what a "candidate number" is, and I think we will
> have to consider this issue along with the core Icicles features.

Let me explain. It's pretty important for Icicles.

The TABLE arg to `completing-read' can be an alist whose elements can be
conses whose cars are strings. The element cdrs can be anything.

You can choose a candidate in different ways in Icicles, including by
cycling to it and using RET, C-RET, or C-M-RET on the "current candidate"
during cycling. You can also choose it with `mouse-2', `C-mouse-2', or
`C-M-mouse-2' in *Completions*.

In some important cases, it is not enough to recuperate the string used in
*Completions* as the return value from `completing-read'. In those cases,
Icicles must recuperate the entire raw candidate value from the TABLE
argument - that is, it needs not only the candidate name but also its
associated alist data, that is, both the car and the cdr of the candidate
element.

One way that Icicles does this (the best way, when it is possible) is to use
the number (i.e. index) of the candidate in the TABLE arg. This index
corresponds to the order of candidates when cycling and the order of the
candidates in *Completions*.

Keep in mind that for some commands there can be different raw candidates
that have the same string name. For example, a candidate to `icicle-search'
looks like this:

 (SEARCH-HIT-STRING . HIT-END-MARKER) or
 ((SEARCH-HIT-STRING BUFFER-NAME) . HIT-END-MARKER), where

. SEARCH-HIT-STRING is the candidate "name", that is, the displayed
  candidate in *Completions*. It is a search-pattern hit.

. HIT-END-MARKER is a marker at the end of the search hit.
  It enables search to go to the hit.

. BUFFER-NAME is a string naming the buffer of the hit. It is
  propertized for *Completions*, to help distinguish hits from
  different buffers when searching across buffers.  That is,
  when multiple buffers or files are used, the candidate
  displayed in *Completions* has two parts: the search-hit
  string and the buffer name. Users can turn this display off
  with a user option, so that no BUFFER-NAME is present.

You can see that unless you cycle to define a "current completion candidate"
to choose, or you choose it with the mouse in *Completions*, just picking it
by completing its name does not necessarily uniquely identify the raw
completion candidate. If there is only one matching string, then it does,
but otherwise it does not.

So, you see that it is important for `mouse-choose-completion' to track the
candidate number, just as cycling tracks it. The need is to identify the
proper raw candidate, to be able to recuperate all of its data.

Yes, to forestall a question you might have, this could be implemented
differently if done inside Emacs itself. Working from without, it is a bit
of a workaround. Although `completing-read' lets you pass an alist as TABLE
arg, it does not give you any way to recuperate the alist element given the
chosen string, unless that string is unique. (You can use `assoc', but that
gives you just the first element that has that string.) `completing-read'
returns only the string that names the candidate, not the entire candidate
element from the alist.

Here, to give you an idea, is the code that recuperates a complete raw
candidate value, given its string part:

(defun icicle-get-alist-candidate (cand)
  "Return completion candidate (full entry) from `icicle-candidates-alist'.
CAND is the name of the candidate (a string).

If user cycled among candidates or used `mouse-2', then use the
current candidate number, and ignore CAND.

Otherwise:
  If only one candidate matches CAND, use that.
  Else raise an error telling user to use cycling or `mouse-2'."
  (and icicle-candidates-alist
       (let ((cand-entries
              (icicle-filter-alist icicle-candidates-alist
                                   icicle-completion-candidates)))
         (if (wholenump icicle-candidate-nb) ; Cycled or used `mouse-2'.
             (elt cand-entries
                  (mod icicle-candidate-nb
                       (length icicle-candidates-alist)))
           ;; If `icicle-completion-candidates' is nil, because user
           ;; didn't use `TAB' or `S-TAB', then `icicle-candidates-alist'
           ;; can contain non-matches.  So, we check for more than one
           ;; match.  However, we can't just use `assoc', because
           ;; candidates might be multi-completions (lists).
           (let ((first-match (icicle-first-matching-candidate
                               cand icicle-candidates-alist)))
             (if (and first-match
                      (not (icicle-first-matching-candidate
                            cand
                            (setq cand-entries
                                  (delete first-match cand-entries)))))
                 first-match            ; Only one match, so use it.
               (error
                "Ambiguous choice. Cycle or use `mouse-2' to choose \
unique matching candidate.")))))))

To simplify, think of `icicle-candidate-alist' as the TABLE arg to
`completing-read'.  `icicle-filter-alist' filters this alist, keeping only
those candidates that are current (i.e., whose strings are currently
displayed in *Completions*).

`icicle-first-matching-candidate' is like `assoc', but it also treats
multi-completions. Again, a multi-completion is an element of the TABLE arg
passed to `completing-read' whose car is a list of strings instead of a
string; the strings are joined together by my `completing-read' and treated
as a single candidate.

Icicles uses cycling and often makes use of multiple candidates that have
the same display appearance (same string), so the position of the string you
choose in the list of candidates is important. Another way of saying this is
that Icicles has a notion of "current candidate", which vanilla Emacs does
not.







reply via email to

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