emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

icomplete.el enhancement


From: Drew Adams
Subject: icomplete.el enhancement
Date: Tue, 13 Apr 2004 14:24:35 -0700

I use Emacs 20.7. A quick scan of the Emacs 21 icomplete.el code suggests
that this (1995!)enhancement would still be applicable to Emacs 21. Here's
what it does:

* When several completions are possible, the common completion stem (prefix)
is distinguished from the different completion remainders (suffixes). The
two are shown in different faces, which are user-definable (variables).
Without this enhancement, incremental completion can be quite confusing
(*not even worth it*), IMO.

* The completion alternatives are sorted (using string<).

* Icompletion is not done if the minibuffer input begins with `(', since the
input is not a command name (it is probably an Emacs Lisp expression to be
submitted for evaluation).

* Out of the box, icompletion interferes with the
`minibuffer-completion-table' (or vice versa). To fix this, the following
Emacs primitives have been redefined so that they avoid icompletion:
  . read-from-minibuffer
  . read-no-blanks-input
  . read-string

I don't know if the last bullet is still appropriate, but I believe the
others are, in any case.

Here's the code (macro def-face-const just defines a face):

;;; icomplete+.el --- Extensions to `icomplete.el'.
;;
;; Description: Extensions to `icomplete.el'.
;; Author: Drew Adams
;; Copyright (C) 1996-2004, Drew Adams, all rights reserved.
;; Created: Mon Oct 16 13:33:18 1995
;; Keywords: help, abbrev, internal, extensions, local
;; Compatibility: GNU Emacs 20.x
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;; Commentary:
;;
;;    Extensions to `icomplete.el'.
;;
;;  New user options (variables) defined here:
;;
;;    `icomplete-choices-face', `icomplete-determined-face'.
;;
;;
;;  ***** NOTE: The following functions defined in `icomplete.el'
;;              have been REDEFINED HERE:
;;
;;  `icomplete-completions' -
;;     Sorts alternatives and puts them in a different face.
;;  `icomplete-exhibit' - Doesn't insert if input begins with `('
;;                        (e.g. `repeat-complex-command').
;;
;;
;;  ***** NOTE: The following EMACS PRIMITIVES have been REDEFINED HERE
;;
;;  `read-from-minibuffer' -
;;     Resets `minibuffer-completion-table' to avoid icompletion.
;;  `read-no-blanks-input' -
;;     Resets `minibuffer-completion-table' to avoid icompletion.
;;  `read-string' -
;;     Resets `minibuffer-completion-table' to avoid icompletion.
;;
;;
;;  This file should be loaded after loading the standard GNU file
;;  `icomplete.el'.  So, in your `~/.emacs' file, do this:
;;  (eval-after-load "icomplete" '(progn (require 'icomplete+)))
;;
;;; Code:

