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 16:43:16 +0200 Message-ID: <20200919144316.bl6vwq33o6tzfecc@Ergus> References: <20200919015957.prffuac2jke3hp6a@Ergus> <20200919061531.oyjlbdvkbeif5fsg@Ergus> <20200919114359.7kph6xt2gdmah2pp@Ergus> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="ykztzjubasflyw2f" Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="9557"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Stefan Monnier , 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 16:44:17 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 1kJe63-0002Mg-RE for ged-emacs-devel@m.gmane-mx.org; Sat, 19 Sep 2020 16:44:15 +0200 Original-Received: from localhost ([::1]:49228 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kJe62-0004Zg-TU for ged-emacs-devel@m.gmane-mx.org; Sat, 19 Sep 2020 10:44:14 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:36352) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kJe5K-0003wz-89 for emacs-devel@gnu.org; Sat, 19 Sep 2020 10:43:31 -0400 Original-Received: from sonic310-14.consmr.mail.bf2.yahoo.com ([74.6.135.124]:42115) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kJe5G-0004lM-RE for emacs-devel@gnu.org; Sat, 19 Sep 2020 10:43:29 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1600526604; bh=jWlM3w2jUDo4AtjlgP7+/+whgCkhIP5P5lZt9CpaZzA=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From:Subject; b=MA5qX4MZ57OafPKean363MwTChoYnkPqslwYUawvt3yckndcwrQnjktmiNov8YoYO6dJxtRcDo5iwwrVG9Tr06twNhL3XwELWGIxM0rGedgXjQn7TPhkXYR/kRhpBfG/uHfzCI/ZPuUividWD2NFpmLD19jub++5I+6dnfdQW2gjWdoA9ibYVw/weyF411nj0ra/3kotoTEhzU/JQordID2w9r/+vXldE1fWwOoBd73Cif38a+m3nP87FsSuQy0h1Gnx+3VHpD8/w7LiDaWue8vQiyvkorqXiLCZmltRKYPAu/gn9Nsg0cfnVV+7zslje74wtt5ppEbco0ml5c64Sw== X-YMail-OSG: avgamgAVM1kVdE1Bq7oY_PjpCLpJ1U9TM_9..Jm041TiOTIRqTFTDn5xAtEM7Uk jhEqL4F96L5APQR8AdV.OQiE9F6mtPxCXIgmf5O8YZ1.KoKf7hwBe5csokNMdGbEHcQ0U.pzfCCV 1ASDJ312NsbtgI8XWmTNGpEs5_3ve_hgZvCkoxU4LjD1ILx71F.dYoSVPA7WpQ5D9mVMdmkxjMP4 ta4y_99cfm6frb6.cssmn4QZ5GnpQjIi_2xG.au_eG8Gjp.bP1YX2bMO6P.afDFcjJsGiU5ctoTT yMa38D6VBT_PfaHbvbDEwFIEcjSeOFIO42celMznyhEPxVaczfy2k4zmQUjc5AqbBlkgSnyfhi13 YDNOcxcn3NVXjJhM0tnR0eaiCQE6CUshRUpW_XT9ovo0NvpEFjjeuzP7Z67yyqxP8aaPkkD6.IWO BvybJRoMR5qs1ZQ.b53iiiBWujELOc9wBH1Jo7amqWU8iCOrgRtl3nXa9AAje_r2i5bp2mSZBAoS R1sIKCE1rfiGM0GUwOJdEuOvf_43CUr8cPegPFJS31s_z78dO31vRjtm4tZTuD83cDYbE4X7Ytm2 y.ymSAgvGfwSoBFIBvc6ShLA1KmFde4iA7ANuya7h_9_LJgM9z80jucb4OpClU99ESSkmOukQUMR 5W48W4MWJIW2I.Lsj3H7jUL.kjo6PaWVjCstjNiJu5.XRrtm0owIKjTPe_z0B9HLvCFlvYWFclgG U78l2uEdbAfBb4XwIssDDfd71P_wihnVr5TldRlBzzKcU4N7gsGtOFN6gYXQAJYkmgIR0m35oldv chD54kykLSRh5ivYrSbMhUGlCiBcMIP7kfx19DmN3T Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic310.consmr.mail.bf2.yahoo.com with HTTP; Sat, 19 Sep 2020 14:43:24 +0000 Original-Received: by smtp406.mail.ir2.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID 375b07a2291c236f3c29976e7170cf97; Sat, 19 Sep 2020 14:43:22 +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.135.124; envelope-from=spacibba@aol.com; helo=sonic310-14.consmr.mail.bf2.yahoo.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/09/19 10:43:25 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=unavailable 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:256175 Archived-At: --ykztzjubasflyw2f Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline On Sat, Sep 19, 2020 at 01:59:18PM +0000, Gregory Heytings wrote: Hi Stefan and Gregory: Here it is so far my last patch about vertical only feature. Please feel free to correct it. I removed the format variable and now to get vertical we only need to add at least a \n to the separator. With the simple cases I tested it works. Stefan: As you requested for a single feature I will remove the remote branch again and push one only with this commit. Gregory: I took the freedom to add one condition from your code and added it to mine. (The one for empty candidates) I didn't do this before because I was keeping the first candidate in the same line so it made sense to keep the empty one. So after all I will condition this condition in a future too; when I add options to remove/change the [] and (). The other one (prospects-max (- icomplete--prospects-available-height (/ (point) (window-width)))) I think is good for continuation lines, but I am not sure if that's better than truncate line and horizontally scroll because in that case the proposed candidates will be very long as well and continuing them will reduce their number to the half too. (I took ivy as a pattern for this) So I will wait for other users' suggestions about what they prefer OR take this condition conditionally in case the user explicitly disables the lines truncation. Best, Ergus --ykztzjubasflyw2f 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..c071691b07 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." @@ -139,6 +140,19 @@ icomplete-minibuffer-setup-hook :group 'icomplete) +(defvar-local icomplete--ellipsis nil + "Ellipsis symbol to indicate continuation.") + +(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--prospects nil) +(defvar-local icomplete--match-braket nil) +(defvar-local icomplete--prospects-max-height nil) +(defvar-local icomplete--lines-per-candidate 0) + + ;;;_* Initialization ;;;_ + Internal Variables @@ -437,6 +451,62 @@ 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 + (single-line-height (line-pixel-height)) + ;; in general this should be done for every line + (line-height (* icomplete--lines-per-candidate single-line-height)) + (prospects-rows (* (1+ (cl-count ?\n icomplete--list-indicators-format)) + single-line-height)) + limit prospects comp comp-len) + + (while (and comps (not limit)) + (setq comp (substring (pop comps) prefix-len) + comp-len (> (length comp) 0)) + + (if comp-len + (setq prospects-rows (+ prospects-rows line-height))) + + (if (< prospects-rows icomplete--prospects-max-height) + (if comp-len + (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)) + + (when (> (length comp) 0) + (setq 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 +516,29 @@ 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 icomplete--ellipsis (if (char-displayable-p ?…) "…" "...") + icomplete--lines-per-candidate (cl-count ?\n icomplete-separator)) + + (if (> icomplete--lines-per-candidate 0) + (setq-local icomplete--list-indicators-format " \n%s" + icomplete--prospects 'icomplete--vertical-prospects + icomplete--prospects-max-height + (let ((minibuffer-parameter (frame-parameter nil 'minibuffer))) + (min (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))) + (* (+ 2 icomplete-prospects-height) (line-pixel-height))))) + + (setq-local icomplete--list-indicators-format "{%s}" + icomplete--prospects 'icomplete--horizontal-prospects)))) + (defvar icomplete--in-region-buffer nil) @@ -639,8 +731,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 +755,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 +777,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 +800,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 +827,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 +843,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 --ykztzjubasflyw2f--