From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: Dmitry Gutov Newsgroups: gmane.emacs.devel Subject: Re: project--completing-read-strict breaks ada-mode project completion table Date: Fri, 3 May 2019 03:48:26 +0300 Message-ID: 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> <86lg2swg14.fsf@stephe-leake.org> <86wom5vlki.fsf@stephe-leake.org> <86sgwpuk2q.fsf@stephe-leake.org> <86imv99982.fsf@stephe-leake.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------55EDEE3876F9213595E62201" Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="163072"; mail-complaints-to="usenet@blaine.gmane.org" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.6.1 To: Stephen Leake , emacs-devel Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri May 03 03:00:43 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 1hMMZ8-000gCu-Bf for ged-emacs-devel@m.gmane.org; Fri, 03 May 2019 03:00:42 +0200 Original-Received: from localhost ([127.0.0.1]:60740 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMMZ7-0001WI-D6 for ged-emacs-devel@m.gmane.org; Thu, 02 May 2019 21:00:41 -0400 Original-Received: from eggs.gnu.org ([209.51.188.92]:39460) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hMMNR-0001Lw-3E for emacs-devel@gnu.org; Thu, 02 May 2019 20:48:40 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hMMNN-0005iT-EP for emacs-devel@gnu.org; Thu, 02 May 2019 20:48:35 -0400 Original-Received: from mail-wr1-x42a.google.com ([2a00:1450:4864:20::42a]:39259) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hMMNL-0005bf-F5 for emacs-devel@gnu.org; Thu, 02 May 2019 20:48:32 -0400 Original-Received: by mail-wr1-x42a.google.com with SMTP id a9so5688478wrp.6 for ; Thu, 02 May 2019 17:48:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:subject:to:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language; bh=6Zqoq/FG40BX180br4V9frqhovOmMaBIgG/HYH+9evc=; b=IYo9kM4alnFvEJD6egMzql07rUJ2t+gedX9J6P7/lMXWY8IqZfDp049+6wzFFs9zRm 8W+JEr3dwQ9BjKSjEQIwnyj0maEnFu34vVCpdpA/QdewbhXy7sz8U/VBmujTqU9fwx3J 6mHdxQ+kGxA0ll81MQW8N4QQyRVkqTA6MEHOIyxIaCBSv3iPXAaNwIHFQlB2iQa+yVi9 RVCO+BAe0YsqKU/Fe98lYFmWjtO86Wm5cYQhAPDUQkFnpa4kTQPpVmGCA2Nt/SfA2/RV j6ZFVSHUfJBEa7KjDD4fyolfE9pZXw9tT3LWpyINayEgFFNDdX44oljeJgrQWfbCmBPw msqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:subject:to:references:from:message-id :date:user-agent:mime-version:in-reply-to:content-language; bh=6Zqoq/FG40BX180br4V9frqhovOmMaBIgG/HYH+9evc=; b=LeyqnNg8/XUmLyPtYKhuDnY4bqDGLzLGEbH/gRi6/zjzbrnjqnASBMicGIqanhrj2Z l4LF01lUDVQHjuPIXxHp3qZD2r+2DqKmvIhtggw9dEot28Yo0BDOuoEkn7+BjTYgbLn2 /8vRGxNamxca3h6gHGSpE0MvFkkysECqUdMzs4dsc3fkaNn1NOADnVSjr1Zem5ZUbVIh gVN5yru4PnQJ/q0pVAh4Fp6aDUmGOObeC3FdpGNmmBFlO1TaDQvqWU56Ud5hj1/9Kj+p EJxHz8V+gqu+7dEpzPl7IziF3VAb7IrdbS2GmpqLTJnFcnE6T8MVZdqmHt+ZQmZPFCHG BuVA== X-Gm-Message-State: APjAAAUNS7sf2mbQkA1/UYhVcQBX8HQ1/SBxFkT3VKQiYnVaCch9hHad mqiKDBRK+itwuULVPQ9NL8nNmiK21x0= X-Google-Smtp-Source: APXvYqwbh1Z0EFRWe1SxT0W2t5d00tSYLS6oALMzG+yrO6SJ6I7SlRwVKXoq1QUaxDoLhv3WVNqphw== X-Received: by 2002:a5d:4e82:: with SMTP id e2mr1796689wru.199.1556844509058; Thu, 02 May 2019 17:48:29 -0700 (PDT) Original-Received: from [192.168.1.3] ([185.105.174.23]) by smtp.googlemail.com with ESMTPSA id e5sm490113wrh.79.2019.05.02.17.48.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 May 2019 17:48:28 -0700 (PDT) In-Reply-To: <86imv99982.fsf@stephe-leake.org> Content-Language: en-US X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::42a 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:236103 Archived-At: This is a multi-part message in MIME format. --------------55EDEE3876F9213595E62201 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit On 19.04.2019 20:49, Stephen Leake wrote: > I've pushed a scratch branch scratch/project-uniquify-files with a > different approach to this issue. > > I did some benchmarking, and realized that almost all of the time taken > by uniq-file-completion-table startup is spent scanning the disk for > file names, and filling the operating system disk cache. Doing all of > that first to get the file list, and then doing completion on the list, > is measureably slower than repeating the scan (using the cache) during > completion, but the difference is not enough to be noticeable (unless > the completion string eliminates most of the directories, which is not a > typical case for uniquify-files). I've seen similar results in my own optimization initiatives. Simple often beats being too clever. > Since standard completion works with alists, one way to make the result > string different from the display string is to build an alist with the > display string as key. I think this is the key insight. And we don't really need complex completion styles or special completion tables (which wouldn't work across project implementations). > Then we just need to fetch the result string from > the alist; completing-read does not do that, unfortunately (I guess > because the cdr of the alist might not be a string, in general). Calling cdr is not hard, though. An extra wrapper can do that. > Also, in browsing around the completion sources, I found the > 'quote/unquote' mechanism, which passes yet more actions to the > completion table function. > > So I added 'alist to the completion table metadata, and a step in > completing-read-default to query the completion table with the 'alist > action if that metadata is present. Like I said before, we don't want project implementations to provide extra features like this by overriding project-file-completion-table. It's not customizable and thus not user-friendly. > file-complete-root-relative and uniquify-files both use this mechanism; > they wrap an alist in a completion function that handles 'alist. All right. Let's try a different mechanism, though. > project--completing-read-strict delegates the UI to the completion > table, with the default using file-complete-root-relative. Let's not do UI inside completion tables. Or as little as possible, at least. Here is my counter-proposal (attached). It both nicely factors out the common-parent-directory logic (which had no place in completing-read-strict anyway) and creates a customization point. You can write a different project-find-file-read-fn that would render the file names differently. For instance, with the alist example, you could build it, call project--completing-read-strict on it, and cdr at the end. Or use a hash-table, etc. Let me know if you need any help with implementing that. --------------55EDEE3876F9213595E62201 Content-Type: text/x-patch; name="project-find-file-read-fn.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="project-find-file-read-fn.diff" diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 7c8ca15868..70c9f60a0d 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -157,19 +157,13 @@ project--find-in-directory vc-directory-exclusion-list) grep-find-ignored-files)) -(cl-defgeneric project-file-completion-table (project dirs) - "Return a completion table for files in directories DIRS in PROJECT. -DIRS is a list of absolute directories; it should be some -subset of the project roots and external roots. - -The default implementation delegates to `project-files'." - (let ((all-files (project-files project dirs))) - (lambda (string pred action) - (cond - ((eq action 'metadata) - '(metadata . ((category . project-file)))) - (t - (complete-with-action action all-files string pred)))))) +(defun project-file--completion-table (all-files) + (lambda (string pred action) + (cond + ((eq action 'metadata) + '(metadata . ((category . project-file)))) + (t + (complete-with-action action all-files string pred))))) (cl-defmethod project-roots ((project (head transient))) (list (cdr project))) @@ -470,19 +464,14 @@ project-or-external-find-file (project-external-roots pr)))) (project-find-file-in (thing-at-point 'filename) dirs pr))) -(defun project-find-file-in (filename dirs project) - "Complete FILENAME in DIRS in PROJECT and visit the result." - (let* ((table (project-file-completion-table project dirs)) - (file (project--completing-read-strict - "Find file" table nil nil - filename))) - (if (string= file "") - (user-error "You didn't specify the file") - (find-file file)))) +(defcustom project-find-file-read-fn #'project-find-file--read-cpd-relative + "Function to call to read a file name from a list. +For the arguments list, see project-find-file--read-cpd-relative." + :type 'function) -(defun project--completing-read-strict (prompt - collection &optional predicate - hist default inherit-input-method) +(defun project-find-file--read-cpd-relative (prompt + collection &optional predicate + hist default) ;; Tried both expanding the default before showing the prompt, and ;; removing it when it has no matches. Neither seems natural ;; enough. Removal is confusing; early expansion makes the prompt @@ -504,21 +493,43 @@ project--completing-read-strict ((eq action 'metadata) (if (functionp collection) (funcall collection nil nil 'metadata))) (t - (complete-with-action action substrings string pred))))) - (new-prompt (if default + (complete-with-action action substrings string pred))))) + (res (project--completing-read-strict prompt + new-collection predicate + hist default))) + (concat common-parent-directory res))) + +(defun project-find-file-in (filename dirs project) + "Complete FILENAME in DIRS in PROJECT and visit the result." + (let* ((all-files (project-files project dirs)) + (table (project-file--completion-table all-files)) + (file (funcall project-find-file-read-fn + "Find file" table nil nil + filename))) + (if (string= file "") + (user-error "You didn't specify the file") + (find-file file)))) + +(defun project--completing-read-strict (prompt + collection &optional predicate + hist default) + ;; Tried both expanding the default before showing the prompt, and + ;; removing it when it has no matches. Neither seems natural + ;; enough. Removal is confusing; early expansion makes the prompt + ;; too long. + (let* ((new-prompt (if default (format "%s (default %s): " prompt default) (format "%s: " prompt))) (res (completing-read new-prompt - new-collection predicate t + collection predicate t nil ;; initial-input - hist default inherit-input-method))) + hist default))) (when (and (equal res default) (not (test-completion res collection predicate))) (setq res (completing-read (format "%s: " prompt) - new-collection predicate t res hist nil - inherit-input-method))) - (concat common-parent-directory res))) + collection predicate t res hist nil))) + res)) (declare-function fileloop-continue "fileloop" ()) --------------55EDEE3876F9213595E62201--