forked from Github/emacs
Compare commits
28 commits
master
...
feature/co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7c47e78e6 | ||
|
|
5dd563f053 | ||
|
|
60a1003aee | ||
|
|
25f23b1910 | ||
|
|
c990a28965 | ||
|
|
6369e59576 | ||
|
|
3319dcc33f | ||
|
|
bfb8a712dd | ||
|
|
6ea2c5c980 | ||
|
|
10359ba98a | ||
|
|
3f0f13e823 | ||
|
|
ce8b5b2663 | ||
|
|
268170e7a9 | ||
|
|
ab1e2e6b20 | ||
|
|
f19039c2f9 | ||
|
|
11431bc5f6 | ||
|
|
31d6ec4112 | ||
|
|
5cf9dd62c5 | ||
|
|
e143246df8 | ||
|
|
c6943d75eb | ||
|
|
644d5e5ffc | ||
|
|
6cea8936b1 | ||
|
|
42f47e7568 | ||
|
|
3a22b70ef5 | ||
|
|
d4d9f6a0ad | ||
|
|
d015144058 | ||
|
|
5509afa6e9 | ||
|
|
b1a027de35 |
5 changed files with 261 additions and 10 deletions
|
|
@ -182,6 +182,9 @@ The command loop runs this soon after @code{post-command-hook} (q.v.).
|
|||
@itemx minibuffer-exit-hook
|
||||
@xref{Minibuffer Misc}.
|
||||
|
||||
@item completion-setup-hook
|
||||
@xref{Minibuffer Misc}.
|
||||
|
||||
@item mouse-leave-buffer-hook
|
||||
@vindex mouse-leave-buffer-hook
|
||||
Hook run when about to switch windows with a mouse command.
|
||||
|
|
|
|||
|
|
@ -2617,6 +2617,12 @@ This is a normal hook that is run whenever the minibuffer is exited.
|
|||
@xref{Hooks}.
|
||||
@end defvar
|
||||
|
||||
@defvar completion-setup-hook
|
||||
This is a normal hook that is run every time the Completions buffer is
|
||||
shown or created.
|
||||
@xref{Hooks}.
|
||||
@end defvar
|
||||
|
||||
@defvar minibuffer-help-form
|
||||
@anchor{Definition of minibuffer-help-form}
|
||||
The current value of this variable is used to rebind @code{help-form}
|
||||
|
|
|
|||
8
etc/NEWS
8
etc/NEWS
|
|
@ -1317,6 +1317,14 @@ This face is used for error messages from 'diff'.
|
|||
*** New command 'diff-refresh-hunk'.
|
||||
This new command (bound to 'C-c C-l') regenerates the current hunk.
|
||||
|
||||
** Completions
|
||||
|
||||
---
|
||||
*** New option 'minibuffer-tab-through-completions-function'
|
||||
This contains the function to call when completions are shown and
|
||||
the *Completions* is already visible already. The variable contains
|
||||
the default value 'minibuffer-tab-through-completions-default'.
|
||||
|
||||
** Miscellaneous
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1283,6 +1283,27 @@ scroll the window of possible completions."
|
|||
minibuffer-completion-table
|
||||
minibuffer-completion-predicate)))
|
||||
|
||||
|
||||
(defun minibuffer-tab-through-completions-default ()
|
||||
"Default action in `minibuffer-scroll-window' WINDOW.
|
||||
This is called when *Completions* window is already visible."
|
||||
(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)
|
||||
;; Else scroll down one screen.
|
||||
(with-selected-window window
|
||||
(scroll-up)))
|
||||
nil)))
|
||||
|
||||
(defvar minibuffer-tab-through-completions-function
|
||||
#'minibuffer-tab-through-completions-default
|
||||
"Function to execute when requested completion.
|
||||
This is used when *Completions* frame is already visible and the
|
||||
completions command is called again. This function receives the
|
||||
window to execute commands as a paramenter.")
|
||||
|
||||
(defun completion--in-region-1 (beg end)
|
||||
;; If the previous command was not this,
|
||||
;; mark the completion buffer obsolete.
|
||||
|
|
@ -1290,21 +1311,14 @@ scroll the window of possible completions."
|
|||
(unless (eq 'completion-at-point last-command)
|
||||
(completion--flush-all-sorted-completions)
|
||||
(setq minibuffer-scroll-window nil))
|
||||
|
||||
(cond
|
||||
;; If there's a fresh completion window with a live buffer,
|
||||
;; and this command is repeated, scroll that window.
|
||||
((and (window-live-p minibuffer-scroll-window)
|
||||
(eq t (frame-visible-p (window-frame minibuffer-scroll-window))))
|
||||
(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)
|
||||
;; Else scroll down one screen.
|
||||
(with-selected-window window
|
||||
(scroll-up)))
|
||||
nil)))
|
||||
;; Action to perform when pressing tab and completions are shown.
|
||||
(funcall minibuffer-tab-through-completions-function)
|
||||
nil)
|
||||
;; If we're cycling, keep on cycling.
|
||||
((and completion-cycling completion-all-sorted-completions)
|
||||
(minibuffer-force-complete beg end)
|
||||
|
|
|
|||
220
lisp/zcomplete.el
Normal file
220
lisp/zcomplete.el
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
;;; zcomplete.el --- highlight and natural move throw *Completions* buffer -*- lexical-binding: t -*-
|
||||
|
||||
;; Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Jimmy Aguilar Mena <spacibba at aol dot com>
|
||||
;; Created: Aug 2020 Jimmy Aguilar Mena spacibba@aol.com
|
||||
;; Keywords: help, abbrev
|
||||
|
||||
;; This file is part of GNU Emacs.
|
||||
|
||||
;; GNU Emacs is free software: you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; the Free Software Foundation, either version 3 of the License, or
|
||||
;; (at your option) any later version.
|
||||
|
||||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||||
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;; GNU General Public License for more details.
|
||||
|
||||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; Enabling this package implements more dynamic interaction with the
|
||||
;; *Completions* buffer to give the user a similar experience than
|
||||
;; interacting with Zle from zsh shell. This basically means:
|
||||
|
||||
;; 0. When tab is pressed in the minibuffer the *Completions* buffer
|
||||
;; is shown as usual.
|
||||
|
||||
;; 1.1 If the completion list is too large then a second tab just
|
||||
;; scrolls the list.
|
||||
|
||||
;; 1.2 If all the completion candidates are visible then a second tab
|
||||
;; highlights the first candidate and completed in the minibuffer.
|
||||
;; (selected)
|
||||
|
||||
;; 2. Every time tab is pressed the next horizontal completion (on the
|
||||
;; right) is selected.
|
||||
|
||||
;; 3. When a candidate is highlighted arrow keys also selects the next
|
||||
;; candidate in the arrow direction. The arrow produces the same
|
||||
;; result either in the minibuffer or in *Completions* window.
|
||||
|
||||
;; The package intents to implement such functionalities without using
|
||||
;; hacks or complex functions, using the default Emacs *Completions*
|
||||
;; infrastructure. The main advantage is that it is not needed to
|
||||
;; switch to/from *Completions* buffer to select a candidate from the
|
||||
;; list with arrow keys.
|
||||
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'simple)
|
||||
(require 'minibuffer)
|
||||
|
||||
(defgroup zcomplete nil
|
||||
"Highlight candidates in completions buffer."
|
||||
:version "28.1"
|
||||
:group 'completion)
|
||||
|
||||
(defcustom zcomplete-set-suffix t
|
||||
"Insert completion candidate in minibuffer
|
||||
|
||||
When this variable is nil the completions will be highlighted but
|
||||
not inserted in the minibuffer."
|
||||
:type 'boolean
|
||||
:group 'zcomplete
|
||||
:version "28.1")
|
||||
|
||||
(defcustom zcomplete-tab-no-scroll nil
|
||||
"When press tab with too many candidates go to next or scroll.
|
||||
|
||||
When non-nil tab always go to next completions independently of
|
||||
the *Completions* buffer size. When this variable is nil tab
|
||||
scrolls the *Completions* buffer if there are too many candidates
|
||||
otherwise it goes to the next completion. "
|
||||
:type 'boolean
|
||||
:group 'zcomplete
|
||||
:version "28.1")
|
||||
|
||||
(defface zcomplete
|
||||
'((t :inherit highlight :extend t))
|
||||
"Default face for highlighting the current line in Hl-Line mode."
|
||||
:version "28.1"
|
||||
:group 'zcomplete)
|
||||
|
||||
(defvar zcomplete-overlay (make-overlay 0 0)
|
||||
"Overlay to use when `completion-highlight-mode' is enabled.")
|
||||
|
||||
;; *Completions* side commands
|
||||
(defun zcomplete-select-near ()
|
||||
"Move to and highlight closer item in the completion list."
|
||||
(interactive "p")
|
||||
(let ((point (point))
|
||||
(pmin (point-min))
|
||||
(pmax (point-max))
|
||||
prev next choice)
|
||||
|
||||
;; Try to find the closest completion if not in one
|
||||
(if (get-text-property point 'mouse-face)
|
||||
(unless isearch-mode ;; assert we are in the beginning
|
||||
(next-completion -1)
|
||||
(next-completion 1))
|
||||
|
||||
(setq prev (previous-single-property-change (min pmax (1+ point)) 'mouse-face nil pmin))
|
||||
(setq next (next-single-property-change point 'mouse-face nil pmax))
|
||||
(if (or (eobp)
|
||||
(< (- point prev) (- next point)))
|
||||
(next-completion -1)
|
||||
(next-completion 1)))
|
||||
|
||||
;; Select region
|
||||
(setq point (point))
|
||||
(setq next (next-single-property-change point 'mouse-face nil (point-max)))
|
||||
(setq choice (buffer-substring-no-properties point next))
|
||||
|
||||
(move-overlay zcomplete-overlay point next)
|
||||
(zcomplete--set-suffix choice)))
|
||||
|
||||
;; General commands
|
||||
(defun zcomplete--set-suffix (choice)
|
||||
"Set CHOICE suffix to current completion.
|
||||
It uses `completion-base-position' to determine the cursor
|
||||
position. If choice is the empty string the command removes the
|
||||
suffix."
|
||||
(when zcomplete-set-suffix
|
||||
(let* ((obase-position completion-base-position)
|
||||
(minibuffer-window (active-minibuffer-window))
|
||||
(minibuffer-buffer (window-buffer minibuffer-window))
|
||||
(completion-no-auto-exit t))
|
||||
|
||||
(with-selected-window minibuffer-window
|
||||
(let* ((prompt-end (minibuffer-prompt-end))
|
||||
(cursor-pos (if obase-position
|
||||
(cadr obase-position)
|
||||
(choose-completion-guess-base-position choice)))
|
||||
(prefix-len (- cursor-pos prompt-end))
|
||||
(suffix (if (< prefix-len (length choice))
|
||||
(substring choice prefix-len)
|
||||
""))
|
||||
(suffix-len (string-width suffix)))
|
||||
|
||||
(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))))))
|
||||
|
||||
(defvar zcomplete-completions-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map [mouse-2] 'choose-completion)
|
||||
(define-key map [follow-link] 'mouse-face)
|
||||
(define-key map [down-mouse-2] nil)
|
||||
(define-key map "\C-m" 'choose-completion)
|
||||
(define-key map "\e\e\e" 'delete-completion-window)
|
||||
(define-key map [left] 'previous-completion)
|
||||
(define-key map [right] 'next-completion)
|
||||
(define-key map [?\t] 'next-completion)
|
||||
(define-key map [backtab] 'previous-completion)
|
||||
(define-key map "\C-g" #'quit-window)
|
||||
map)
|
||||
"Keymap used in *Completions* while highlighting candidates.")
|
||||
|
||||
(defun zcomplete--minibuffer-hook ()
|
||||
"Close *Completions* buffer when the command is not in the map."
|
||||
(zcomplete--set-suffix "")
|
||||
(unless (lookup-key minibuffer-local-must-match-map
|
||||
(this-single-command-keys))
|
||||
(minibuffer-hide-completions)))
|
||||
|
||||
(defun zcomplete--completions-pre-hook ()
|
||||
"Close *Completions* buffer when the command is not in the map."
|
||||
(zcomplete--set-suffix "")
|
||||
(when (eq this-command 'self-insert-command)
|
||||
(call-interactively #'quit-window)))
|
||||
|
||||
(defun zcomplete--hack (data context signal)
|
||||
"Alternative to command-error-default-function.
|
||||
This will exit the *Completions* if the error is buffer-read-only."
|
||||
(if (eq (car data) 'buffer-read-only)
|
||||
(call-interactively #'quit-window)
|
||||
(command-error-default-function data context signal)))
|
||||
|
||||
(defun zcomplete--completions-setup-hook ()
|
||||
"Function to call when enabling the `completion-highlight-mode' mode.
|
||||
It is called when showing the *Completions* buffer."
|
||||
(delete-overlay zcomplete-overlay)
|
||||
|
||||
;; Add zcomplete-minibuffer-map bindings to minibuffer
|
||||
(add-hook 'pre-command-hook #'zcomplete--minibuffer-hook nil t)
|
||||
|
||||
;; After this commands are for Completions
|
||||
(call-interactively #'switch-to-completions)
|
||||
(add-hook 'pre-command-hook #'zcomplete--completions-pre-hook nil t)
|
||||
(add-hook 'post-command-hook #'zcomplete-select-near nil t)
|
||||
|
||||
(setq-local command-error-function #'zcomplete--hack)
|
||||
(setq-local mode-line-format nil)
|
||||
(use-local-map zcomplete-completions-map)
|
||||
|
||||
;; Autoselect candidate if enabled
|
||||
(zcomplete-select-near))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode zcomplete-mode
|
||||
"Completion highlight mode to enable candidates highlight in the minibuffer."
|
||||
:global t
|
||||
:group 'minibuffer
|
||||
|
||||
(if zcomplete-mode
|
||||
(progn
|
||||
(overlay-put zcomplete-overlay 'face 'zcomplete)
|
||||
(add-hook 'completion-setup-hook #'zcomplete--completions-setup-hook t))
|
||||
|
||||
(remove-hook 'completion-setup-hook #'zcomplete--completions-setup-hook)))
|
||||
|
||||
(provide 'zcomplete)
|
||||
;;; zcomplete.el ends here
|
||||
Loading…
Reference in a new issue