[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: completing-read return meta-information?
From: |
Stephen Leake |
Subject: |
Re: completing-read return meta-information? |
Date: |
Wed, 16 Sep 2015 12:45:37 -0500 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/24.5 (windows-nt) |
Drew Adams <address@hidden> writes:
>> I tried storing the directory info in a text property of the
>> completion string; that was not returned.
>
> Here is a thread about this from 2008, for instance:
> http://lists.gnu.org/archive/html/emacs-devel/2008-06/msg01503.html
That thread includes a request from Stefan for a clear use case for
this. So let me make my case clear.
I'm implementing file name completion in projects. The details vary with
each backend. Two backends I've implemented so far look like this:
(cl-defgeneric project-find-file (prj filename)
"Find FILENAME with completion in current project PRJ."
(let* ((flat-path (project-flat-search-path prj))
(regexp (project-ignore-files-regexp prj))
(predicate
(lambda (filename)
;; FIXME: should call project-ignores here with each directory
(not (string-match regexp filename)))))
;; (project-ignore-files-regexp prj) matches filenames, not
;; uniquified filenames. So it must be applied in
;; `find-file-path-completion-table', not `completing-read'.
(setq filename
(completing-read
"file: " ;; prompt
(completion-table-dynamic (apply-partially
'find-file-path-completion-table
flat-path predicate))
nil
t ;; require match
filename
))
;; We construct a relative path to ensure the filename is found on
;; `flat-path'.
(when (string-match find-file-uniquify-regexp filename)
(let ((dir (match-string 2 filename))
(prefix "../")
(i 0))
(while (< i (length dir))
(when (= (aref dir i) ?/)
(setq prefix (concat prefix "../")))
(setq i (1+ i)))
(setq filename
(concat prefix
dir
"/"
(match-string 1 filename)))
))
(let ((absfilename (locate-file filename flat-path nil)))
(if absfilename
(find-file absfilename)
;; FIXME: need human-readable name for project
(error "'%s' not found in project." filename)))
))
(defun find-file-complete-global (filename)
"Prompt for completion of FILENAME in a Gnu global project."
(setq filename
(completing-read
"file: " ;; prompt
(completion-table-with-cache #'find-file-complete-global-table) ;;
collection
nil ;; predicate
t ;; require match
filename
))
(when (string-match find-file-uniquify-regexp filename)
;; Get partial dir from conflict
(setq filename (concat (match-string 2 filename) (match-string 1
filename))))
;; If there are two files like:
;;
;; src/keyboard.c
;; test/etags/c-src/emacs/src/keyboard.c
;;
;; and the user completes to the first, the following global call
;; will return both. The desired result is always the shortest.
(with-current-buffer (cedet-gnu-global-call (list "--ignore-case" "-Pa"
filename))
(let ((paths (split-string (buffer-substring (point-min) (point-max))
"\n" t)))
(setq paths (sort paths (lambda (a b) (< (length a) (length b)))))
(car paths)))
)
There is a desire to refactor this so that the only difference is inside
the completion table.
Currently, that is not possible, because completing-read does not return
the absolute path of the file that the user selected. Therefore the
calling code must do additional work, that is different for the two
backends. That work typically duplicates some of what the completion
table has already done.
The solution is to make completing-read return more information.
One way to do this with the current completing read code is to add the
entire path to the completion string, as a suffix. But that results in a
horrible user experience; in the Emacs project on my disk, "locate"
would complete to:
locate{.rnc</Projects/emacs/master/etc/schema/>
.el</Projects/emacs/master/lisp/cedet/ede/>
.elc</Projects/emacs/master/lisp/cedet/ede/>
.el</Projects/emacs/master/lisp/>
.elc</Projects/emacs/master/lisp/>}
instead of the mininmal:
locate{.rnc .el<lisp/cedet/ede/> .elc<lisp/cedet/ede/> .el<lisp/> .elc</lisp/>}
So a better solution is to allow completing-read to return the
additional information in some other way.
Three ways seem straightforward:
1) The completion table can add the absolute path in an `abspath' text
property of the completion string.
The rest of the completion functions must preserve the text
property.
This may need a new optional arg to completing-read, if current code
relies on text properties being discarded.
2) The completion table can return an alist (note this is already
supported), with the cdr of each element being the absolute path. In
this case, completing-read (and other completion functions?) returns
the selected cons.
To avoid breaking current alist completion-tables, this needs a new
optional argument to completing-read (and other completion
functions); cons-result, nil by default.
Currently, when a completion-table returns an alist, the predicate
supplied to completing-read is applied to the cdr of the elements.
That could be useful for the file completion case as well, although
it will probably be more efficient to apply the predicate inside the
table, to prune the file tree.
3) Extend completion-metadata to cover this usage; then the calling code
calls completion-metadata with the completing-read string result to
retrieve the absolute path.
This has the downside that the completion table code must repeat
work done previously to recompute the metadata.
The calling code then has a standard way of retrieving the absolute path
from the result of completing-read (either get the `abspath' text
property, take the cdr, or call completion-metadata), and can be
independent of the completion backend.
I think the alist approach is more in keeping with general Emacs style,
although xref-read-identifier is one recent precendent for using text
properties in a similar way.
Which is the best approach depends mostly on how much code inside the
completion functions must be changed.
--
-- Stephe
- completing-read return meta-information?, Stephen Leake, 2015/09/16
- RE: completing-read return meta-information?, Drew Adams, 2015/09/16
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/16
- Re: completing-read return meta-information?,
Stephen Leake <=
- Re: completing-read return meta-information?, Stefan Monnier, 2015/09/16
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/17
- Re: completing-read return meta-information?, Stefan Monnier, 2015/09/17
- Re: completing-read return meta-information?, Dmitry Gutov, 2015/09/17
- Re: completing-read return meta-information?, Stefan Monnier, 2015/09/17
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/21
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/21
- Re: completing-read return meta-information?, Stefan Monnier, 2015/09/21
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/22
- Re: completing-read return meta-information?, Stephen Leake, 2015/09/26