1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159 |
- ;;; ido-completing-read+.el --- A completing-read-function using ido -*- lexical-binding: t -*-
- ;; Copyright (C) 2011-2017 Ryan C. Thompson
- ;; Filename: ido-completing-read+.el
- ;; Author: Ryan Thompson
- ;; Created: Sat Apr 4 13:41:20 2015 (-0700)
- ;; Version: 4.3
- ;; Package-Requires: ((emacs "24.4") (cl-lib "0.5") (s "0.1"))
- ;; URL: https://github.com/DarwinAwardWinner/ido-completing-read-plus
- ;; Keywords: ido, completion, convenience
- ;; This file is NOT part of GNU Emacs.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;;; Commentary:
- ;; If you use the excellent `ido-mode' for efficient completion of
- ;; file names and buffers, you might wonder if you can get ido-style
- ;; completion everywhere else too. Well, that's what this package
- ;; does! ido-ubiquitous is here to enable ido-style completion for
- ;; (almost) every function that uses the standard completion function
- ;; `completing-read'.
- ;; This package implements 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. Hence, you can safely set
- ;; `completing-read-function' to `ido-completing-read+' without
- ;; worrying about breaking completion features that are incompatible
- ;; with ido.
- ;; To use this package, call `ido-ubiquitous-mode' to enable the mode,
- ;; or use `M-x customize-variable ido-ubiquitous-mode' it to enable it
- ;; permanently. Once the mode is enabled, most functions that use
- ;; `completing-read' will now have ido completion. If you decide in
- ;; the middle of a command that you would rather not use ido, just use
- ;; C-f or C-b at the end/beginning of the input to fall back to
- ;; non-ido completion (this is the same shortcut as when using ido for
- ;; buffers or files).
- ;; Note that `completing-read-default' is a very general function with
- ;; many complex behaviors that ido cannot emulate. This package
- ;; attempts to detect some of these cases and avoid using ido when it
- ;; sees them. So some functions will not have ido completion even when
- ;; this mode is enabled. Some other functions have ido disabled in
- ;; them because their packages already provide support for ido via
- ;; other means (for example, magit). See `M-x describe-variable
- ;; ido-cr+-function-blacklist' for more information.
- ;; ido-completing-read+ version 4.0 is a major update. The formerly
- ;; separate package ido-ubiquitous has been subsumed into
- ;; ido-completing-read+, so ido-ubiquitous 4.0 is just a wrapper that
- ;; loads ido-completing-read+ and displays a warning about being
- ;; obsolete. If you have previously customized ido-ubiquitous, be sure
- ;; to check out `M-x customize-group ido-completing-read-plus' after
- ;; updating to 4.0 and make sure the new settings are to your liking.
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;
- ;; 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:
- (defconst ido-completing-read+-version "4.3"
- "Currently running version of ido-completing-read+.
- Note that when you update ido-completing-read+, this variable may
- not be updated until you restart Emacs.")
- (require 'ido)
- (require 'cl-lib)
- (require 'cus-edit)
- (require 's)
- ;;; Debug messages
- (define-minor-mode ido-cr+-debug-mode
- "If non-nil, ido-cr+ will print debug info.
- Debug info is printed to the *Messages* buffer."
- nil
- :global t
- :group 'ido-completing-read-plus)
- (defsubst ido-cr+--debug-message (format-string &rest args)
- (when ido-cr+-debug-mode
- (apply #'message (concat "ido-completing-read+: " format-string) args)))
- ;;; Ido variables
- ;; For unknown reasons, these variables need to be re-declared here to
- ;; silence byte-compiler warnings, despite already being declared in
- ;; ido.el.
- (defmacro define-ido-internal-var (symbol &optional initvalue docstring)
- "Declare and initialize an ido internal variable.
- This is used to suppress byte-compilation warnings about
- reference to free variables when ido-cr+ attempts to access
- internal ido variables with no initial value set. Such variables
- are originally declared like `(defvar VARNAME)'.
- This is a wrapper for `defvar' that supplies a default for the
- INITVALUE and DOCSTRING arguments."
- `(defvar ,symbol ,initvalue
- ,(or docstring
- "Internal ido variable.
- This variable was originally declared in `ido.el' without an
- initial value or docstring. The documentation you're reading
- comes from re-declaring it in `ido-completing-read+.el' in order
- to suppress some byte-compilation warnings. Setting another
- package's variable is not safe in general, but in this case it
- should be, because ido always let-binds this variable before
- using it, so the initial value shouldn't matter.")))
- (define-ido-internal-var ido-context-switch-command)
- (define-ido-internal-var ido-cur-list)
- (define-ido-internal-var ido-require-match)
- ;;;###autoload
- (defvar ido-cr+-minibuffer-depth -1
- "Minibuffer depth of the most recent ido-cr+ activation.
- If this equals the current minibuffer depth, then the minibuffer
- is currently being used by ido-cr+, and ido-cr+ feature will be
- active. Otherwise, something else is using the minibuffer and
- ido-cr+ features will be deactivated to avoid interfering with
- the other command.
- This is set to -1 by default, since `(minibuffer-depth)' should
- never return this value.")
- (defvar ido-cr+-assume-static-collection nil
- "If non-nil, ido-cr+ will assume that the collection is static.
- This is used to avoid unnecessary work in the case where the
- collection is a function, since a function collection could
- potentially change the set of completion candidates
- dynamically.")
- (defvar ido-cr+-current-command nil
- "Command most recently invoked by `call-interactively'.
- This is necessary because `command-execute' and
- `call-interactively' do not set `this-command'. Instead, the C
- code that calls `command-execute' sets it beforehand, so using
- either of those functions directly won't set `this-command'.")
- (defvar ido-cr+-dynamic-collection nil
- "Stores the collection argument if it is a function.
- This allows ido-cr+ to update the set of completion candidates
- dynamically.")
- (defvar ido-cr+-previous-dynamic-update-texts nil
- "Values of `ido-text' for the last few dynamic collection updates.
- This is used in `ido-cr+-update-dynamic-collection' as an LRU
- cache of recent values of `ido-text' in order to skip re-checking
- prefixes of these strings.")
- (defvar ido-cr+-dynamic-update-idle-time 0.25
- "Time to wait before updating dynamic completion list.")
- (defvar ido-cr+-dynamic-update-timer nil
- "Idle timer for updating dynamic completion list.")
- (defvar ido-cr+-exhibit-pending nil
- "This is non-nil after calling `ido-tidy' until the next call to `ido-exhibit'.
- Typically this is non-nil while any command is running and nil at all
- other times, since those two functions are in `pre-command-hook'
- and `post-command-hook' respectively. In particular, this will
- generally be nil while running an idle timer.")
- (make-obsolete-variable
- 'ido-cr+-no-default-action
- " This variable no longer has any effect. Customize `ido-cr+-nil-def-alternate-behavior-list' instead."
- "4.2")
- (defvar ido-cr+-orig-completing-read-args nil
- "Original arguments passed to `ido-completing-read+'.
- These are used for falling back to `completing-read-default'.")
- (defgroup ido-completing-read-plus nil
- "Extra features and compatibility for `ido-completing-read'."
- :group 'ido)
- (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
- ;; Current ido-ubiquitous function
- completing-read-ido-ubiquitous
- ;; Old ido-ubiquitous functions that shouldn't be used
- completing-read-ido
- ido-ubiquitous-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 incompatible 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-plus)
- (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 30000
- :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-plus)
- (defcustom ido-cr+-function-blacklist
- '(read-file-name-internal
- read-buffer
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/60
- todo-add-category
- ;; Gnus already supports ido on its own
- gnus-emacs-completing-read
- gnus-iswitchb-completing-read
- grep-read-files
- ;; Magit already supports ido on its own
- magit-builtin-completing-read
- ;; ESS already supports ido on its own
- ess-completing-read
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/39
- Info-read-node-name
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/44
- tmm-prompt)
- "Functions & commands for which ido-cr+ should be disabled.
- Each entry can be either a symbol or a string. A symbol means to
- fall back specifically for the named function. A regular
- expression means to fall back for any function whose name matches
- that regular expression. When ido-cr+ is called through
- `completing-read', if any function in the call stack of the
- current command matches any of the blacklist entries, ido-cr+
- will be disabled for that command. Additionally, if the
- collection in the call to `completing-read' matches any of the
- blacklist entries, ido-cr+ will be disabled.
- Note that using specific function names is generally preferable
- to regular expressions, because the associated function
- definitions will be compared directly, so if the same function is
- called by another name, it should still trigger the fallback. For
- regular expressions, only name-based matching is possible."
- :group 'ido-completing-read-plus
- :type '(repeat (choice (symbol :tag "Function or command name")
- (string :tag "Regexp"))))
- (defcustom ido-cr+-function-whitelist
- nil
- "Functions & commands for which ido-cr+ should be enabled.
- If this variable is nil, the whitelist will not be used, and
- ido-cr+ will be allowed in all functions/commands not listed in
- `ido-cr+-function-backlist'.
- If this variable is non-nil, ido-cr+'s whitelisting mode will be
- enabled, and ido-cr+ will be disabled for *all* functions unless
- they match one of the entries. Matching is done in the same
- manner as `ido-cr+-function-blacklist', and blacklisting takes
- precedence over whitelisting."
- :group 'ido-completing-read-plus
- :type '(repeat (choice (symbol :tag "Function or command name")
- (string :tag "Regexp"))))
- (defcustom ido-cr+-nil-def-alternate-behavior-list
- '("\\`describe-\\(function\\|variable\\)\\'"
- "\\`wl-"
- ;; https://github.com/mrkkrp/ebal/issues/12
- "\\`ebal-"
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/4
- webjump
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/83
- where-is
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/51
- find-tag
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/89
- "\\`etags-select-"
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/58
- imenu--completion-buffer
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/116
- project--completing-read-strict
- ;; https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues/127#issuecomment-319463217
- bookmark-completing-read
- )
- "Functions & commands with alternate behavior when DEF is nil.
- This variable has the same format as
- `ido-cr+-function-blacklist'. When `ido-completing-read+` is
- called through `completing-read' by/with any command, function,
- or collection matched by entries in this list, it will behave
- differently when DEF is nil. Instead of using the empty string as
- the default value, it will use the first element of COLLECTION.
- This is needed for optimal compatibility with commands written
- under the assumption that REQUIRE-MATCH means that a match is
- required."
- :group 'ido-completing-read-plus
- :type '(repeat (choice (symbol :tag "Function or command name")
- (string :tag "Regexp"))))
- (defvaralias 'ido-cr+-nil-def-wall-of-shame 'ido-cr+-nil-def-alternate-behavior-list
- "Functions and commands whose authors need to read the docstring for `completing-read'.
- Many functions that call `completing-read' are written with the
- assumption that the setting the REQUIRE-MATCH argument of
- `completing-read' to t means it is required to return a match.
- While that would make logical sense, it's wrong. the docstring
- for `completing-read' describes the correct behavior.
- > If the input is null, ‘completing-read’ returns DEF, or the
- > first element of the list of default values, or an empty string
- > if DEF is nil, regardless of the value of REQUIRE-MATCH.
- This can be avoided by passing an element of COLLECTION as DEF
- instead of leaving it as nil.")
- ;;;###autoload
- (defcustom ido-cr+-replace-completely nil
- "If non-nil, replace `ido-completeing-read' completely with ido-cr+.
- Enabling this may interfere with or cause errors in other
- packages that use `ido-completing-read'. If you discover any such
- incompatibilities, please file a bug report at
- https://github.com/DarwinAwardWinner/ido-completing-read-plus/issues"
- :type 'boolean)
- ;; Signal used to trigger fallback
- (define-error 'ido-cr+-fallback "ido-cr+-fallback")
- (defsubst ido-cr+--explain-fallback (arg)
- ;; This function accepts a string, or an ido-cr+-fallback
- ;; signal.
- (when ido-cr+-debug-mode
- (when (and (listp arg)
- (eq (car arg) 'ido-cr+-fallback))
- (setq arg (cadr arg)))
- (ido-cr+--debug-message "Falling back to `%s' because %s."
- ido-cr+-fallback-function arg)))
- ;;;###autoload
- (defsubst ido-cr+-active ()
- "Returns non-nil if ido-cr+ is currently using the minibuffer."
- (>= ido-cr+-minibuffer-depth (minibuffer-depth)))
- (defsubst ido-cr+-default-was-provided ()
- "Returns non-nil if ido-cr+ was passed a non-nil default argument."
- (and (nth 6 ido-cr+-orig-completing-read-args)))
- (defun ido-cr+--called-from-completing-read ()
- "Returns non-nil if the most recent call to ido-cr+ was from `completing-read'."
- (equal (cadr (backtrace-frame 1 'ido-completing-read+))
- 'completing-read))
- (defmacro ido-cr+-function-is-in-list (fun fun-list &optional list-name)
- "Return non-nil if FUN matches an entry in FUN-LIST.
- This is used to check for matches to `ido-cr+-function-blacklist'
- and `ido-cr+-function-whitelist'. Read those docstrings to see
- how the matching is done.
- This is declared as macro only in order to extract the variable
- name used for the second argument so it can be used in a debug
- message. It should be called as if it were a normal function."
- (when (null list-name)
- (if (symbolp fun-list)
- (setq list-name (symbol-name fun-list))
- (setq list-name "list")))
- `(cl-loop
- for entry in ,fun-list
- if (cond
- ;; Nil: Never matches anything
- ((null entry)
- nil)
- ;; Symbol: Compare names and function definitions
- ((symbolp entry)
- (or (eq entry ,fun)
- (let ((entry-def (ignore-errors (indirect-function entry)))
- (fun-def (ignore-errors (indirect-function ,fun))))
- (and
- fun-def entry-def
- (eq
- (indirect-function entry-def)
- (indirect-function fun-def))))))
- ;; String: Do regexp matching against function name if it is a
- ;; symbol
- ((stringp entry)
- (and (symbolp ,fun)
- (string-match-p entry (symbol-name ,fun))))
- ;; Anything else: invalid blacklist entry
- (t
- (ido-cr+--debug-message "Ignoring invalid entry in %s: `%S'" ,list-name entry)
- nil))
- return entry
- ;; If no blacklist entry matches, return nil
- finally return nil))
- (defun ido-cr+-function-is-blacklisted (fun)
- "Return non-nil if FUN is blacklisted.
- See `ido-cr+-function-blacklist'."
- (ido-cr+-function-is-in-list fun ido-cr+-function-blacklist))
- (defun ido-cr+-function-is-whitelisted (fun)
- "Return non-nil if FUN is whitelisted.
- See `ido-cr+-function-whitelist'."
- (or (null ido-cr+-function-whitelist)
- (ido-cr+-function-is-in-list fun ido-cr+-function-whitelist)))
- ;;;###autoload
- (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
- (ido-cr+-orig-completing-read-args
- (list prompt collection predicate require-match
- initial-input hist def inherit-input-method))
- ;; Need to save this since activating the minibuffer once will
- ;; clear out any temporary minibuffer hooks, which need to get
- ;; restored before falling back.
- (orig-minibuffer-setup-hook minibuffer-setup-hook)
- ;; Need just the string part of INITIAL-INPUT
- (initial-input-string
- (cond
- ((consp initial-input)
- (car initial-input))
- ((stringp initial-input)
- initial-input)
- ((null initial-input)
- "")
- (t
- (signal 'wrong-type-argument (list 'stringp initial-input)))))
- ;; If collection is a function, save it for later, unless
- ;; instructed not to
- (ido-cr+-dynamic-collection
- (when (and (not ido-cr+-assume-static-collection)
- (functionp collection))
- collection))
- ;; If the whitelist is empty, everything is whitelisted
- (whitelisted (not ido-cr+-function-whitelist))
- ;; If non-nil, we need alternate nil DEF handling
- (alt-nil-def nil))
- (condition-case sig
- (progn
- ;; Check a bunch of fallback conditions
- (when inherit-input-method
- (signal 'ido-cr+-fallback
- '("ido cannot handle non-nil INHERIT-INPUT-METHOD")))
- (when (bound-and-true-p completion-extra-properties)
- (signal 'ido-cr+-fallback
- '("ido cannot handle non-nil `completion-extra-properties'")))
- ;; Check for black/white-listed collection function
- (when (functionp collection)
- ;; Blacklist
- (when (ido-cr+-function-is-blacklisted collection)
- (if (symbolp collection)
- (signal 'ido-cr+-fallback
- (list (format "collection function `%S' is blacklisted" collection)))
- (signal 'ido-cr+-fallback
- (list "collection function is blacklisted"))))
- ;; Whitelist
- (when (and (not whitelisted)
- (ido-cr+-function-is-whitelisted collection))
- (ido-cr+--debug-message
- (if (symbolp collection)
- (format "Collection function `%S' is whitelisted" collection)
- "Collection function is whitelisted"))
- (setq whitelisted t))
- ;; nil DEF list
- (when (and
- require-match (null def)
- (ido-cr+-function-is-in-list
- collection
- ido-cr+-nil-def-alternate-behavior-list))
- (ido-cr+--debug-message
- (if (symbolp collection)
- (format "Using alternate nil DEF handling for collection function `%S'" collection)
- "Using alternate nil DEF handling for collection function"))
- (setq alt-nil-def t)))
- ;; Expand all currently-known completions.
- (setq collection
- (if ido-cr+-assume-static-collection
- (all-completions "" collection predicate)
- (ido-cr+-all-dynamic-completions
- initial-input-string collection predicate)))
- ;; No point in using ido unless there's a collection
- (when (and (= (length collection) 0)
- (not ido-cr+-dynamic-collection))
- (signal 'ido-cr+-fallback '("ido is not needed for an empty collection")))
- ;; Check for excessively large collection
- (when (and ido-cr+-max-items
- (> (length collection) ido-cr+-max-items))
- (signal 'ido-cr+-fallback
- (list
- (format
- "there are more than %i items in COLLECTION (see `ido-cr+-max-items')"
- ido-cr+-max-items))))
- ;; If called from `completing-read', check for
- ;; black/white-listed commands/callers
- (when (ido-cr+--called-from-completing-read)
- ;; Check calling command and `ido-cr+-current-command'
- (cl-loop
- for cmd in (list this-command ido-cr+-current-command)
- if (ido-cr+-function-is-blacklisted cmd)
- do (signal 'ido-cr+-fallback
- (list "calling command `%S' is blacklisted" cmd))
- if (and (not whitelisted)
- (ido-cr+-function-is-whitelisted cmd))
- do (progn
- (ido-cr+--debug-message "Command `%S' is whitelisted" cmd)
- (setq whitelisted t))
- if (and
- require-match (null def) (not alt-nil-def)
- (ido-cr+-function-is-in-list
- cmd ido-cr+-nil-def-alternate-behavior-list))
- do (progn
- (ido-cr+--debug-message
- "Using alternate nil DEF handling for command `%S'" cmd)
- (setq alt-nil-def t)))
- ;; Check every function in the call stack starting after
- ;; `completing-read' until to the first
- ;; `funcall-interactively' (for a call from the function
- ;; body) or `call-interactively' (for a call from the
- ;; interactive form, in which the function hasn't actually
- ;; been called yet, so `funcall-interactively' won't be on
- ;; the stack.)
- (cl-loop for i upfrom 1
- for caller = (cadr (backtrace-frame i 'completing-read))
- while caller
- while (not (memq (indirect-function caller)
- '(internal--funcall-interactively
- (indirect-function 'call-interactively))))
- if (ido-cr+-function-is-blacklisted caller)
- do (signal 'ido-cr+-fallback
- (list (if (symbolp caller)
- (format "calling function `%S' is blacklisted" caller)
- "a calling function is blacklisted")))
- if (and (not whitelisted)
- (ido-cr+-function-is-whitelisted caller))
- do (progn
- (ido-cr+--debug-message
- (if (symbolp caller)
- (format "Calling function `%S' is whitelisted" caller)
- "A calling function is whitelisted"))
- (setq whitelisted t))
- if (and require-match (null def) (not alt-nil-def)
- (ido-cr+-function-is-in-list
- caller ido-cr+-nil-def-alternate-behavior-list))
- do (progn
- (ido-cr+--debug-message
- (if (symbolp caller)
- (format "Using alternate nil DEF handling for calling function `%S'" caller)
- "Using alternate nil DEF handling for a calling function"))
- (setq alt-nil-def t))))
- (unless whitelisted
- (signal 'ido-cr+-fallback
- (list "no functions or commands matched the whitelist for this call")))
- (when (and require-match (null def))
- ;; Replace nil with "" for DEF if match is required, unless
- ;; alternate nil DEF handling is enabled
- (if alt-nil-def
- (ido-cr+--debug-message
- "Leaving the default at nil because alternate nil DEF handling is enabled.")
- (ido-cr+--debug-message
- "Adding \"\" as the default completion since no default was provided.")
- (setq def (list ""))))
- ;; In ido, the semantics of "default" are simply "put it at
- ;; the front of the list". Furthermore, ido can't handle a
- ;; list of defaults, nor can it handle both DEF and
- ;; INITIAL-INPUT being non-nil. So, just pre-process the
- ;; collection to put the default(s) at the front and then
- ;; set DEF to nil in the call to ido to avoid these issues.
- (unless (listp def)
- ;; Ensure DEF is a list
- (setq def (list def)))
- (when def
- ;; Ensure DEF are strings
- (setq def (mapcar (apply-partially #'format "%s") def))
- ;; Prepend DEF to COLLECTION and remove duplicates
- (setq collection (delete-dups (append def collection)))
- def nil)
- ;; Check for a specific bug
- (when (and ido-enable-dot-prefix
- (version< emacs-version "26.1")
- (member "" collection))
- (signal 'ido-cr+-fallback
- '("ido cannot handle the empty string as an option when `ido-enable-dot-prefix' is non-nil; see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997")))
- ;; Fix ido handling of cons-style INITIAL-INPUT. TODO add a
- ;; version check after this bug is fixed:
- ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27807
- (when (consp initial-input)
- ;; `completing-read' uses 0-based index while
- ;; `read-from-minibuffer' uses 1-based index.
- (cl-incf (cdr initial-input)))
- ;; Finally ready to do actual ido completion
- (prog1
- (let ((ido-cr+-minibuffer-depth (1+ (minibuffer-depth)))
- ;; Initialize dynamic update vars
- (ido-cr+-previous-dynamic-update-texts
- (list initial-input-string))
- (ido-cr+-dynamic-update-timer nil)
- (ido-cr+-exhibit-pending t)
- ;; Reset these for the next call to ido-cr+
- (ido-cr+-no-default-action 'prepend-empty-string)
- (ido-cr+-assume-static-collection nil))
- (unwind-protect
- (ido-completing-read
- prompt collection
- predicate require-match initial-input hist def
- inherit-input-method)
- (when ido-cr+-dynamic-update-timer
- (cancel-timer ido-cr+-dynamic-update-timer)
- (setq ido-cr+-dynamic-update-timer nil))))
- ;; This detects when the user triggered fallback mode
- ;; manually.
- (when (eq ido-exit 'fallback)
- (signal 'ido-cr+-fallback '("user manually triggered fallback")))))
- ;; Handler for ido-cr+-fallback signal
- (ido-cr+-fallback
- (let (;; Reset `minibuffer-setup-hook' to original value
- (minibuffer-setup-hook orig-minibuffer-setup-hook)
- ;; Reset these for the next call to ido-cr+
- (ido-cr+-no-default-action 'prepend-empty-string)
- (ido-cr+-assume-static-collection nil))
- (ido-cr+--explain-fallback sig)
- (apply ido-cr+-fallback-function ido-cr+-orig-completing-read-args))))))
- ;;;###autoload
- (defun ido-completing-read@ido-cr+-replace (orig-fun &rest args)
- "This advice allows ido-cr+ to completely replace `ido-completing-read'.
- See the varaible `ido-cr+-replace-completely' for more information."
- ;; If this advice is autoloaded, then we need to force loading of
- ;; the rest of the file so all the variables will be defined.
- (when (not (featurep 'ido-completing-read+))
- (require 'ido-completing-read+))
- (if (or (ido-cr+-active)
- (not ido-cr+-replace-completely))
- ;; ido-cr+ has either already activated or isn't going to
- ;; activate, so just run the function as normal
- (apply orig-fun args)
- ;; Otherwise, we need to activate ido-cr+.
- (apply #'ido-completing-read+ args)))
- ;;;###autoload
- (advice-add 'ido-completing-read :around
- #'ido-completing-read@ido-cr+-replace)
- ;;;###autoload
- (defun call-interactively@ido-cr+-record-current-command
- (orig-fun command &rest args)
- "Let-bind the command being interactively called.
- See `ido-cr+-current-command' for more information."
- (let ((ido-cr+-current-command command))
- (apply orig-fun command args)))
- ;;;###autoload
- (advice-add 'call-interactively :around
- #'call-interactively@ido-cr+-record-current-command)
- ;; Fallback on magic C-f and C-b
- (defun ido-magic-forward-char@ido-cr+-fallback (&rest args)
- "Allow falling back in ido-completing-read+."
- (when (ido-cr+-active)
- ;; `ido-context-switch-command' is already let-bound at this
- ;; point.
- (setq ido-context-switch-command #'ido-fallback-command)))
- (advice-add 'ido-magic-forward-char :before
- #'ido-magic-forward-char@ido-cr+-fallback)
- (defun ido-magic-backward-char@ido-cr+-fallback (&rest args)
- "Allow falling back in ido-completing-read+."
- (when (ido-cr+-active)
- ;; `ido-context-switch-command' is already let-bound at this
- ;; point.
- (setq ido-context-switch-command #'ido-fallback-command)))
- (advice-add 'ido-magic-backward-char :before
- #'ido-magic-backward-char@ido-cr+-fallback)
- (defun ido-select-text@ido-cr+-fix-require-match (orig-fun &rest args)
- "Fix ido behavior when `require-match' is non-nil.
- Standard ido will allow C-j to exit with an incomplete completion
- even when `require-match' is non-nil. Ordinary completion does
- not allow this. In ordinary completion, RET on an incomplete
- match is equivalent to TAB, and C-j selects the first match.
- Since RET in ido already selects the first match, this advice
- sets up C-j to be equivalent to TAB in the same situation.
- This advice only activates if the current ido completion was
- called through ido-cr+."
- (if (and
- ;; Only override C-j behavior if...
- ;; We're using ico-cr+, and...
- (ido-cr+-active)
- ;; Require-match is non-nil, and...
- ido-require-match
- ;; The current input doesn't exactly match a known option, and...
- (not (member ido-text ido-cur-list))
- ;; The current input doesn't exactly match an option according
- ;; to `try-completion' (or the collection is not dynamic).
- (or (not ido-cr+-dynamic-collection)
- (eq t (try-completion ido-text ido-cr+-dynamic-collection
- (nth 2 ido-cr+-orig-completing-read-args)))))
- (progn
- (ido-cr+--debug-message
- "Overriding C-j behavior for require-match: performing completion instead of exiting with current text. (This might still exit with a match if `ido-confirm-unique-completion' is nil)")
- (ido-complete))
- (apply orig-fun args)))
- (advice-add 'ido-select-text :around
- #'ido-select-text@ido-cr+-fix-require-match)
- (defun ido-tidy@ido-cr+-set-exhibit-pending (&rest args)
- (setq ido-cr+-exhibit-pending t))
- (advice-add 'ido-tidy :after 'ido-tidy@ido-cr+-set-exhibit-pending)
- (defun ido-exhibit@ido-cr+-clear-exhibit-pending (&rest args)
- (setq ido-cr+-exhibit-pending nil))
- (advice-add 'ido-exhibit :before 'ido-exhibit@ido-cr+-clear-exhibit-pending)
- (cl-defun ido-cr+-all-dynamic-completions
- (string collection &optional predicate
- &key prev-string (rmdups t))
- "Run `all-completions' on every prefix of STRING.
- Arguments COLLECTION and PREDICATE are as in `all-completions'.
- Note that \"all prefixes\" includes both STRING itself and the
- empty string.
- If keyword argument RMDUPS is non-nil, call `delete-dups' on the
- result before returning. This is the default. You can pass nil
- for this argument if the caller is already going to do its own
- duplicate removal.
- As an optimization, if keyword argument PREV-STRING is non-nil,
- then prefixes of STRING that are also prefixes of PREV-STRING
- will be skipped. This is used to avoid duplicating work if the
- caller already knows about the completions for PREV-STRING.
- PREV-STRING can also be a list of previous strings, in which case
- all prefixes of all previous strings will be skipped. In
- particular, note that if PREV-STRING equals STRING, this function
- will return nil.
- This function is only useful if COLLECTION is a function that
- might return additional completions for certain non-empty strings
- that it wouldn't return for the empty string. If COLLECTION is
- not a function, this is equivalent to
- `(all-completions \"\" COLELCTION PREDICATE)'."
- (cond
- ;; Dynamic collection.
- ((functionp collection)
- (let ((prev-strings (if (listp prev-string)
- prev-string
- (list prev-string)))
- (common-prefix-length -1))
- ;; Get the length of the longest common prefix, or -1 if no
- ;; previous strings.
- (cl-loop for ps in prev-strings
- for common-prefix = (s-shared-start ps string)
- maximize (length common-prefix) into prefix-length
- finally do (setq common-prefix-length
- (or prefix-length -1)))
- ;; Get completions for all prefixes starting after the longest
- ;; previous prefix, or starting from "" if no previous prefix.
- (cl-loop
- with start-index = (1+ common-prefix-length)
- ;; This might execute zero times, if common-prefix = string
- for i from start-index upto (length string)
- append (all-completions
- (s-left i string)
- collection
- predicate)
- into completion-list
- finally return (when completion-list
- (funcall
- (if rmdups #'delete-dups #'identity)
- completion-list)))))
- ;; If COLLECTION is not a function and PREV-STRING is non-nil, then
- ;; the caller already has all the possible completions, so return
- ;; nil.
- (prev-string
- nil)
- ;; Otherwise, just call `all-completions' on the empty string to
- ;; get every possible completions for a static COLLECTION.
- (t
- (unless prev-string
- (all-completions "" collection predicate)))))
- (defun ido-cr+-update-dynamic-collection ()
- "Update the set of completions for a dynamic collection.
- This has no effect unless `ido-cr+-dynamic-collection' is non-nil."
- (when (and (ido-cr+-active)
- ido-cr+-dynamic-collection)
- (let* ((ido-text
- (buffer-substring-no-properties (minibuffer-prompt-end)
- ido-eoinput))
- (predicate (nth 2 ido-cr+-orig-completing-read-args))
- (first-match (car ido-matches))
- (remembered-new-string nil)
- (strings-to-check
- ;; If `ido-text' is a prefix of `first-match', then we
- ;; only need to check the latter, because that will
- ;; implicitly check the former as well.
- (cond
- ((null first-match)
- (list ido-text))
- ((and first-match
- (s-prefix? ido-text first-match))
- (list first-match))
- (t
- (list ido-text first-match))))
- (new-completions
- (cl-loop
- with checked-strings = '()
- for string in strings-to-check
- nconc
- (ido-cr+-all-dynamic-completions
- string ido-cr+-dynamic-collection predicate
- :rmdups nil
- :prev-string (append checked-strings
- ido-cr+-previous-dynamic-update-texts))
- into result
- collect string into checked-strings
- finally return result)))
- (when new-completions
- ;; Merge new completions into `ido-cur-list'
- (setq
- ido-cur-list
- (delete-dups (nconc ido-cur-list new-completions)))
- ;; Ensure that the currently-selected match is still at the head
- ;; of the list
- (let ((current-match (car ido-matches)))
- (when (and current-match (member current-match ido-cur-list))
- (setq ido-cur-list (ido-chop ido-cur-list current-match))))
- (ido-cr+--debug-message
- "Updated completion candidates for dynamic collection because `ido-text' changed to %S. `ido-cur-list' now has %s elements"
- ido-text (length ido-cur-list))
- ;; Recompute matches with new completions
- (setq ido-rescan t)
- (ido-set-matches)
- ;; Rebuild the completion display unless ido is already planning
- ;; to do it anyway
- (unless ido-cr+-exhibit-pending
- (ido-tidy)
- (ido-exhibit)))
- ;; Add `ido-text' and/or `first-match' to the list of remembered
- ;; previous update texts. This is used to avoid re-computing
- ;; completions on previously-seen string prefixes (since those
- ;; completions have already been added to `ido-cur-list')
- (cl-loop
- for new-text in strings-to-check
- do
- (cond
- ;; Common case optimization: if eitehr new element or first
- ;; element of list is a prefix of the other, just keep the
- ;; longer one.
- ((s-prefix? new-text (car ido-cr+-previous-dynamic-update-texts))
- nil)
- ((s-prefix? (car ido-cr+-previous-dynamic-update-texts) new-text)
- (setf (car ido-cr+-previous-dynamic-update-texts) new-text))
- ;; General case: just prepend it to the list
- (t
- (setq remembered-new-string t)
- (push new-text ido-cr+-previous-dynamic-update-texts))))
- ;; Remove duplicates and trim the list down to the last 5
- ;; remembered texts
- (when remembered-new-string
- (setq
- ido-cr+-previous-dynamic-update-texts
- ;; Elisp doesn't seem to have a "take first N elements"
- ;; function that returns the entire list if it's shorter than
- ;; N instead of signaling an error
- (cl-loop
- with result = '()
- with n-taken = 0
- for item in ido-cr+-previous-dynamic-update-texts
- if (not (member item result))
- collect item into result and
- sum 1 into n-taken
- if (>= n-taken 5)
- return result
- finally return result))))))
- ;; Always cancel an active timer when this function is called.
- (when ido-cr+-dynamic-update-timer
- (cancel-timer ido-cr+-dynamic-update-timer)
- (setq ido-cr+-dynamic-update-timer nil))
- (defun ido-cr+-schedule-dynamic-collection-update ()
- "Schedule a dynamic collection update for now or in the future."
- (when (and (ido-cr+-active)
- ido-cr+-dynamic-collection)
- ;; Cancel the previous timer
- (when ido-cr+-dynamic-update-timer
- (cancel-timer ido-cr+-dynamic-update-timer)
- (setq ido-cr+-dynamic-update-timer nil))
- (if (<= (length ido-matches) 1)
- ;; If we've narrowed it down to zero or one matches, update
- ;; immediately.
- (ido-cr+-update-dynamic-collection)
- ;; If there are still several choices, defer update until idle
- (setq ido-cr+-dynamic-update-timer
- (run-with-idle-timer (max 0.01 ido-cr+-dynamic-update-idle-time) nil
- #'ido-cr+-update-dynamic-collection)))))
- (defun ido-cr+-minibuffer-setup ()
- "set up minibuffer `post-command-hook' for ido-cr+ "
- (when (ido-cr+-active)
- (add-hook 'post-command-hook
- 'ido-cr+-schedule-dynamic-collection-update)))
- (add-hook 'ido-minibuffer-setup-hook
- 'ido-cr+-minibuffer-setup)
- ;; (defadvice ido-complete (around dynamic activate)
- ;; (let ((ido-confirm-unique-completion
- ;; (if ido-cr+-dynamic-collection
- ;; t
- ;; ido-confirm-unique-completion)))
- ;; ad-do-it))
- ;; Also need to update dynamic collections on TAB, and do so *before*
- ;; deciding to exit based on `ido-confirm-unique-completion'
- (defun ido-complete@ido-cr+-update-dynamic-collection (oldfun &rest args)
- "Maybe update the set of completions when pressing TAB."
- (when ido-cr+-dynamic-collection
- ;; First run with `ido-confirm-unique-completion' non-nil so it
- ;; can't exit
- (let ((ido-confirm-unique-completion t))
- (apply oldfun args))
- ;; Update `ido-eoinput'
- (setq ido-eoinput (point-max))
- ;; Now do update
- (ido-cr+-update-dynamic-collection))
- ;; After maybe updating the dynamic collection, if there's still
- ;; only one completion, now it's allowed to exit
- (apply oldfun args))
- (advice-add 'ido-complete :around 'ido-complete@ido-cr+-update-dynamic-collection)
- ;; Interoperation with minibuffer-electric-default-mode: only show the
- ;; default when the input is empty and the empty string is the
- ;; selected choice
- (defun minibuf-eldef-update-minibuffer@ido-cr+-compat (orig-fun &rest args)
- "This advice allows minibuffer-electric-default-mode to work with ido-cr+."
- (if (ido-cr+-active)
- (unless (eq minibuf-eldef-showing-default-in-prompt
- (and (string= (car ido-cur-list) "")
- (string= ido-text "")))
- ;; Swap state.
- (setq minibuf-eldef-showing-default-in-prompt
- (not minibuf-eldef-showing-default-in-prompt))
- (overlay-put minibuf-eldef-overlay 'invisible
- (not minibuf-eldef-showing-default-in-prompt)))
- (apply orig-fun args)))
- (advice-add 'minibuf-eldef-update-minibuffer :around
- #'minibuf-eldef-update-minibuffer@ido-cr+-compat)
- ;;;###autoload
- (define-minor-mode ido-ubiquitous-mode
- "Use ido completion instead of standard completion almost everywhere.
- If this mode causes problems for a function, you can customize
- when ido completion is or is not used by customizing
- `ido-cr+-function-blacklist'."
- nil
- :global t
- :group 'ido-completing-read-plus
- ;; Actually enable/disable the mode by setting
- ;; `completing-read-function'.
- (setq completing-read-function
- (if ido-ubiquitous-mode
- #'ido-completing-read+
- ido-cr+-fallback-function)))
- (defcustom ido-cr+-auto-update-blacklist 'notify
- "Whether to add new overrides when updating ido-cr+.
- This variable has 3 possible values, with the following meanings:
- `t': Auto-update the blacklist
- `notify': Notify you about updates but do not apply them
- `nil': Ignore all blacklist updates
- Ido-cr+ comes with a default blacklist for commands that are
- known to be incompatible with ido completion. New versions of
- ido-cr+ may come with updates to this blacklist as more
- incompatible commands are discovered. However, customizing your
- own overrides would normally prevent you from receiving these
- updates, since Emacs will not overwrite your customizations.
- To resolve this problem, you can set this variable to `t', and
- then ido-cr+ can automatically add any new built-in overrides
- whenever it is updated. (Actually, the update will happen the
- next time Emacs is restarted after the update.) This allows you
- to add your own overrides but still receive updates to the
- default set.
- If you want ido-cr+ to just notify you about new default
- overrides instead of adding them itself, set this variable to
- `notify'. If you don't want this auto-update behavior at all, set
- it to `nil'.
- (Note that having this option enabled effectively prevents you
- from removing any of the built-in default blacklist entries,
- since they will simply be re-added the next time Emacs starts.)"
- :type '(choice :tag "When new overrides are available:"
- (const :menu-tag "Auto-add"
- :tag "Add them automatically"
- t)
- (const :menu-tag "Notify"
- :tag "Notify me about them"
- notify)
- (const :menu-tag "Ignore"
- :tag "Ignore them"
- nil))
- :group 'ido-completing-read-plus)
- (defun ido-cr+-update-blacklist (&optional save quiet)
- "Re-add any missing default blacklist entries.
- This is useful after an update of ido-ubiquitous that adds new
- default overrides. See `ido-cr+-auto-update-blacklist' for more
- information.
- If SAVE is non-nil, also save the new blacklist to the user's
- Custom file (but only if it was already customized beforehand).
- When called interactively, a prefix argument triggers a save.
- When called from Lisp code, this function returns non-nil if the
- blacklist was modified."
- (interactive "P")
- (let* ((var-state (custom-variable-state 'ido-cr+-function-blacklist
- ido-cr+-function-blacklist))
- (curval ido-cr+-function-blacklist)
- (defval (eval (car (get 'ido-cr+-function-blacklist 'standard-value))))
- (newval (delete-dups (append defval curval)))
- (new-entries (cl-set-difference defval curval :test #'equal))
- (modified nil)
- (saved nil)
- (message-lines ()))
- (cl-case var-state
- (standard
- ;; Var is not customized, just set the new default
- (ido-cr+--debug-message "Blacklist was not customized, so it has been updated to the new default value.")
- (setq ido-cr+-function-blacklist defval
- modified new-entries))
- ((saved set changed)
- ;; Var has been customized and saved by the user, so set the
- ;; new value and maybe save it
- (ido-cr+--debug-message "Updating user-customized blacklist with new default entries.")
- (setq ido-cr+-function-blacklist newval
- modified t)
- (when (and save (eq var-state 'saved))
- (ido-cr+--debug-message "Saving new blacklist value to Custom file.")
- (customize-save-variable 'ido-cr+-function-blacklist ido-cr+-function-blacklist)
- (setq saved t)))
- (otherwise
- (ido-cr+--debug-message "Customization status of blacklist is unknown. Not modifying it.")))
- (if (and modified (not quiet))
- (progn
- (push (format "Added the following entries to `ido-cr+-function-blacklist': %S" new-entries)
- message-lines)
- (if saved
- (push "Saved the new value of `ido-cr+-function-blacklist' to your Custom file."
- message-lines)
- (push "However, the new value of `ido-cr+-function-blacklist' has not yet been saved for future sessions. To save it. re-run this command with a prefix argument: `C-u M-x ido-cr+-update-blacklist'; or else manually inspect and save the value using `M-x customize-variable ido-cr+-function-blacklist'."
- message-lines)))
- (push "No updates were required to `ido-cr+-function-blacklist'." message-lines))
- (unless quiet
- (message (mapconcat #'identity (nreverse message-lines) "\n")))
- modified))
- (defun ido-cr+-maybe-update-blacklist ()
- "Maybe call `ico-cr+-update-blacklist.
- See `ido-cr+-auto-update-blacklist' for more information."
- (if ido-cr+-auto-update-blacklist
- (let* ((curval ido-cr+-function-blacklist)
- (defval (eval (car (get 'ido-cr+-function-blacklist 'standard-value))))
- (new-entries (cl-set-difference curval defval :test #'equal)))
- (if new-entries
- (if (eq ido-cr+-auto-update-blacklist 'notify)
- (display-warning 'ido-completing-read+ "There are %s new blacklist entries available. Use `M-x ido-cr+-update-blacklist' to install them. (See `ido-cr+-auto-update-blacklist' for more information.)")
- (ido-cr+--debug-message "Initiating blacklist update.")
- (ido-cr+-update-blacklist t))
- (ido-cr+--debug-message "No blacklist updates available.")))
- (ido-cr+--debug-message "Skipping blacklist update by user request.")))
- (ido-cr+-maybe-update-blacklist)
- (provide 'ido-completing-read+)
- ;;; ido-completing-read+.el ends here
|