From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Ergus Newsgroups: gmane.emacs.devel Subject: Re: feature/icomplete-vertical Date: Sat, 19 Sep 2020 13:43:59 +0200 Message-ID: <20200919114359.7kph6xt2gdmah2pp@Ergus> References: <20200919015957.prffuac2jke3hp6a@Ergus> <20200919061531.oyjlbdvkbeif5fsg@Ergus> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="rmslxlmar3kty44p" Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="26326"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Eli Zaretskii , casouri@gmail.com, emacs-devel@gnu.org To: Gregory Heytings Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Sep 19 13:44:53 2020 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1kJbIS-0006j2-Jz for ged-emacs-devel@m.gmane-mx.org; Sat, 19 Sep 2020 13:44:52 +0200 Original-Received: from localhost ([::1]:36666 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kJbIR-00076H-LK for ged-emacs-devel@m.gmane-mx.org; Sat, 19 Sep 2020 07:44:51 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:35736) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kJbHq-0006SI-Ff for emacs-devel@gnu.org; Sat, 19 Sep 2020 07:44:14 -0400 Original-Received: from sonic312-20.consmr.mail.bf2.yahoo.com ([74.6.128.82]:36807) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kJbHn-0001Zd-I0 for emacs-devel@gnu.org; Sat, 19 Sep 2020 07:44:13 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1600515849; bh=Gu5O/MH8drbZ0sHaPMbQsyH6YJSqr1ySGW9emNcF+Gk=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From:Subject; b=KUNcnMefnL5LcQFYp7VJPJtXuwSvvKlI8nYA7+Drgk3UjPzFohaeicBBUyPQevfHvw10YnbmSgqreOvYj0YZrUbm0T6dgThwIWVe1Ud4zP+o8GQiZv17gdB4lr+gmpmWodMAYzwoofsgl+6AKDHARZ59a5i/F4VIoFRm6vIKIxa5A42PWcIGsBU+DvYhUZOgLRzQGilmHWD/BAOC+2gpwkPRUY1tntEq+5nOWgzwxiWM/FVZyoUBL2ZIltoLDR5bxOW+nND9kNluEdzPBM1tSAHcxfUOs3XlQyU+JRmVww6hgSRWW/oj8Sy3uJx6Bmn0vLdeaE7rxPq087+NZX/Osg== X-YMail-OSG: YBA1v.8VM1nkfSpIYwJBC2eXrnPlzDFgq8N41kd1.XRM1cKJGOGLqhnVW1uohmT GZUQwA437zbPL.9E3dBXrXaA.cULL615l5YXABSMVlY29X3dfbRiCmUT7gjmZvgom9TMH3qb4BjL 3MVAo0T9RgRMhaKOBmdYOGFkbK3kY0qukhQh0oCtNqovsSc6T3DY8e7vY21j23rcn1gBA_xah1H5 GkiPq2tDz_5T9PeXILRL2WLtJhTortxh_XZ_N4S6EDTTwdjUgfiMA3noMYA53h4yH49J2r1M._6H lYTguNCVAgMmeUUTSoffvLPDolAnHjGuHfumL.HNfotmUfIxFN_..c6upknXZQPeMQ27JkwXSVrA yGpRn78SbeBRxXALiYnQ.qfSE4D.DFuBayET5oITI2ahFi4f8nrvMbzP4TkSlCaXPW.u12RAfeOF WzfWs_8Eg_yYeigYzW0pC.OPQxL8yKdr.aeb5DCm0LPvRKzG2SVhkgXVCelyNmVh6.jJxjDPiwW3 ABdLvRqgl9uJYHtqWNreLyu4f1fPi12yafWQ7OjauHvpuU0BDXz6nrEzUcD9jRZJI5QS_28IdIr7 fB.om1kZXaQZSKFXvdFtgngBrz3xhIHUYtZJ5O_fQbOZNuQzgeUUr98rInfdMOxOS0rRA.FA8yLu bXuyVTrccpjaZJqiq6EEq.2tMhtN51wjXIOHzIkyjF2Xwd0evQF8g6zLe4f8XKVvgEu9xm8SXoje 55aij8XaNf0PgDR599O9_jPKdMH4qmJZevXNoSItyg.V.GMJL20j2B5sRiLifFGw6roCONPYmBlM oCDpf3cX3ulFC0QO6ME9MrxC00hlxQkw62EABUO_c2 Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic312.consmr.mail.bf2.yahoo.com with HTTP; Sat, 19 Sep 2020 11:44:09 +0000 Original-Received: by smtp419.mail.ir2.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID 55d3ebba85d038acbf27d2c5889a7fd6; Sat, 19 Sep 2020 11:44:05 +0000 (UTC) Content-Disposition: inline In-Reply-To: X-Mailer: WebService/1.1.16583 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.aol Apache-HttpAsyncClient/4.1.4 (Java/11.0.7) Received-SPF: pass client-ip=74.6.128.82; envelope-from=spacibba@aol.com; helo=sonic312-20.consmr.mail.bf2.yahoo.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/09/19 07:44:09 X-ACL-Warn: Detected OS = Linux 3.11 and newer [fuzzy] X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.23 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-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:256160 Archived-At: --rmslxlmar3kty44p Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline Hi Gregory: Please look at the attached patch where I use a modified version of your line approach instead of pixels. Because I can't reproduce most of the issues you mention. This change is not permanent, but it is the only substantial difference with your and my code; so lets try that and if this fixes your issue I will check the pixels approach. Thanks in advance, Ergus On Sat, Sep 19, 2020 at 10:30:13AM +0000, Gregory Heytings wrote: > > >It seems to me that it does not work as well as my code, alas. A few >points: > >- with C-x C-f the default completion, between angle brackets in the >original icomplete code, is not shown anymore, unless there is a >single candidate (for example in a directory with "foo", "bar1", >"bar2", "f" will give "f[oo]" but "b" will give "b[]"); in other >words, the "[]" after the point usually serves no purpose > >- likewise, with M-x there is a "()" after the point that usually >serves no purpose > >- if I type "M-x icomplete-m", I see the following: > >M-x icomplete-m(ode) >| > >where "|" represents the cursor (on the next line!) > >- when the current line after the prompt is too long the prompt disappears > >- when completing a unique completion with TAB the point is moved to >the next line > >Gregory --rmslxlmar3kty44p Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename="icomplete-vertical.patch" Content-Transfer-Encoding: 8bit diff --git a/lisp/icomplete.el b/lisp/icomplete.el index 4e546807b7..6905883cad 100644 --- a/lisp/icomplete.el +++ b/lisp/icomplete.el @@ -50,6 +50,7 @@ ;;; Code: (require 'rfn-eshadow) ; rfn-eshadow-overlay +(require 'cl-lib) (defgroup icomplete nil "Show completions dynamically in minibuffer." @@ -57,7 +58,7 @@ icomplete :link '(info-link "(emacs)Icomplete") :group 'minibuffer) -(defcustom icomplete-separator " | " +(defcustom icomplete-separator nil "String used by Icomplete to separate alternatives in the minibuffer." :type 'string :version "24.4") @@ -68,6 +69,12 @@ icomplete-hide-common-prefix :type 'boolean :version "24.4") +(defcustom icomplete-format 'horizontal + "Enable `icomplete' vertical mode." + :type '(choice (const horizontal) + (const vertical)) + :version "28.1") + (defvar icomplete-tidy-shadowed-file-names nil "If non-nil, automatically delete superfluous parts of file names. For example, if the user types ~/ after a long path name, @@ -139,6 +146,22 @@ icomplete-minibuffer-setup-hook :group 'icomplete) +(defvar-local icomplete--ellipsis nil + "Ellipsis symbol to indicate continuation.") + +(defvar-local icomplete--separator nil + "If there are multiple possibilities this separates them.") + +(defvar-local icomplete--list-indicators-format nil + "Indicator for when multiple prospects are available. +means that further input is required to distinguish a single one") + +(defvar-local icomplete--last-format nil) +(defvar-local icomplete--prospects nil) +(defvar-local icomplete--match-braket nil) +(defvar-local icomplete--prospects-max-height nil) + + ;;;_* Initialization ;;;_ + Internal Variables @@ -437,6 +460,52 @@ icomplete-simple-completing-p (member table icomplete-with-completion-tables)))))) ;;;_ > icomplete-minibuffer-setup () + +(defun icomplete--vertical-prospects (prefix-len _determ comps) + "List of vertical completions limited." + ;; Max total rows to use, including the minibuffer content. + (let* (;; Needed for prospects-max-height-pixel + (prospects-rows (+ 1 (cl-count ?\n icomplete--list-indicators-format))) + limit prospects comp) + + (while (and comps (not limit)) + (setq comp (substring (pop comps) prefix-len) + prospects-rows (1+ prospects-rows)) + + (if (< prospects-rows icomplete--prospects-max-height) + (push comp prospects) + (push icomplete--ellipsis prospects) + (setq limit t))) + (nreverse prospects))) + + +(defun icomplete--horizontal-prospects (prefix-len determ comps) + "List of horizontal completions limited." + + (let* (;; Max total length to use, including the minibuffer content. + (separator-width (string-width icomplete--separator)) + (prospects-len (+ (string-width (or determ (format icomplete--match-braket ""))) + (string-width icomplete--separator) + (+ 2 (string-width icomplete--ellipsis)) ;; take {…} into account + (string-width (buffer-string)))) + (prospects-max-len (* (+ icomplete-prospects-height + ;; If the minibuffer content already uses up more than + ;; one line, increase the allowable space accordingly. + (/ prospects-len (window-width))) + (window-width))) + limit prospects comp) + + (while (and comps (not limit)) + (setq comp (substring (pop comps) prefix-len) + prospects-len (+ prospects-len (string-width comp) separator-width)) + + (if (< prospects-len prospects-max-len) + (push comp prospects) + (push icomplete--ellipsis prospects) + (setq limit t))) + (nreverse prospects))) + + (defun icomplete-minibuffer-setup () "Run in minibuffer on activation to establish incremental completion. Usually run by inclusion in `minibuffer-setup-hook'." @@ -446,7 +515,32 @@ icomplete-minibuffer-setup (current-local-map))) (add-hook 'pre-command-hook #'icomplete-pre-command-hook nil t) (add-hook 'post-command-hook #'icomplete-post-command-hook nil t) - (run-hooks 'icomplete-minibuffer-setup-hook))) + (run-hooks 'icomplete-minibuffer-setup-hook) + + (setq-local icomplete--ellipsis (if (char-displayable-p ?…) "…" "...")) + + (cond + ((eq icomplete-format 'vertical) + (setq-local icomplete--list-indicators-format " \n%s" + icomplete--separator (or icomplete-separator "\n") + icomplete--prospects 'icomplete--vertical-prospects + icomplete--prospects-max-height + (let ((minibuffer-parameter (frame-parameter nil 'minibuffer))) + (min (floor (cond + ((eq minibuffer-parameter t) + (if (floatp max-mini-window-height) + (* max-mini-window-height (frame-pixel-height)) + (* max-mini-window-height (frame-char-height)))) + ;; TODO : minibuffer-parameter can have other values. + ((eq minibuffer-parameter 'only) + (frame-pixel-height))) + (* (cl-count ?\n icomplete--separator) (line-pixel-height))) + (+ 2 icomplete-prospects-height))))) + ;; Default is horizontal + (t (setq-local icomplete--list-indicators-format "{%s}" + icomplete--separator (or icomplete-separator " | ") + icomplete--prospects 'icomplete--horizontal-prospects))))) + (defvar icomplete--in-region-buffer nil) @@ -639,8 +733,6 @@ icomplete-completions (...) - a single prospect is identified and matching is enforced, [...] - a single prospect is identified but matching is optional, or - {...} - multiple prospects, separated by commas, are indicated, and - further input is required to distinguish a single one. If there are multiple possibilities, `icomplete-separator' separates them. @@ -665,13 +757,13 @@ icomplete-completions (md (completion--field-metadata (icomplete--field-beg))) (comps (icomplete--sorted-completions)) (last (if (consp comps) (last comps))) - (base-size (cdr last)) - (open-bracket (if require-match "(" "[")) - (close-bracket (if require-match ")" "]"))) + (base-size (cdr last))) + + (setq-local icomplete--match-braket (if require-match "(%s)" "[%s]")) ;; `concat'/`mapconcat' is the slow part. (if (not (consp comps)) (progn ;;(debug (format "Candidates=%S field=%S" candidates name)) - (format " %sNo matches%s" open-bracket close-bracket)) + (format icomplete--match-braket "No matches")) (if last (setcdr last nil)) (let* ((most-try (if (and base-size (> base-size 0)) @@ -687,33 +779,18 @@ icomplete-completions ;; a prefix of most, or something else. (compare (compare-strings name nil nil most nil nil completion-ignore-case)) - (ellipsis (if (char-displayable-p ?…) "…" "...")) (determ (unless (or (eq t compare) (eq t most-try) (= (setq compare (1- (abs compare))) (length most))) - (concat open-bracket + (format icomplete--match-braket (cond ((= compare (length name)) ;; Typical case: name is a prefix. (substring most compare)) ;; Don't bother truncating if it doesn't gain ;; us at least 2 columns. - ((< compare (+ 2 (string-width ellipsis))) most) - (t (concat ellipsis (substring most compare)))) - close-bracket))) - ;;"-prospects" - more than one candidate - (prospects-len (+ (string-width - (or determ (concat open-bracket close-bracket))) - (string-width icomplete-separator) - (+ 2 (string-width ellipsis)) ;; take {…} into account - (string-width (buffer-string)))) - (prospects-max - ;; Max total length to use, including the minibuffer content. - (* (+ icomplete-prospects-height - ;; If the minibuffer content already uses up more than - ;; one line, increase the allowable space accordingly. - (/ prospects-len (window-width))) - (window-width))) + ((< compare (+ 2 (string-width icomplete--ellipsis))) most) + (t (concat icomplete--ellipsis (substring most compare))))))) ;; Find the common prefix among `comps'. ;; We can't use the optimization below because its assumptions ;; aren't always true, e.g. when completion-cycling (bug#10850): @@ -725,12 +802,13 @@ icomplete-completions (prefix (when icomplete-hide-common-prefix (try-completion "" comps))) (prefix-len - (and (stringp prefix) + (and icomplete-hide-common-prefix + (stringp prefix) ;; Only hide the prefix if the corresponding info ;; is already displayed via `most'. (string-prefix-p prefix most t) - (length prefix))) ;;) - prospects comp limit) + (length prefix))) + prospects) (if (or (eq most-try t) (not (consp (cdr comps)))) (setq prospects nil) (when (member name comps) @@ -751,20 +829,11 @@ icomplete-completions ;; To circumvent all the above problems, provide a visual ;; cue to the user via an "empty string" in the try ;; completion field. - (setq determ (concat open-bracket "" close-bracket))) + (setq determ (format icomplete--match-braket ""))) ;; Compute prospects for display. - (while (and comps (not limit)) - (setq comp - (if prefix-len (substring (car comps) prefix-len) (car comps)) - comps (cdr comps)) - (setq prospects-len - (+ (string-width comp) - (string-width icomplete-separator) - prospects-len)) - (if (< prospects-len prospects-max) - (push comp prospects) - (setq limit t)))) - (setq prospects (nreverse prospects)) + (setq prospects + (funcall icomplete--prospects prefix-len determ comps))) + ;; Decorate first of the prospects. (when prospects (let ((first (copy-sequence (pop prospects)))) @@ -776,10 +845,8 @@ icomplete-completions (if last (setcdr last base-size)) (if prospects (concat determ - "{" - (mapconcat 'identity prospects icomplete-separator) - (and limit (concat icomplete-separator ellipsis)) - "}") + (format icomplete--list-indicators-format + (mapconcat 'identity prospects icomplete--separator))) (concat determ " [Matched]")))))) ;;; Iswitchb compatibility --rmslxlmar3kty44p--