Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return the value instead of key by completing-read

(completing-read
 "Complete a foo: "
 '(("foobar1" "~/foobar1/") ("barfoo" "/usr/barfoo/") ("foobaz" "/hello/")))

As shown above, I would like to prompt for "foobar1","barfoo", and "foobaz" but getting the paired directory in return.

Moverover, if I have a hash-table like this

(cl-defstruct person ID name)
(setq person-object (make-person :ID 123 :name "foo"))
(setq person-table (make-hash-table))
(pushash (person-ID person-object) person-object person-table)

How can I prompt for the person name but getting a person ID in return?

like image 715
tom Avatar asked Feb 14 '16 10:02

tom


2 Answers

There's no way to get completing-read to return the value instead of the key, so you have to do the lookup yourself:

(let ((completions '(("foobar1" "~/foobar1/") ("barfoo" "/usr/barfoo/") ("foobaz" "/hello/"))))
  (cadr (assoc (completing-read "Complete a foo: " completions) completions)))

As for the hash table, since the name is not the key, you need to iterate through every object in the hash table to find it, using maphash. Since it would be wasteful to keep iterating after you've found what you're looking for, you could use catch and throw, like this:

(catch 'found-it
  (maphash
   (lambda (key value)
     (when (equal (person-name value) desired-name)
       (throw 'found-it key)))
   person-table))

This will return the person ID, or nil if there's no person whose name equals desired-name.

like image 193
legoscia Avatar answered Sep 21 '22 05:09

legoscia


@legoscia provided a good answer: completing-read does not give you access to the value associated with a key that it uses for completion. E.g., for an alist COLLECTION argument, it does not give you access to the cdr of a chosen alist key.

For an alist you can use assoc to get the first matching alist element, and for a hash table you can maphash or do a get.

But these approaches preclude getting the particular value associated with a particular chosen key occurrence when there are duplicates of the key, that is, when multiple candidates have the same key, or name.

You cannot get the 2nd matching element, or the 13th. In fact, vanilla Emacs completing-read eliminates duplication of completion candidates that have the same key (name). For vanilla Emacs, any information in the cdr of an alist entry is wasted. You can use an alist, for convenience, if you already have one, but if not then you might as well just use a list of names (strings or symbols), not conses.

If you use Icicles then alist entries are not wasted. There is no problem retrieving cdr values. You can easily get to the complete information of a candidate you choose, after completing-read is done.

Icicles does this by using propertized strings as candidates, and by enhancing completing-read so that it can return the complete string, properties and all, that a user chooses. You can recuperate the complete alist entry from the propertized string that is returned.

When is it important to be able to have and use duplicate keys that can have different associated values? And how can a user tell them apart (e.g., in *Completions*), if they are duplicates?

Examples:

  • Bookmarks that have the same name but are for different targets - for example, for different files with the same relative name in different directories.

  • Lines or other text in a buffer that match a pattern or that contain a marker. This includes matches in Icicles search, where you can define search contexts any way you like (not just lines). It also includes buffer zones (including restrictions, aka narrowings) and buffer positions (markers).

  • Candidates that have the same text but whose annotations are different. (User input is not matched against annotations shown in *Completions*.)

  • Imenu items that have the same name, e.g., multiple definitions of objects (e.g. functions) that have the same name.

  • Tagged items (e.g. functions) that have the same name.

  • Candidates that are other Lisp objects, such as frames, that can have the same name.

In Icicles, how does a user choose one among several completion candidates that have the same name?

  • Users can control the order (sorting) of the candidates, including changing order on the fly. *Completions* shows them to you in a specific order. You can cycle among candidates or choose any of them directly. You are not limited to matching, to choose. (With duplicate candidates, matching can be insufficient to get you to only one of them.)

  • *Completions* can also show you additional information about candidates, which distinguishes them even when they have the same name/text. Such info could be surrounding text (if the candidates are matching buffer text), or candidate metadata (e.g. file or bookmark attributes).

  • You can also see important additional information about the current candidate (e.g. during cycling) in the mode line.

  • You can get additional information (complete *Help*) about the current candidate on demand, by hitting a key.

What do you have to do, to be able to take advantage of this Icicles feature in your own code?

See Defining Tripping Commands for how to define your own commands that let users trip among (explore) candidates might have associated positional or other navigational information. (See Tripping for predefined Icicles tripping commands.)

A brief overview of what to do in your command:

  1. Bind variable icicle-whole-candidate-as-text-prop-p to non-nil.

  2. Set variable icicle-candidates-alist to the alist that you pass to completing-read. This has the effect of encoding, as a text property on the candidate display string, the entire corresponding original alist entry.

  3. Use icicle-get-alist-candidate after calling completing-read, to recover that complete information about the candidate the user chooses, that is, the complete alist element, kncluding the cdr.

(See also: Note to programmers using Icicles.)

like image 45
Drew Avatar answered Sep 21 '22 05:09

Drew