unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Tassilo Horn <tsdh@gnu.org>
To: emacs-devel@gnu.org
Subject: [GNU ELPA] New package proposal: aggressive-completion.el
Date: Sat, 03 Apr 2021 09:53:50 +0200	[thread overview]
Message-ID: <87o8evok58.fsf@gnu.org> (raw)

[-- Attachment #1: Type: text/plain, Size: 973 bytes --]

Hi all,

I'd like to propose the attached aggressive-completion.el as a new GNU
ELPA package.  I've used this since several months and now had the time
to extract it from my ~/.emacs and make a proper minor-mode out of it.

What is it?
===========

I've used several different minibuffer completion frameworks in the past
(including ivy, raven, and selectrum) in the past but always came back
to the standard emacs minibuffer completion with its nice configuration
means in terms of `completion-category-overrides' and friends.

What I've liked with the other frameworks, however, was that the
completion candidates are immediately visible and in many scenarios I
needed less typing (especially less pinky-stressing TAB-ing).

So the central idea of aggressive-completion.el is that it

  1) automatically completes for you after a short delay, and it
  2) always shows the completion help (unless there are too many).

Without further ado, here it is (comments welcome):


[-- Attachment #2: aggressive-completion.el --]
[-- Type: text/plain, Size: 7239 bytes --]

;;; aggressive-completion.el --- Automatic minibuffer completion -*- lexical-binding: t -*-

;; Copyright (C) 2021 Free Software Foundation, Inc.

;; Author: Tassilo Horn <tsdh@gnu.org>
;; Maintainer: Tassilo Horn <tsdh@gnu.org>
;; Keywords: minibuffer completion
;; Package-Requires: ((emacs "27.1"))
;; Version: 1.0

;; 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:
;;
;; Aggressive completion mode (`aggressive-completion-mode') is a minor mode
;; which automatically completes for you after a short delay
;; (`aggressive-completion-delay') and always shows all possible completions
;; using the standard completion help (unless the number of possible
;; completions exceeds `aggressive-completion-max-shown-completions').
;;
;; Automatic completion is temporarily disabled after all commands in
;; `aggressive-completion-no-complete-commands'.  Basically all deletion/kill
;; commands are listed here in order not to complete back to the thing you just
;; deleted.
;;
;; Aggressive completion can be toggled using
;; `aggressive-completion-toggle-auto-complete' (bound to `M-t' by default)
;; which is especially useful when trying to find a not yet existing file or
;; switch to a new buffer.
;;
;; You can switch from minibuffer to *Completions* buffer and back again using
;; `aggressive-completion-switch-to-completions' (bound to `M-c' by default).
;; All keys bound to this command in `aggressive-completion-minibuffer-map'
;; will be bound to `other-window' in `completion-list-mode-map' so that those
;; keys act as switch-back-and-forth commands.

(defgroup aggressive-completion nil
  "Aggressive completion completes for you.")

(defcustom aggressive-completion-delay 0.3
  "Delay in seconds before aggressive completion kicks in."
  :type 'number)

(defcustom aggressive-completion-auto-complete t
  "Complete automatically if non-nil.
If nil, only show the completion help."
  :type 'boolean)

(defcustom aggressive-completion-max-shown-completions 1000
  "Maximum number of possible completions for showing completion help."
  :type 'integer)

(defcustom aggressive-completion-no-complete-commands
  '( left-char icomplete-fido-backward-updir minibuffer-complete
     right-char delete-backward-char backward-kill-word
     backward-kill-paragraph backward-kill-sentence backward-kill-sexp
     delete-char kill-word kill-line completion-at-point)
  "Commands after which automatic completion is not performed.")

(defvar aggressive-completion--timer nil)

(defun aggressive-completion--do ()
  (when (window-minibuffer-p)
    (let* ((completions (completion-all-sorted-completions))
           ;; Don't ding if there are no completions, etc.
           (visible-bell nil)
           (ring-bell-function #'ignore)
           ;; Automatic completion should not cycle.
           (completion-cycle-threshold nil)
           (completion-cycling nil))
      (let ((i 0))
        (while (and (<= i aggressive-completion-max-shown-completions)
                    (consp completions))
          (setq completions (cdr completions))
          (cl-incf i))
        (if (and (> i 0)
                 (< i aggressive-completion-max-shown-completions))
            (if (or (null aggressive-completion-auto-complete)
                    (memq last-command
                          aggressive-completion-no-complete-commands))
                ;; This ensures we still can repeatedly hit TAB to scroll
                ;; through the list of completions.
                (unless (and (= last-command-event ?\t)
                             (window-live-p
                              (get-buffer-window "*Completions*"))
                             (with-current-buffer "*Completions*"
                               (> (point) (point-min))))
                  (minibuffer-completion-help))
              (minibuffer-complete)
              (unless (window-live-p (get-buffer-window "*Completions*"))
                (minibuffer-completion-help)))
          ;; Close the *Completions* buffer if there are too many
          ;; or zero completions.
          (when-let ((win (get-buffer-window "*Completions*")))
            (when (and (window-live-p win)
                       (not (memq last-command
                                  '(minibuffer-completion-help
                                    minibuffer-complete
                                    completion-at-point))))
              (quit-window nil win))))))))

(defun aggressive-completion--timer-start ()
  (when aggressive-completion--timer
    (cancel-timer aggressive-completion--timer))

  (setq aggressive-completion--timer
        (run-with-idle-timer aggressive-completion-delay nil
                             #'aggressive-completion--do)))

(defun aggressive-completion-toggle-auto-complete ()
  "Toggles automatic completion."
  (interactive)
  (setq aggressive-completion-auto-complete
        (not aggressive-completion-auto-complete)))

(defun aggressive-completion--setup ()
  "Setup aggressive completion."
  (when (and (not executing-kbd-macro)
             (window-minibuffer-p)
             minibuffer-completion-table)
    (set-keymap-parent aggressive-completion-minibuffer-map (current-local-map))
    (use-local-map aggressive-completion-minibuffer-map)

    ;; If `aggressive-completion-switch-to-completions' is bound to keys, bind
    ;; the same keys in `completion-list-mode-map' to `other-window' so that
    ;; one can conveniently switch back and forth using the same key.
    (dolist (key (where-is-internal
	          #'aggressive-completion-switch-to-completions))
      (define-key completion-list-mode-map key #'other-window))

    (add-hook 'post-command-hook
              #'aggressive-completion--timer-start nil t)))

;; Add an alias so that we can find out the bound key using
;; `where-is-internal'.
(defalias 'aggressive-completion-switch-to-completions
  #'switch-to-completions)

(defvar aggressive-completion-minibuffer-map
  (let ((map (make-sparse-keymap)))
    (require 'icomplete)
    (define-key map (kbd "DEL") #'icomplete-fido-backward-updir)
    (define-key map (kbd "M-t") #'aggressive-completion-toggle-auto-complete)
    (define-key map (kbd "M-c") #'aggressive-completion-switch-to-completions)
    map)
  "The local minibuffer keymap when `aggressive-completion-mode' is enabled.")

(define-minor-mode aggressive-completion-mode
  "Perform aggressive minibuffer completion."
  :lighter "ACmp"
  (if aggressive-completion-mode
      (add-hook 'minibuffer-setup-hook #'aggressive-completion--setup)
    (remove-hook 'minibuffer-setup-hook #'aggressive-completion--setup)))

[-- Attachment #3: Type: text/plain, Size: 14 bytes --]


Bye,
Tassilo

             reply	other threads:[~2021-04-03  7:53 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-04-03  7:53 Tassilo Horn [this message]
2021-04-03  8:37 ` [GNU ELPA] New package proposal: aggressive-completion.el Tassilo Horn
2021-04-03  9:11 ` Manuel Uberti
2021-04-03  9:42   ` Tassilo Horn
2021-04-03 10:14     ` Jean Louis
2021-04-03 11:17     ` Jean Louis
2021-04-03 10:07   ` Jean Louis
2021-04-03  9:36 ` Jean Louis
2021-04-03 10:03   ` Tassilo Horn
2021-04-03 10:19     ` Jean Louis
2021-04-03 10:24       ` Tassilo Horn
2021-04-04 13:53     ` Basil L. Contovounesios
2021-04-04 19:05       ` Tassilo Horn
2021-04-04 20:12         ` T.V Raman
2021-04-05  7:01           ` Tassilo Horn
2021-04-05 14:21             ` T.V Raman
2021-04-04 20:26         ` Stefan Monnier
2021-04-05  7:17           ` Tassilo Horn
2021-04-03  9:49 ` Jean Louis
2021-04-03 10:05   ` Tassilo Horn
2021-04-03 11:53 ` Philip Kaludercic
2021-04-03 11:55 ` Philip Kaludercic
2021-04-03 13:43   ` Tassilo Horn
2021-04-03 17:22     ` [GNU ELPA] New package proposal: aggressive-completion.El Philip Kaludercic
2021-04-03 18:03       ` Tassilo Horn
2021-04-03 14:04 ` [GNU ELPA] New package proposal: aggressive-completion.el Stefan Monnier
2021-04-03 18:29   ` Tassilo Horn
2021-04-03 19:30     ` Tassilo Horn
2021-04-03 21:01       ` Stefan Monnier
2021-04-03 20:02 ` Gabriel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87o8evok58.fsf@gnu.org \
    --to=tsdh@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).