Ver código fonte

Add ido-completing-read+.el

This implements a version of "ido-completing-read" that incorporates all
the generally useful improvements, fixups, and workarounds from
ido-ubiquitous. It does *not* include any overrides, a collection
argument that is a function, or support for old-style default behavior.
Those features are only avilable through ido-ubiquitous.

See #64 for more information.
Ryan C. Thompson 10 anos atrás
pai
commit
bcb9c23d5a
1 arquivos alterados com 189 adições e 0 exclusões
  1. 189 0
      ido-completing-read+.el

+ 189 - 0
ido-completing-read+.el

@@ -0,0 +1,189 @@
+;; -*- lexical-binding: t -*-
+
+;;; ido-completing-read+.el --- A completing-read-function using ido
+;; 
+;; Filename: ido-completing-read+.el
+;; Author: Ryan Thompson
+;; Created: Sat Apr  4 13:41:20 2015 (-0700)
+;; Version: 0.1
+;; Package-Requires: ((emacs "24.1"))
+;; URL: https://github.com/DarwinAwardWinner/ido-ubiquitous
+;; Keywords: ido, completion, convenience
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Commentary: 
+;; 
+;; This package implments the `ido-completing-read+' function, which
+;; is a wrapper for `ido-completing-read'. Importantly, it detects
+;; edge cases that ordinary ido cannot handle and either adjusts them
+;; so ido *can* handle them, or else simply falls back to Emacs'
+;; standard completion instead.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;; This program 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.
+;; 
+;; This program 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 <http://www.gnu.org/licenses/>.
+;; 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Code:
+
+(require 'cl-macs)
+
+(defvar ido-cr+-enable-next-call nil
+  "If non-nil, then the next call to `ido-completing-read' is by `ido-completing-read+'.")
+(defvar ido-cr+-enable-this-call nil
+  "If non-nil, then the current call to `ido-completing-read' is by `ido-completing-read+'")
+
+(defcustom ido-cr+-fallback-function
+  ;; Initialize to the current value of `completing-read-function',
+  ;; unless that is already set to the ido completer, in which case
+  ;; use `completing-read-default'.
+  (if (memq completing-read-function
+            '(ido-completing-read+ ido-completing-read))
+      'completing-read-default
+    completing-read-function)
+  "Alternate completing-read function to use when ido is not wanted.
+
+This will be used for functions that are incompatibile with ido
+or if ido cannot handle the completion arguments. It will also be
+used when the user requests non-ido completion manually via C-f
+or C-b."
+  :type '(choice (const :tag "Standard emacs completion"
+                        completing-read-default)
+                 (function :tag "Other function"))
+  :group 'ido-completing-read+)
+
+(defcustom ido-cr+-max-items 30000
+  "Max collection size to use ido-cr+ on.
+
+If `ido-completing-read+' is called on a collection larger than
+this, the fallback completion method will be used instead. To
+disable fallback based on collection size, set this to nil."
+  :type '(choice (const :tag "No limit" nil)
+                 (integer
+                  :tag "Limit" :value 5000
+                  :validate
+                  (lambda (widget)
+                    (let ((v (widget-value widget)))
+                      (if (and (integerp v)
+                               (> v 0))
+                          nil
+                        (widget-put widget :error "This field should contain a positive integer")
+                        widget)))))
+  :group 'ido-completing-read+)
+
+;; Signal used to trigger fallback
+(define-error 'ido-cr+-fallback "ido-cr+-fallback")
+
+(defun ido-completing-read+ (prompt collection &optional predicate
+                                    require-match initial-input
+                                    hist def inherit-input-method)
+  "ido-based method for reading from the minibuffer with completion.
+
+See `completing-read' for the meaning of the arguments.
+
+This function is a wrapper for `ido-completing-read' designed to
+be used as the value of `completing-read-function'. Importantly,
+it detects edge cases that ido cannot handle and uses normal
+completion for them."
+  (let (;; Save the original arguments in case we need to do the
+        ;; fallback
+        (orig-args
+         (list prompt collection predicate require-match
+               initial-input hist def inherit-input-method)))
+    (condition-case nil
+        (progn
+          (when (or
+                 ;; Can't handle this arg
+                 inherit-input-method
+                 ;; Can't handle this being set
+                 (bound-and-true-p completion-extra-properties)
+                 ;; Can't handle functional collection
+                 (functionp collection))
+            (signal 'ido-cr+-fallback nil))
+          ;; Expand all possible completions
+          (setq collection (all-completions "" collection predicate))
+          ;; Check for excessively large collection
+          (when (and ido-cr+-max-items
+                     (> (length collection) ido-cr+-max-items))
+            (signal 'ido-cr+-fallback nil))
+          ;; ido doesn't natively handle DEF being a list. If DEF is
+          ;; a list, prepend it to CHOICES and set DEF to just the
+          ;; car of the default list.
+          (when (and def (listp def))
+            (setq choices
+                  (append def
+                          (nreverse (cl-set-difference choices def)))
+                  def (car def)))
+          ;; Work around a bug in ido when both INITIAL-INPUT and
+          ;; DEF are provided.
+          (let ((initial
+                 (or (if (consp initial-input)
+                         (car initial-input)
+                       initial-input)
+                     "")))
+            (when (and def initial
+                       (stringp initial)
+                       (not (string= initial "")))
+              ;; Both default and initial input were provided. So
+              ;; keep the initial input and preprocess the choices
+              ;; list to put the default at the head, then proceed
+              ;; with default = nil.
+              (setq choices (cons def (remove def choices))
+                    def nil)))
+          ;; Ready to do actual ido completion
+          (prog1
+              (let ((ido-cr+-enable-next-call t))
+                (ido-completing-read
+                 prompt collection
+                 predicate require-match initial-input hist def
+                 inherit-input-method))
+            ;; This detects when the user triggered fallback mode
+            ;; manually.
+            (when (eq ido-exit 'fallback)
+              (signal 'ido-cr+-fallback nil))))
+      ;; Handler for ido-cr+-fallback signal
+      (ido-cr+-fallback
+       (apply ido-cr+-fallback-function orig-args)))))
+
+(defadvice ido-completing-read (around ido-cr+ activate)
+  "Ensure that `ido-cr+-enable-this-call' is set properly.
+
+This variable should be non-nil while executing
+`ido-completing-read' if it was called from
+`ido-completing-read+'."
+  (let ((ido-cr+-enable-this-call ido-cr+-enable-next-call)
+        (ido-cr+-enable-next-call nil))
+    ad-do-it))
+
+;; Fallback on magic C-f and C-b
+(defadvice ido-magic-forward-char (before ido-cr+-fallback activate)
+  "Allow falling back in ido-completing-read+."
+  (message "ido-cr+-enable-this-call is %S" ido-cr+-enable-this-call)
+  (when ido-cr+-enable-this-call
+    ;; `ido-context-switch-command' is already let-bound at this
+    ;; point.
+    (setq ido-context-switch-command #'ido-fallback-command)))
+
+(defadvice ido-magic-backward-char (before ido-cr+-fallback activate)
+  "Allow falling back in ido-completing-read+."
+  (when ido-cr+-enable-this-call
+    ;; `ido-context-switch-command' is already let-bound at this
+    ;; point.
+    (setq ido-context-switch-command #'ido-fallback-command)))
+
+(provide 'ido-completing-read+)
+
+;;; ido-completing-read+.el ends here