From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Ergus via "Emacs development discussions." Newsgroups: gmane.emacs.devel Subject: Re: vertical fido-mode (new branch) Date: Mon, 24 Aug 2020 21:06:54 +0200 Message-ID: <20200824190654.d5obxfwyehdgpr4u@Ergus> References: <13ec44ed-4b54-8d43-590f-709bd813fd01@yandex.ru> <795146083.1708851.1591826041689@mail.yahoo.com> <87y2ouldrr.fsf@mail.linkov.net> <20200819121755.24hgq4gyba2wkt76@Ergus> <87pn7mdqbe.fsf@mail.linkov.net> <20200820103746.6n5op7dtz563k7qm@Ergus> <87pn7llbnb.fsf@mail.linkov.net> <20200821000513.xqdhsdwxm7ov7fkw@Ergus> <87y2m5jlh6.fsf@mail.linkov.net> Reply-To: Ergus Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="diwebvuwzo34zy67" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="11000"; mail-complaints-to="usenet@ciao.gmane.io" Cc: "emacs-devel@gnu.org" To: Juri Linkov Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Mon Aug 24 21:07:58 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 1kAHp0-0002jg-1M for ged-emacs-devel@m.gmane-mx.org; Mon, 24 Aug 2020 21:07:58 +0200 Original-Received: from localhost ([::1]:55194 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kAHoy-0001ny-Sq for ged-emacs-devel@m.gmane-mx.org; Mon, 24 Aug 2020 15:07:56 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:40936) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1kAHoR-0001MS-ET for emacs-devel@gnu.org; Mon, 24 Aug 2020 15:07:23 -0400 Original-Received: from sonic317-26.consmr.mail.bf2.yahoo.com ([74.6.129.81]:44119) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1kAHoN-0004pY-5N for emacs-devel@gnu.org; Mon, 24 Aug 2020 15:07:22 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aol.com; s=a2048; t=1598296034; bh=q0AF8aHvnva9MiXuK8uaoJ778LPncRg8azmPq5KVv6c=; h=Date:From:To:Cc:Subject:References:In-Reply-To:From:Subject; b=Zq4t42H7KooShXoOEKm/R55Plw3w0+pa0qMqqOZZgaWCK0nw/mz+qt228iId3sEQFSMwu79IJfgkIPevc01fmhnQtmNGxKYIToaaLRaZJG22DFLcWqm/9TrReHYiKeJ/vvAYAEDwb9SpP6OTerYs50JRhZvsIVjkRsY1XrrZdSnHLzWvEnmQwyCyzQW683JOmIM1NZOMlwy5i7vhTaIv5PTjEPQ9qtnFGhwEXuIi7a9bgZTJjsucj3S2w3m+aoAmDiNoN4kc0HrzmC5jZMM6Wq2REUBGdwh8vMEAA3Y7dZwLwecQpoK6TkuxGGkCvz3MBK2AG3YcBBF+AO2dj3CfIA== X-YMail-OSG: 6O7yBBYVM1mj2gqwwQkjYtT4bL9AxPuojKR.FD6IvFINlnRuVTtodWHocKgzDbl ubOcQEUoVt6Gx_H9o7lA5k4v8K4.aZ1lJu2t1hN_gv.vy6mAp9dzqVuxmogxj4P4SwI30adzlRZQ LxMthI6sMGQb0g3p30I9pdLpJD2oz_b1t807DBOtsWwKV5UBggjIPfocPk0gbdOjbstEAoqzG3YW YP.tkGGHs9lyHwR2u146zKDG7FaoFMhm9CgiQeqCaAkhhDFHqOrlcPzZhrpbDhr4C4zy0j4yyrg2 g1SS5e_zB8FPRaadj7c4slvxVKaoPzQVWXSk9976_ap3tppMHq.FqfM8f9HpgdyWH0o8.wFNv43k Z63mlLNzE8iv72LP2tC5AHms42np.ACqp7BORT8.peJ2W4mFkqlZ40uHufaX9MpIOWBCDvZLPQ0y mfEkGmJFqHI_eWJQc5cXJ0Saf..10ijKh2alzNdo1we_l9IBDTKzu3nlWdhTHAMH6Rc10zcnXp.h xNStnFgnhHHKkgsGhTdO3DCr3DC.s_CNvjRtnUo_BN7wzSf6O3oxU5vI4nmyzqDBu.4F.9_Mg52b ORxKcZwSWtEJSyDW6Pz_q1JVu6ViEwo3Y0nf18KbftFfZrUg.nIlcT8XkHNgdYchpagjo96NMqhv 4Fk_pC.eJB0fuRNSfnrfBLwD2fzycGoHnTR3qrEwCVwQJ5SlK5GPbGvmiJJKpUAXOXH.GwdDMFkU MtdnftXBYcrK_U_DhOrZ7CO7pUiC9OQGyZDtTsHhGDtnKMtrtAJs4oRutm0uuXd5QBK5ZtaS7_nP YeiRnIqF7BXTtwU55LWqB8BGlsKvzKXtIe2kOO.Za2 Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic317.consmr.mail.bf2.yahoo.com with HTTP; Mon, 24 Aug 2020 19:07:14 +0000 Original-Received: by smtp423.mail.ir2.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID e0cbc607ff1dd583415710d143ac69da; Mon, 24 Aug 2020 19:07:06 +0000 (UTC) Content-Disposition: inline In-Reply-To: <87y2m5jlh6.fsf@mail.linkov.net> X-Mailer: WebService/1.1.16455 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.129.81; envelope-from=spacibba@aol.com; helo=sonic317-26.consmr.mail.bf2.yahoo.com X-detected-operating-system: by eggs.gnu.org: First seen = 2020/08/24 15:07:14 X-ACL-Warn: Detected OS = Linux 3.11 and newer [fuzzy] X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=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=no 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:254204 Archived-At: --diwebvuwzo34zy67 Content-Type: text/plain; charset=us-ascii; format=flowed Content-Disposition: inline Hi: Could you please try the attached patch (where the mode is enabled by default). On Sun, Aug 23, 2020 at 09:45:17PM +0300, Juri Linkov wrote: > >Easier can be only one thing: to use arrows and navigation keys to navigate >completions from the minibuffer. So the main question is on what condition >to activate these keys (instead of allowing them to search in history)? > I enabled the keys only when the *Completions* buffer is shown and the highlight completions is active. And added a hook to remove the bindings when minibuffer-hide-completions. >Since making TAB more DWIM doesn't work, what about the following solution: >activate completions navigation keys and display the completions buffer >only when there is some input in the minibuffer, i.e. when the minibuffer's >content is different from its default value. > Please try this closer to zsh experience implementation. >Or similar to this https://api.jqueryui.com/autocomplete/#option-minLength >activate completions only when input is longer than the minimum number >of characters. > I will give a look. If this patch is too much code for adding in simple and minibuffer, I would try to make a separate file with a mode. WDYT? Best, Ergus --diwebvuwzo34zy67 Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="completion-highlight.patch" diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 641a2e5315..cdc1e18708 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -753,6 +753,12 @@ minibuffer-message-clear-timeout (integer :tag "Wait for the number of seconds" 2)) :version "27.1") +(defcustom minibuffer-tab-go-completion t + "If a second `TAB' jump to completion buffer." + :type 'boolean + :version "28.1" + :group 'completion) + (defvar minibuffer-message-timer nil) (defvar minibuffer-message-overlay nil) @@ -940,6 +946,8 @@ completion-styles :type completion--styles-type :version "23.1") + + (defvar completion-category-defaults '((buffer (styles . (basic substring))) (unicode-name (styles . (basic substring))) @@ -1272,6 +1280,122 @@ minibuffer-complete minibuffer-completion-table minibuffer-completion-predicate))) +(defmacro with-minibuffer-scroll-window (&rest body) + "Execute BODY in *Completions* buffer and return to `minibuffer'. +The command is only executed if the `minibuffer-scroll-window' is +alive and active." + `(and (window-live-p minibuffer-scroll-window) + (eq t (frame-visible-p (window-frame minibuffer-scroll-window))) + (with-selected-window minibuffer-scroll-window + (with-current-buffer (window-buffer minibuffer-scroll-window) + ,@body)))) + +(defun minibuffer-next-completion (n) + "Execute `next-completion' in *Completions*. +The argument N is passed directly to `next-completion', the +command is executed in another window, but cursor stays in +minibuffer." + (interactive "p") + (with-minibuffer-scroll-window (next-completion n))) + +(defun minibuffer-previous-completion (n) + "Execute `previous-completion' in *Completions*. +The argument N is passed directly to `previous-completion', the +command is executed in another window, but cursor stays in +minibuffer." + (interactive "p") + (with-minibuffer-scroll-window (previous-completion n))) + +(defun minibuffer-next-line-completion (n) + "Execute `next-line-completion' in *Completions*. +The argument N is passed directly to `next-line-completion', the +command is executed in another window, but cursor stays in +minibuffer." + (interactive "p") + (with-minibuffer-scroll-window (next-line-completion n))) + +(defun minibuffer-previous-line-completion (n) + "Execute `previous-line-completion' in *Completions*. +The argument N is passed directly to `previous-line-completion', +the command is executed in another window, but cursor stays in +minibuffer." + (interactive "p") + (with-minibuffer-scroll-window (previous-line-completion n))) + +(defun minibuffer-completion-set-suffix (choice) + "Set CHOICE suffix to current completion. +It uses `completion-base-position' to determine the cursor position" + (let* ((base-position (or completion-base-position + (list (minibuffer-prompt-end) + (choose-completion-guess-base-position choice)))) + (cursor-pos (cadr base-position)) + (prefix-len (- cursor-pos + (car base-position))) + (minibuffer-window (active-minibuffer-window)) + (minibuffer-buffer (window-buffer minibuffer-window)) + (completion-no-auto-exit t) + (suffix (if (< prefix-len (length choice)) + (substring choice prefix-len) + "")) + (suffix-len (string-width suffix))) + + (with-selected-window minibuffer-window + (with-current-buffer minibuffer-buffer + + (choose-completion-string suffix minibuffer-buffer + (list cursor-pos (point-max))) + (add-face-text-property cursor-pos (+ cursor-pos suffix-len) 'shadow) + (goto-char cursor-pos))))) + +(defun minibuffer-completion-unset-suffix () + "Remove suffix to current completion. +It uses `completion-base-position' to determine the cursor position" + (minibuffer-completion-set-suffix "")) + +(defmacro completions-highlight-minibufer-bindings (set) + "Add extra/remove keybindings to `minibuffer-local-must-match-map'." + `(progn + (define-key minibuffer-local-must-match-map [right] ,(and set ''minibuffer-next-completion)) + (define-key minibuffer-local-must-match-map [left] ,(and set ''minibuffer-previous-completion)) + (define-key minibuffer-local-must-match-map [down] ,(and set ''minibuffer-next-line-completion)) + (define-key minibuffer-local-must-match-map [up] ,(and set ''minibuffer-previous-line-completion)))) + +(defun completions-highlight-unset-minibuffer-bindings () + "Remove extra keybindings from `minibuffer-local-must-match-map'." + (completions-highlight-minibufer-bindings nil)) + +(defmacro completions-highlight-completion-bindings (set) + "Add extra keybindings to `completion-list-mode-map'." + `(progn + (define-key completion-list-mode-map "\C-g" ,(and set ''quit-window)) + (define-key completion-list-mode-map [up] ,(and set ''previous-line-completion)) + (define-key completion-list-mode-map "\C-p" ,(and set ''previous-line-completion)) + (define-key completion-list-mode-map [down] ,(and set ''next-line-completion)) + (define-key completion-list-mode-map "\C-n" ,(and set ''next-line-completion)))) + +(defun completions-highlight-unset-completion-bindings () + "Remove extra keybindings from `completion-list-mode-map'." + (completions-highlight-completion-bindings nil)) + +(defun completions-highlight-minibuffer-complete-setup () + "Add extra functionalities for minibuffer when completions are enabled. +This is called from `completion-setup-function'" + (when (and completion-highlight-candidate + (minibufferp)) + (add-hook 'pre-command-hook + (lambda () + ;; TODO: probably we need an alist here + ;; (message "Precommand %s" (current-local-map)) + (unless (eq this-command 'minibuffer-complete-and-exit) + (minibuffer-completion-unset-suffix)) + ) + nil t) + (add-hook 'minibuffer-hide-completions-hook + #'completions-highlight-unset-minibuffer-bindings) + + (completions-highlight-minibufer-bindings t) + (completions-highlight-completion-bindings t))) + (defun completion--in-region-1 (beg end) ;; If the previous command was not this, ;; mark the completion buffer obsolete. @@ -1288,8 +1412,12 @@ completion--in-region-1 (let ((window minibuffer-scroll-window)) (with-current-buffer (window-buffer window) (if (pos-visible-in-window-p (point-max) window) - ;; If end is in view, scroll up to the beginning. - (set-window-start window (point-min) nil) + (if (and minibuffer-tab-go-completion + (pos-visible-in-window-p (point-min) window)) + (minibuffer-next-completion 1) + ;; If all completions are visible use tab completion + ;; If end is in view, scroll up to the beginning. + (set-window-start window (point-min) nil)) ;; Else scroll down one screen. (with-selected-window window (scroll-up))) @@ -1776,6 +1904,12 @@ completion-setup-hook The completion list buffer is available as the value of `standard-output'. See also `display-completion-list'.") +(defvar minibuffer-hide-completions-hook nil + "Normal hook run at the end of completion-hide-completions. +The hook is called from the minibuffer after hide completions. +When this hook is run, the current buffer is the minibuffer and +the *Completions* buffer is already hidden.") + (defface completions-first-difference '((t (:inherit bold))) "Face for the first character after point in completions. @@ -2040,7 +2174,6 @@ minibuffer-completion-help (completion--done result (if (eq (car bounds) (length result)) 'exact 'finished))))))) - (display-completion-list completions))))) nil))) nil)) @@ -2050,7 +2183,9 @@ minibuffer-hide-completions ;; FIXME: We could/should use minibuffer-scroll-window here, but it ;; can also point to the minibuffer-parent-window, so it's a bit tricky. (let ((win (get-buffer-window "*Completions*" 0))) - (if win (with-selected-window win (bury-buffer))))) + (when win + (with-selected-window win (bury-buffer)) + (run-hooks 'minibuffer-hide-completions-hook)))) (defun exit-minibuffer () "Terminate this minibuffer argument." @@ -2318,6 +2453,7 @@ completion-help-at-point (setq completion-in-region--data `(,start ,(copy-marker end t) ,collection ,(plist-get plist :predicate))) + (completion-in-region-mode 1) (minibuffer-completion-help start end))) (`(,hookfun . ,_) @@ -3754,7 +3890,7 @@ completing-read-default require-match)) (minibuffer--require-match require-match) (base-keymap (if require-match - minibuffer-local-must-match-map + minibuffer-local-must-match-map minibuffer-local-completion-map)) (keymap (if (memq minibuffer-completing-file-name '(nil lambda)) base-keymap diff --git a/lisp/simple.el b/lisp/simple.el index fa6e154004..27dc87217b 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -8368,6 +8368,12 @@ set-variable ;; Define the major mode for lists of completions. +(defcustom completion-highlight-candidate t + "Non-nil means show help message in *Completions* buffer." + :type 'boolean + :version "28.1" + :group 'completion) + (defvar completion-list-mode-map (let ((map (make-sparse-keymap))) (define-key map [mouse-2] 'choose-completion) @@ -8381,6 +8387,12 @@ completion-list-mode-map (define-key map [backtab] 'previous-completion) (define-key map "q" 'quit-window) (define-key map "z" 'kill-current-buffer) + + (define-key map "\C-g" 'quit-window) + (define-key map [up] 'previous-line-completion) + (define-key map "\C-p" 'previous-line-completion) + (define-key map [down] 'next-line-completion) + (define-key map "\C-n" 'next-line-completion) map) "Local map for completion list buffers.") @@ -8419,6 +8431,10 @@ completion-base-size If nil, Emacs determines which part of the tail end of the buffer's text is involved in completion by comparing the text directly.") + +(defvar completion-overlay nil + "Highlight to use when `completion-highlight-candidate' is non nil.") + (make-obsolete-variable 'completion-base-size 'completion-base-position "23.2") (defun delete-completion-window () @@ -8432,15 +8448,9 @@ delete-completion-window (if (get-buffer-window buf) (select-window (get-buffer-window buf)))))) -(defun previous-completion (n) - "Move to the previous item in the completion list." - (interactive "p") - (next-completion (- n))) - -(defun next-completion (n) +(defun goto-next-completion (n) "Move to the next item in the completion list. With prefix argument N, move N items (negative N means move backward)." - (interactive "p") (let ((beg (point-min)) (end (point-max))) (while (and (> n 0) (not (eobp))) ;; If in a completion, move to the end of it. @@ -8465,6 +8475,46 @@ next-completion (point) 'mouse-face nil beg)) (setq n (1+ n)))))) +(defun next-completion (n) + "Move to the next item in the completion list. +With prefix argument N, move N items (negative N means move backward). +If completion highlight is enabled, highlights the selected candidate. +Returns the completion string if available." + (interactive "p") + (goto-next-completion n) + + (let* ((obeg (point)) + (oend (next-single-property-change obeg 'mouse-face nil (point-max))) + (choice (buffer-substring-no-properties obeg oend))) + + (when completion-highlight-candidate + (move-overlay completion-overlay obeg oend) + (minibuffer-completion-set-suffix choice)) + + ;; Return the current completion + choice)) + +(defun previous-completion (n) + "Move to the previous N item in the completion list see `next-completion'." + (interactive "p") + (next-completion (- n))) + +(defun next-line-completion (&optional arg try-vscroll) + "Go to completion candidate in line above current. +With prefix argument ARG, move to ARG candidate bellow current. +TRY-VSCROLL is passed straight to `line-move'" + (interactive "^p\np") + (line-move arg t nil try-vscroll) + (goto-next-completion 1) + (next-completion -1)) + +(defun previous-line-completion (&optional arg try-vscroll) + "Go to completion candidate in line above current. +With prefix argument ARG, move to ARG candidate above current. +TRY-VSCROLL is passed straight to `line-move'" + (interactive "^p\np") + (next-line-completion (- arg) try-vscroll)) + (defun choose-completion (&optional event) "Choose the completion at point. If EVENT, use EVENT's position to determine the starting position." @@ -8646,6 +8696,12 @@ completion-show-help :version "22.1" :group 'completion) + +(defun completions-highlight-completions-pre-command-hook () + "Function `pre-command-hook' to use only in the minibuffer." + (move-overlay completion-overlay 0 0) + (minibuffer-completion-unset-suffix)) + ;; This function goes in completion-setup-hook, so that it is called ;; after the text of the completion list buffer is written. (defun completion-setup-function () @@ -8684,7 +8740,22 @@ completion-setup-function (insert "Click on a completion to select it.\n")) (insert (substitute-command-keys "In this buffer, type \\[choose-completion] to \ -select the completion near point.\n\n")))))) +select the completion near point.\n\n"))) + + (when (and completion-highlight-candidate + (string= (buffer-name) "*Completions*")) + + (set (make-local-variable 'completion-overlay) (make-overlay 0 0)) + (overlay-put completion-overlay 'face 'highlight) + + (add-hook 'pre-command-hook #'completions-highlight-completions-pre-command-hook nil t) + (add-hook 'isearch-mode-end-hook (lambda () + (goto-next-completion -1) + (next-completion 1)) nil t) + (completions-highlight-completion-bindings t))) + + (completions-highlight-minibuffer-complete-setup))) + (add-hook 'completion-setup-hook #'completion-setup-function) --diwebvuwzo34zy67--