From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Stephen Leake Newsgroups: gmane.emacs.devel Subject: Re: completing-read return meta-information? Date: Wed, 16 Sep 2015 12:45:37 -0500 Message-ID: <86twquxnpa.fsf@stephe-leake.org> References: <86y4g6zcuo.fsf@stephe-leake.org> <7c37cd21-a9e0-48fa-b5a2-a32595c43dda@default> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1442425581 23049 80.91.229.3 (16 Sep 2015 17:46:21 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 16 Sep 2015 17:46:21 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Sep 16 19:46:11 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZcGmX-0003Vv-FU for ged-emacs-devel@m.gmane.org; Wed, 16 Sep 2015 19:46:09 +0200 Original-Received: from localhost ([::1]:52273 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZcGmW-000179-Nx for ged-emacs-devel@m.gmane.org; Wed, 16 Sep 2015 13:46:08 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:48224) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZcGmP-00016r-F9 for emacs-devel@gnu.org; Wed, 16 Sep 2015 13:46:05 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZcGmK-0004xh-BU for emacs-devel@gnu.org; Wed, 16 Sep 2015 13:46:01 -0400 Original-Received: from gproxy5-pub.mail.unifiedlayer.com ([67.222.38.55]:59320) by eggs.gnu.org with smtp (Exim 4.71) (envelope-from ) id 1ZcGmK-0004x2-3Q for emacs-devel@gnu.org; Wed, 16 Sep 2015 13:45:56 -0400 Original-Received: (qmail 11637 invoked by uid 0); 16 Sep 2015 17:45:50 -0000 Original-Received: from unknown (HELO cmgw4) (10.0.90.85) by gproxy5.mail.unifiedlayer.com with SMTP; 16 Sep 2015 17:45:50 -0000 Original-Received: from host114.hostmonster.com ([74.220.207.114]) by cmgw4 with id Htlh1r00S2UdiVW01tlkfT; Wed, 16 Sep 2015 11:45:50 -0600 X-Authority-Analysis: v=2.1 cv=QdD14Krv c=1 sm=1 tr=0 a=CQdxDb2CKd3SRg4I0/XZPQ==:117 a=CQdxDb2CKd3SRg4I0/XZPQ==:17 a=DsvgjBjRAAAA:8 a=f5113yIGAAAA:8 a=9i_RQKNPAAAA:8 a=hEr_IkYJT6EA:10 a=x_XPkuGwIRMA:10 a=ff-B7xzCdYMA:10 a=yPCof4ZbAAAA:8 a=mDV3o1hIAAAA:8 a=RBalKrRvKpC1UBcXx9oA:9 a=4iOf2Ch62e-GD_5u:21 a=ZXb9jJts3tcJ6VE9:21 a=kKalnZgzkGYA:10 Original-Received: from [76.218.37.33] (port=57196 helo=TAKVER2) by host114.hostmonster.com with esmtpa (Exim 4.84) (envelope-from ) id 1ZcGm5-0002vG-1R for emacs-devel@gnu.org; Wed, 16 Sep 2015 11:45:41 -0600 In-Reply-To: <7c37cd21-a9e0-48fa-b5a2-a32595c43dda@default> (Drew Adams's message of "Wed, 16 Sep 2015 08:00:39 -0700 (PDT)") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (windows-nt) X-Identified-User: {2442:host114.hostmonster.com:stephele:stephe-leake.org} {sentby:smtp auth 76.218.37.33 authed with stephen_leake@stephe-leake.org} X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 67.222.38.55 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:190036 Archived-At: Drew Adams 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 .el .elc .el .elc} instead of the mininmal: locate{.rnc .el .elc .el .elc} 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