From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Stephen Leake Newsgroups: gmane.emacs.devel Subject: Re: project--completing-read-strict breaks ada-mode project completion table Date: Wed, 06 Feb 2019 17:20:23 -0800 Message-ID: <86lg2swg14.fsf@stephe-leake.org> References: <20180922154639.23195.66360@vcs0.savannah.gnu.org> <20180922154640.9D58220310@vcs0.savannah.gnu.org> <54108dbc-9d12-06ff-3f1d-151118e9b234@yandex.ru> <4e729d1e-bb31-455f-fd44-e99ae5a6b9fa@yandex.ru> <86zhs5r9lr.fsf_-_@stephe-leake.org> <08de4d90-d678-0524-9356-f9a3515bf0c4@yandex.ru> <86a7k2rabi.fsf@stephe-leake.org> <86sgxso27d.fsf@stephe-leake.org> <69076784-83cb-5cc7-be39-fea990b8535e@yandex.ru> <861s55n6wk.fsf@stephe-leake.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="155037"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.90 (windows-nt) To: emacs-devel Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Feb 07 02:22:39 2019 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1grYOk-000eC4-Ux for ged-emacs-devel@m.gmane.org; Thu, 07 Feb 2019 02:22:39 +0100 Original-Received: from localhost ([127.0.0.1]:32788 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1grYOj-0003sG-P8 for ged-emacs-devel@m.gmane.org; Wed, 06 Feb 2019 20:22:37 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:39838) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1grYO1-0003rc-Tq for emacs-devel@gnu.org; Wed, 06 Feb 2019 20:21:54 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1grYNy-00062v-PW for emacs-devel@gnu.org; Wed, 06 Feb 2019 20:21:53 -0500 Original-Received: from gproxy10-pub.mail.unifiedlayer.com ([69.89.20.226]:50609) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1grYNr-0005cf-00 for emacs-devel@gnu.org; Wed, 06 Feb 2019 20:21:47 -0500 Original-Received: from cmgw11.unifiedlayer.com (unknown [10.9.0.11]) by gproxy10.mail.unifiedlayer.com (Postfix) with ESMTP id C4F2C14042B for ; Wed, 6 Feb 2019 18:20:30 -0700 (MST) Original-Received: from host114.hostmonster.com ([74.220.207.114]) by cmsmtp with ESMTP id rYMggCXWVD8RprYMggdAQx; Wed, 06 Feb 2019 18:20:30 -0700 X-Authority-Reason: nr=8 X-Authority-Analysis: $(_cmae_reason Original-Received: from [76.77.182.20] (port=56536 helo=Takver4) by host114.hostmonster.com with esmtpsa (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.91) (envelope-from ) id 1grYMg-001bvf-CI for emacs-devel@gnu.org; Wed, 06 Feb 2019 18:20:30 -0700 In-Reply-To: <861s55n6wk.fsf@stephe-leake.org> (Stephen Leake's message of "Mon, 21 Jan 2019 11:32:27 -0800") X-AntiAbuse: This header was added to track abuse, please include it with any abuse report X-AntiAbuse: Primary Hostname - host114.hostmonster.com X-AntiAbuse: Original Domain - gnu.org X-AntiAbuse: Originator/Caller UID/GID - [47 12] / [47 12] X-AntiAbuse: Sender Address Domain - stephe-leake.org X-BWhitelist: no X-Source-IP: 76.77.182.20 X-Source-L: No X-Exim-ID: 1grYMg-001bvf-CI X-Source-Sender: (Takver4) [76.77.182.20]:56536 X-Source-Auth: stephen_leake@stephe-leake.org X-Email-Count: 1 X-Source-Cap: c3RlcGhlbGU7c3RlcGhlbGU7aG9zdDExNC5ob3N0bW9uc3Rlci5jb20= X-Local-Domain: yes X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 69.89.20.226 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 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" Xref: news.gmane.org gmane.emacs.devel:233071 Archived-At: --=-=-= Content-Type: text/plain Stephen Leake writes: > Dmitry Gutov writes: > >> And again, it doesn't have to be a separate command, the behavior >> could be customizable and dispatched inside the one command's >> implementation. I've now implemented this. Gnu ELPA has updated versions of path-iterator and uniquified-files. I added a new file completion style; file-root-rel, in uniquified-files/file-complete-root-relative.el. Attached is an elisp file demo-project-file-completion.el, which rewrites project--completing-read-strict to allow either the user or the project backend to specify the completion style, and provides examples of each of three styles: "default"; substring style on absolute file names uniquified-file: complete on basename, then on appended uniquifying directories. (Juri Linkov mentioned a style like this in https://lists.gnu.org/archive/html/emacs-devel/2019-01/msg00081.html) file-root-rel: complete on filenames relative to the project root (assumes a single root). Also attached are emacs-project.el, uniquify-test-project.el, providing example projects used in the demo. A few points: The file names may want to change; file-root-rel reuses some of uniquify-file, and 'uniquify-file' might be better named 'file-basename'. project-file-completion-table can return a list of files, so a backend can use 'git ls' or 'find' to implement it. That is no harder to implement than 'project-files'. The uniquify-file completion style works on a list of files; see the demo case with (uniquify-test-project-completion-style 'default) (project-file-completion-styles '(uniquify-file)) However, file-root-rel does not, because the root directory must be stored somewhere that is accessible from the completion code. project--completing-read-strict handles this by wrapping a list in a completion table function. We could use a global variable to store the root instead, but that's not much better. If there are other completion styles that have global data stored somewhere, perhaps it would make sense to introduce a root defstruct for completion styles, store the completion table and data in that object, and pass that to completion functions instead of the completion table. That could be backward compatible; completion functions would check for the defstruct as well as for a function or a list. Note that project--completing-read-strict let-binds both completion-category-overrides and completion-styles, in order to handle all the possible choices. Some completion table functions require a particular style, mostly for optimization reasons, but also for the global data issue; that is indicated by returning a "style" metadata. Some backends will want to specify the style; Ada works well with uniquify-file, since the file basenames are required to be unique. Java works well with file-root-rel, since the directory part of the basename is significant. uniquify-files.el now adds two functions to completion-styles-alist, for converting strings from user to table input format, or user to data format. It also adds a "style" metadata to the completion table function API. Together with the advice on completing-read-default and test-completion, this could be moved to minibuffer.el. -- -- Stephe --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=demo-project-file-completion.el Content-Transfer-Encoding: quoted-printable ;; From emacs master -Q=0D =0D ;; adjust paths=0D (add-to-list 'load-path "c:/Projects/elpa/packages/uniquify-files")=0D (add-to-list 'load-path "c:/Projects/elpa/packages/path-iterator")=0D (add-to-list 'load-path "c:/Projects/emacs_stephe.main/projects") ;; for em= acs-project.el, uniquify-test-project.el=0D =0D (find-file "c:/Projects/elpa/packages/uniquify-files/uniquify-files-resourc= es/foo-file3.texts2")=0D =0D (require 'uniquify-test-project)=0D =0D ;; adding user choice of completion style for list collections=0D (defcustom project-file-completion-styles '(substring)=0D "Setting of `completion-styles' for project-find-file.=0D Note that a completion table function can specify styles,=0D overriding this.")=0D =0D (defun project--completing-read-strict (prompt=0D collection &optional predicate=0D hist default inherit-input-method)= =0D ;; Tried both expanding the default before showing the prompt, and=0D ;; removing it when it has no matches. Neither seems natural=0D ;; enough. Removal is confusing; early expansion makes the prompt=0D ;; too long.=0D (let* (;; When COLLECTION (a completion table) is a function that=0D ;; returns category styles, we set=0D ;; completion-category-overrides to reflect the table. If the=0D ;; completion table returns metadata category, we assume it=0D ;; will be project-file.=0D (table-styles (cdr (assq 'styles (completion-metadata "" collection nil))= ))=0D (completion-category-overrides=0D (list (list 'project-file (cons 'styles=0D (or table-styles=0D project-file-completion-styles)))))=0D =0D ;; If the completion table is a list, or a function that does=0D ;; not return styles metadata, we set completion-styles to=0D ;; reflect the user choice.=0D (completion-styles (if table-styles nil project-file-completion-styles))= =0D =0D (common-parent-directory=0D (let ((common-prefix (try-completion "" collection)))=0D (if (> (length common-prefix) 0)=0D (file-name-directory common-prefix))))=0D =0D (new-collection=0D (cond=0D ((or table-styles (not (memq 'file-root-rel completion-styles)))=0D collection)=0D =0D (common-parent-directory=0D (apply-partially 'fc-root-rel-completion-table-list=0D collection common-parent-directory))=0D =0D (t collection)))=0D =0D (new-prompt (if default=0D (format "%s (default %s): " prompt default)=0D (format "%s: " prompt)))=0D (res (completing-read new-prompt=0D new-collection predicate t=0D nil hist default inherit-input-method)))=0D (if (and (equal res default)=0D (not (test-completion res new-collection predicate)))=0D (completing-read (format "%s: " prompt)=0D new-collection predicate t res hist nil=0D inherit-input-method)=0D res)))=0D =0D (with-current-buffer "foo-file3.texts2"=0D (let ((uniquify-test-project-completion-style 'uniquify-file)=0D (project-file-completion-styles '(substring)))=0D (message "navigate to uniquify-test/Alice/alice-1/bar-file1.text, using= uniquified filenames")=0D (sit-for 1.0)=0D (project-find-file)=0D ))=0D =0D (with-current-buffer "foo-file3.texts2"=0D (let ((uniquify-test-project-completion-style 'default)=0D (project-file-completion-styles '(uniquify-file)))=0D (message "navigate to uniquify-test/Alice/alice-1/bar-file1.text, using= uniquified filenames")=0D (sit-for 1.0)=0D (project-find-file)=0D ))=0D =0D (with-current-buffer "foo-file3.texts2"=0D (let ((uniquify-test-project-completion-style 'file-root-rel)=0D (project-file-completion-styles '(substring)))=0D (message "navigate to uniquify-test/Alice/alice-2/foo-file1.text, using= relative filenames")=0D (sit-for 1.0)=0D (project-find-file)=0D ))=0D =0D (with-current-buffer "foo-file3.texts2"=0D (let ((uniquify-test-project-completion-style 'default)=0D (project-file-completion-styles '(file-root-rel)))=0D (message "navigate to uniquify-test/Alice/alice-1/bar-file1.text, using= relative filenames")=0D (sit-for 1.0)=0D (project-find-file)=0D ))=0D =0D (with-current-buffer "foo-file3.texts2"=0D (let ((uniquify-test-project-completion-style 'default)=0D (project-file-completion-styles '(substring)))=0D (message "navigate to uniquify-test/Bob/alice-3/foo-file4.text, using a= bsolute filenames")=0D (sit-for 1.0)=0D (project-find-file)=0D ))=0D --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=emacs-project.el Content-Transfer-Encoding: quoted-printable ;; emacs-project.el --- project for Emacs C source code -*- lexical-bindin= g: t -*-=0D =0D (require 'project)=0D (require 'uniquify-files)=0D (require 'file-complete-root-relative)=0D =0D (cl-defstruct (emacs-project)=0D root=0D )=0D =0D (defun emacs-project-try (dir)=0D "For 'project-find-functions'."=0D (when (string-match "emacs" dir)=0D (make-emacs-project :root (locate-dominating-file dir "autogen.sh"))))= =0D =0D (cl-defmethod project-library-roots ((_prj emacs-project))=0D nil)=0D =0D (cl-defmethod project-roots ((prj emacs-project))=0D (list (emacs-project-root prj)))=0D =0D (defun emacs-project-ignore-dir (dir)=0D "Return non-nil if 'dir' should be ignored in file-completion-table."=0D ;; If DIR is ignored, no children of DIR will be passed to this function.= =0D (string-equal ".git" (file-name-nondirectory dir)))=0D =0D (defcustom emacs-project-completion-style 'file-root-rel=0D "Completion style for emacs `project-file-completion-table';=0D determines which completion table to return."=0D :group 'minibuffer ;; same as other completion options=0D :type '(choice=0D (const default) ;; defers to project-file-completion-styles=0D (const file-root-rel)=0D (const uniquify-file))=0D )=0D =0D (cl-defmethod project-file-completion-table ((prj emacs-project) dirs)=0D (let ((iter (make-path-iterator=0D :user-path-non-recursive nil=0D :user-path-recursive (or dirs (list (emacs-project-root prj)))=0D :ignore-function #'emacs-project-ignore-dir)))=0D (cl-ecase emacs-project-completion-style=0D (default=0D (path-iter-all-files iter))=0D =0D (file-root-rel=0D (apply-partially 'fc-root-rel-completion-table-iter iter))=0D =0D (uniquify-file=0D (apply-partially #'uniq-file-completion-table iter))=0D )))=0D =0D (add-to-list 'project-find-functions #'emacs-project-try)=0D =0D (provide 'emacs-project)=0D ;; end of file=0D --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=uniquify-test-project.el Content-Transfer-Encoding: quoted-printable ;; uniquify-test-project.el --- project for uniquify test files -*- lexica= l-binding: t -*-=0D =0D (require 'project)=0D (require 'uniquify-files)=0D (require 'file-complete-root-relative)=0D =0D (cl-defstruct (uniquify-test-project)=0D root=0D )=0D =0D (defun uniquify-test-project-try (dir)=0D "For 'project-find-functions'."=0D (when (string-match "uniquify-files-resources" dir)=0D (make-uniquify-test-project :root (locate-dominating-file dir "foo-file= 3.texts2"))))=0D =0D (cl-defmethod project-library-roots ((_prj uniquify-test-project))=0D nil)=0D =0D (cl-defmethod project-roots ((prj uniquify-test-project))=0D (list (uniquify-test-project-root prj)))=0D =0D (defcustom uniquify-test-project-completion-style 'default=0D "Completion style for uniquify-test `project-file-completion-table';=0D determines which completion table to return."=0D :group 'minibuffer ;; same as other completion options=0D :type '(choice=0D (const default) ;; defers to project-file-completion-styles=0D (const file-root-rel)=0D (const uniquify-file))=0D )=0D =0D (cl-defmethod project-file-completion-table ((prj uniquify-test-project) di= rs)=0D (let ((iter (make-path-iterator=0D :user-path-non-recursive nil=0D :user-path-recursive (or dirs (list (uniquify-test-project-root prj= ))))))=0D (cl-ecase uniquify-test-project-completion-style=0D (default=0D (path-iter-all-files iter))=0D =0D (file-root-rel=0D (apply-partially 'fc-root-rel-completion-table-iter iter))=0D =0D (uniquify-file=0D (apply-partially #'uniq-file-completion-table iter))=0D )))=0D =0D (add-to-list 'project-find-functions #'uniquify-test-project-try)=0D =0D (provide 'uniquify-test-project)=0D ;; end of file=0D --=-=-=--