(require 'icomplete)
(require 'cl) ;; when, unless

;; Get macro `define-face-const' when this is compiled,
;; or run interpreted, but not when the compiled code is loaded.
(eval-when-compile (require 'def-face-const))

(provide 'icomplete+)

;;;;;;;;;;;;;;;;;;;


(unless (boundp 'darkgoldenrod-foreground-face)
  (define-face-const "DarkGoldenrod" nil))
(defvar icomplete-choices-face darkgoldenrod-foreground-face
  "*Face for minibuffer reminder of possible completion suffixes.")

(unless (boundp 'seagreen-foreground-face)
  (define-face-const "SeaGreen" nil))
(defvar icomplete-determined-face seagreen-foreground-face
  "*Face for minibuffer reminder of possible completion prefix.")


;; REPLACES ORIGINAL defined in `icomplete.el':
;; Doesn't insert if input begins with `(' (e.g. `repeat-complex-command').
(defun icomplete-exhibit ()
  "Insert icomplete completions display.
Should be run via minibuffer `post-command-hook'.
See `icomplete-mode' and `minibuffer-setup-hook'."
  (when (icomplete-simple-completing-p)
    (let ((contents (buffer-substring (point-min) (point-max)))
          (buffer-undo-list t))
      (save-excursion
        (goto-char (point-max))
                                        ; Register the end of input, so we
                                        ; know where the extra stuff
                                        ; (match-status info) begins:
        (unless (boundp 'icomplete-eoinput)
          ;; In case it got wiped out by major mode business:
          (make-local-variable 'icomplete-eoinput))
        (setq icomplete-eoinput (point))
                                        ; Insert the match-status
information:
        (when (and (> (point-max) 1)
                   (save-excursion
                     (goto-char (point-min))
                     (not (looking-at   ; No (, ", ', 9 etc. at start.

"\\(\\s-+$\\|\\s-*\\(\\s(\\|\\s\"\\|\\s'\\|\\s<\\|\
[0-9]\\)\\)")))
                   (or
                    ;; Don't bother with delay after certain number of chars:
                    (> (point-max) icomplete-max-delay-chars)
                    ;; Don't delay if alternatives number is small enough:
                    (if minibuffer-completion-table
                        (cond ((numberp minibuffer-completion-table)
                               (< minibuffer-completion-table
                                  icomplete-delay-completions-threshold))
                              ((sequencep minibuffer-completion-table)
                               (< (length minibuffer-completion-table)
                                  icomplete-delay-completions-threshold))
                              ))
                    ;; Delay - give some grace time for next keystroke, before
                    ;; embarking on computing completions:
                    (sit-for icomplete-compute-delay)))
          (insert-string
           (icomplete-completions contents minibuffer-completion-table
                                  minibuffer-completion-predicate
                                  (not minibuffer-completion-confirm))))))))


;; REPLACES ORIGINAL defined in `icomplete.el':
;; Sorts alternatives and puts them in a different face.
(defun icomplete-completions (name candidates predicate require-match)
  "Identify prospective candidates for minibuffer completion.

The display is updated with each minibuffer keystroke during
minibuffer completion.

Prospective completion suffixes (if any) are displayed, bracketed by
\"()\", \"[]\", or \"{}\".  The choice of brackets is as follows:

  \(...) - A single prospect is identified and matching is enforced.
  \[...] - A single prospect is identified and matching is optional.
  \{...} - Multiple prospects, separated by commas, are indicated,
           and further input is required to distinguish a single one.

The displays for unambiguous matches have \" [ Matched ]\" appended
\(whether complete or not), or \" \[ No match ]\", if no eligible
matches exist.  \(Keybindings for uniquely matched commands
are exhibited within the square braces.)"
  ;; 'all-completions' doesn't like empty
  ;; minibuffer-completion-table's (ie: (nil))
  (when (and (listp candidates) (null (car candidates)))
    (setq candidates nil))
  (let ((comps (sort (all-completions name candidates predicate) 'string<))
                                        ; "-determined" - only one candidate
        (open-bracket-determined (if require-match "   (" "   ["))
        (close-bracket-determined (if require-match ")" "]"))
                                        ;"-prospects" - more than one
candidate
        (open-bracket-prospects "     { ")
        (close-bracket-prospects " }")
        prompt determined-part)
    (catch 'input
      (cond ((null comps)
             (setq prompt (setq determined-part
                                (format "\t%sNo match%s"
                                        open-bracket-determined
                                        close-bracket-determined))))
            ((null (cdr comps))         ;one match
             (setq prompt
                   (setq determined-part
                         (concat (if (and (> (length (car comps))
                                             (length name)))
                                     (concat open-bracket-determined
                                             (substring (car comps)
                                                        (length name))
                                             close-bracket-determined)
                                   "")
                                 "\t[Matched"
                                 (let ((keys (and
icomplete-show-key-bindings
                                                  (commandp (intern-soft
(car comps)))
                                                  (icomplete-get-keys (car
comps)))))
                                   (if keys
                                       (concat "; " keys)
                                     ""))
                                 "]"))))
            (t                          ;multiple matches
             (let* ((most (try-completion name candidates
                                          (and predicate
                                               ;; Wrap predicate in
impatience - ie,
                                               ;; `throw' up when pending
input is
                                               ;; noticed.  Adds some
overhead to
                                               ;; predicate, but should be
worth it.
                                               (function
                                                (lambda (item)
                                                  (if (input-pending-p)
                                                      (throw 'input "")
                                                    (apply predicate
                                                           item nil)))))))
                    (most-len (length most))
                    most-is-exact
                    (alternatives
                     (substring
                      (apply
                       (function concat)
                       (mapcar (function
                                (lambda (com)
                                  (if (input-pending-p) (throw 'input ""))
                                  (if (= (length com) most-len)
                                      ;; Most is one exact match,
                                      ;; note that and leave out
                                      ;; for later indication:
                                      (progn (setq most-is-exact t)
                                             nil)
                                    (concat ", "
                                            (substring com most-len)))))
                               comps))
                      1)))
               (setq prompt
                     (concat (and (> most-len (length name))
                                  (setq determined-part
                                        (concat open-bracket-determined
                                                (substring most (length
name))
                                                close-bracket-determined)))
                             open-bracket-prospects
                             (if most-is-exact
                                 ;; Add a ',' at the front to indicate
"complete but
                                 ;; not unique":
                                 (concat "," alternatives)
                               alternatives)
                             close-bracket-prospects)))))
      (put-text-property (length determined-part) (length prompt)
                         'face icomplete-choices-face prompt)
      (put-text-property 0 (length determined-part)
                         'face icomplete-determined-face prompt)
      prompt)))


;;; The following functions have been REDEFINED to reset the
;;; `minibuffer-completion-table' in order to avoid icompletion.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;; Note:  The function `read-input' is an alias for `read-string'.

(or (fboundp 'old-read-string)
(fset 'old-read-string (symbol-function 'read-string)))

;; REPLACES ORIGINAL:
;; Resets `minibuffer-completion-table' to avoid icompletion.
(defsubst read-string
  (prompt &optional initial-input history default-value
inherit-input-method)
  "Read a string from the minibuffer, prompting with string PROMPT.
If non-nil, optional 2nd arg INITIAL-INPUT is a string to insert
    before reading.
The third arg HISTORY, if non-nil, specifies a history list and
    optionally the initial position in that list.
    See `read-from-minibuffer' for details of HISTORY argument.
Fourth arg DEFAULT-VALUE is the default value.  If non-nil, it is used
    for history commands and as the value to return if user enters an
    empty string.
Fifth arg INHERIT-INPUT-METHOD, if non-nil, means minibuffer inherits
    current input method and setting of `enable-multibyte-characters'."
  (setq minibuffer-completion-table nil) ; So won't icomplete by default.
  (old-read-string prompt initial-input history default-value
inherit-input-method))


(or (fboundp 'old-read-from-minibuffer)
(fset 'old-read-from-minibuffer (symbol-function 'read-from-minibuffer)))

;; REPLACES ORIGINAL:
;; Resets `minibuffer-completion-table' to avoid icompletion.
(defsubst read-from-minibuffer
  (prompt
   &optional initial-contents keymap read hist default-value
inherit-input-method)
  "Read a string from the minibuffer, prompting with string PROMPT.
If optional second arg INITIAL-CONTENTS is non-nil, it is a string
  to be inserted into the minibuffer before reading input.
  If INITIAL-CONTENTS is (STRING . POSITION), the initial input
  is STRING, but point is placed at position POSITION in the minibuffer.
Third arg KEYMAP is a keymap to use while reading;
  if omitted or nil, the default is `minibuffer-local-map'.
If fourth arg READ is non-nil, then interpret the result as a lisp object
  and return that object:
  in other words, do `(car (read-from-string INPUT-STRING))'
Fifth arg HIST, if non-nil, specifies a history list
  and optionally the initial position in the list.
  It can be a symbol, which is the history list variable to use,
  or it can be a cons cell (HISTVAR . HISTPOS).
  In that case, HISTVAR is the history list variable to use,
  and HISTPOS is the initial position (the position in the list
  which INITIAL-CONTENTS corresponds to).
  Positions are counted starting from 1 at the beginning of the list.
Sixth arg DEFAULT-VALUE is the default value.  If non-nil, it is
  available for history commands; but `read-from-minibuffer' does NOT
  return DEFAULT-VALUE if the user enters empty input.
  It returns the empty string.
Seventh arg INHERIT-INPUT-METHOD, if non-nil, means the minibuffer
  inherits the current input method and the setting of
  `enable-multibyte-characters'.
If variable `minibuffer-allow-text-properties' is non-nil, then the
  string returned includes whatever text properties were present in
  the minibuffer.  Otherwise the value has no text properties."
  (setq minibuffer-completion-table nil) ; So won't icomplete by default.
  (old-read-from-minibuffer
   prompt initial-contents keymap read hist default-value
inherit-input-method))


(or (fboundp 'old-read-no-blanks-input)
(fset 'old-read-no-blanks-input (symbol-function 'read-no-blanks-input)))

;; REPLACES ORIGINAL:
;; Resets `minibuffer-completion-table' to avoid icompletion.
(defsubst read-no-blanks-input (prompt &optional initial-contents
inherit-input-method)
  "Read a string from the minibuffer, not allowing blanks.
Arg PROMPT is a prompt string.

If optional 2nd arg INITIAL-CONTENTS is non-nil, it is a string
to be inserted into the minibuffer before reading input.

Third arg INHERIT-INPUT-METHOD, if non-nil, means the minibuffer
inherits the current input method and the setting of
`enable-multibyte-characters'."
  (setq minibuffer-completion-table nil) ; So won't icomplete by default.
  (old-read-no-blanks-input prompt initial-contents inherit-input-method))





reply via email to

[Prev in Thread] Current Thread [Next in Thread]