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: Sun, 20 Sep 2020 17:04:54 +0200 Message-ID: <20200920150454.62emnrrzu6uuquc3@Ergus> References: <20200912133311.6ujtgczj6wyclufy@Ergus> <20200920130435.heye7bk73pm252km@Ergus> <83sgbczj0i.fsf@gnu.org> <83lfh4zfml.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="u23ne664pnedvkjh" Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="21021"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Gregory Heytings , casouri@gmail.com, joaotavora@gmail.com, emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Sep 20 17:06:20 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 1kK0ux-0005Ly-NK for ged-emacs-devel@m.gmane-mx.org; Sun, 20 Sep 2020 17:06:19 +0200 Original-Received: from localhost ([::1]:57470 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kK0uw-0007ZJ-PH for ged-emacs-devel@m.gmane-mx.org; Sun, 20 Sep 2020 11:06:18 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:50294) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kK0tn-00071R-Tt for emacs-devel@gnu.org; Sun, 20 Sep 2020 11:05:08 -0400 Original-Received: from sonic308-2.consmr.mail.bf2.yahoo.com ([74.6.130.41]:40509) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kK0tj-0006Pu-W1 for emacs-devel@gnu.org; Sun, 20 Sep 2020 11:05:07 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1600614302; bh=NB2wIkvOV+2O+ESHJBMvlJvy058pM0vt0Cjhztn2BMs=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From:Subject; b=pWMoLuel+WTV4DCW9Mayd45Br9QMGO+nWcxk73phNtnDH/ArrTb4LBrrz6N1/XRtbV65FV69ZRuobK5AXyIhiNMRZo+T+w+FsAqwNMSgMy2XAK/izkCeB9gyPstKUvj0F4Rtnt+3WqCZk411N2CLJkCX8ArrgbZ9QUPHrHMyl7iwX0XRhIJbzQywcE9YDo72sxpRxloi5YAmx9CYHSbAOgSs6k7xkTDSt0SdUQMMReptkxraiekAm1CDnd4PeHlgzYjrIDlYH8rprUWUk1OyhgXgmkdjgucZQHMGkhL3hrb7jPVYKZ3gJ3ERi/1KSkKMwV2VsVwYOdcGoSO0VkkDSA== X-YMail-OSG: Gk7ZebsVM1lXu1B83ONals6QBt6q2WZ0V1DHeXQ7wVC.cvOqhuHYhmnuIr6ZWtw lnkUD34LaNGwuWsc9yVoqCVXb0txzuYUarOhHcYAr0y5U9CGsDlhHJ5JMazZKQCzpeGFkI1uVZET ecjojSMXfEqpY760pHL4bHfrFMf6Tq_5UdkW_jLs9Rhc1o9KX1pbdTR2Xlfd7g3I9HLqLIoPtVA3 mn7XPv2irEkd1402E1NlLDyRbxmIyf.4NoxIrktX1H_HcRpTnqfc.UIMSTXrhdU5Qo9URU6j8Oh2 YTQ5WEdF0RSlmeDhK71IIa8zfy4eQlR7dQqL4UCECwBY58CI4FSyXQbLfgw7gbTHK5waBfTG.l0E EQLGjbxinSuvyZfkDwI1NSp7Z6GcxYVytoN15sQghCTt55Oy8q4vLsngM5dyHKjZazaT6Bwi1jDl JVEp0px.ZZb6S2gyhvRytxXuM0Kw71MEJsGgud.tBycGPpTmnNkqHz8cGc7dyCE3gkwPe8PqpWVD LH5VcStGKs46kU_djpqvADWE7Bpnb1EA1Iykig4CAAYlsBFGo1r_wq5LE7ClTacR0e5QhgeZGDpo BWFOWY0dj2Eqp_BlCkfqHpcnl1bnVcLSBzKccertcgc1DDgF1bvLOxkjyVwqg1xnXS3cP__HYHvV OLQDVQdwhFtfD6UHNqjMHGgNNUOo97zLsBPD2jrW4MLXCEFmA2sTvb9CrwvzRb0dy_QQYlOtToXB b0lnwpgaL7RZ79vU1P9x1OzY7_aJAG3Sr62liQuJamHP.Z25YkQW_qXe4G9oO.tl.tui00s7vUwc xsl2fpAkDkKyDYnh2Fa4PTbkVGe6ueaYND_Q1F4IWA Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic308.consmr.mail.bf2.yahoo.com with HTTP; Sun, 20 Sep 2020 15:05:02 +0000 Original-Received: by smtp423.mail.ir2.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID 6e35f30c4740fafdafc95309196ecfe1; Sun, 20 Sep 2020 15:05:01 +0000 (UTC) Content-Disposition: inline In-Reply-To: <83lfh4zfml.fsf@gnu.org> 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.130.41; envelope-from=spacibba@aol.com; helo=sonic308-2.consmr.mail.bf2.yahoo.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/09/20 11:05:02 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:256256 Archived-At: --u23ne664pnedvkjh Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline On Sun, Sep 20, 2020 at 05:28:50PM +0300, Eli Zaretskii wrote: > >Please note that I wasn't replying to your proposal, I was replying to >Jimmy's. > >> trying to calculate the height of the completion candidate list, >> which amount (as Stefan wrote) to redoing what redisplay does. > >I don't agree with Stefan, if this interpretation of what he said is >correct. We have window-text-pixel-size to measure the size of text >on display without redoing what redisplay does. Hi Eli that's what I have been doing so far, but the approach looks a bit too complex. It is actually not, but I understand that a better simpler one is desirable. You can look at the attached patch. Any way it seems to me a bit buggy that (only) the prompt disappears in this situation and how it behaves when we use the arrows. In general without this issue we should be allowed to set \n separator and it basically should work out of the box without requiring my code at all. Best, Ergus --u23ne664pnedvkjh Content-Type: text/plain; charset=utf-8 Content-Disposition: attachment; filename="icomplete-vertical-Eli.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 --u23ne664pnedvkjh--