minibuf-eldef.el 7.96 KB
Newer Older
1
;;; minibuf-eldef.el --- Only show defaults in prompts when applicable  -*- lexical-binding: t -*-
Miles Bader's avatar
Miles Bader committed
2
;;
3
;; Copyright (C) 2000-2012 Free Software Foundation, Inc.
Miles Bader's avatar
Miles Bader committed
4 5 6 7 8 9
;;
;; Author: Miles Bader <miles@gnu.org>
;; Keywords: convenience

;; This file is part of GNU Emacs.

10
;; GNU Emacs is free software: you can redistribute it and/or modify
Miles Bader's avatar
Miles Bader committed
11
;; it under the terms of the GNU General Public License as published by
12 13
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Miles Bader's avatar
Miles Bader committed
14 15 16 17 18 19 20

;; 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
21
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
Miles Bader's avatar
Miles Bader committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35

;;; Commentary:
;;
;; Defines the mode `minibuffer-electric-default-mode'.
;;
;; When active, minibuffer prompts that show a default value only show
;; the default when it's applicable -- that is, when hitting RET would
;; yield the default value.  If the user modifies the input such that
;; hitting RET would enter a non-default value, the prompt is modified
;; to remove the default indication (which is restored if the input is
;; ever restore to the match the initial input).

;;; Code:

36
(defvar minibuffer-eldef-shorten-default)
37

38
(defun minibuffer-default--in-prompt-regexps ()
39 40
  `(("\\( (default\\(?: is\\)? \\(.*\\))\\):? \\'"
     1 ,(if minibuffer-eldef-shorten-default " [\\2]"))
41 42 43 44 45 46 47 48 49 50 51 52 53 54
    ("\\( \\[.*\\]\\):? *\\'" 1)))

(defcustom minibuffer-eldef-shorten-default nil
  "If non-nil, shorten \"(default ...)\" to \"[...]\" in minibuffer prompts."
  :set (lambda (symbol value)
         (set-default symbol value)
	 (setq-default minibuffer-default-in-prompt-regexps
		       (minibuffer-default--in-prompt-regexps)))
  :type 'boolean
  :group 'minibuffer
  :version "24.3")

(defvar minibuffer-default-in-prompt-regexps
  (minibuffer-default--in-prompt-regexps)
55
  "A list of regexps matching the parts of minibuffer prompts showing defaults.
Miles Bader's avatar
Miles Bader committed
56 57 58
When `minibuffer-electric-default-mode' is active, these regexps are
used to identify the portions of prompts to elide.

59 60 61 62 63
Each entry is of the form (REGEXP MATCH-NUM &optional REWRITE),
where REGEXP should match the default part of the prompt,
MATCH-NUM is the subgroup that matched the actual default indicator,
and REWRITE, if present, is a string to pass to `replace-match' that
should be displayed in its place.")
Miles Bader's avatar
Miles Bader committed
64 65 66 67 68 69


;;; Internal variables

;; A list of minibuffers to which we've added a post-command-hook.
(defvar minibuf-eldef-frobbed-minibufs nil)
70

Miles Bader's avatar
Miles Bader committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
;;; The following are all local variables in the minibuffer

;; Input pre-inserted into the minibuffer before the user can edit it.
(defvar minibuf-eldef-initial-input)
(make-variable-buffer-local 'minibuf-eldef-initial-input)
;; and the length of the buffer with it inserted.
(defvar minibuf-eldef-initial-buffer-length)
(make-variable-buffer-local 'minibuf-eldef-initial-buffer-length)

;; True if the current minibuffer prompt contains the default spec.
(defvar minibuf-eldef-showing-default-in-prompt)
(make-variable-buffer-local 'minibuf-eldef-showing-default-in-prompt)

;; An overlay covering the default portion of the prompt
(defvar minibuf-eldef-overlay)
(make-variable-buffer-local 'minibuf-eldef-overlay)


;;; Hook functions

;; This function goes on minibuffer-setup-hook
(defun minibuf-eldef-setup-minibuffer ()
  "Set up a minibuffer for `minibuffer-electric-default-mode'.
The prompt and initial input should already have been inserted."
Miles Bader's avatar
Miles Bader committed
95
  (let ((regexps minibuffer-default-in-prompt-regexps)
Miles Bader's avatar
Miles Bader committed
96 97 98 99
	(match nil)
	(inhibit-point-motion-hooks t))
    (save-excursion
      (save-restriction
100
	;; Narrow to only the prompt.
Miles Bader's avatar
Miles Bader committed
101
	(goto-char (point-min))
Miles Bader's avatar
Miles Bader committed
102
	(narrow-to-region (point) (minibuffer-prompt-end))
103
	;; See if the prompt contains a default input indicator.
Miles Bader's avatar
Miles Bader committed
104 105
	(while regexps
	  (setq match (pop regexps))
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	  (cond
           ((not (re-search-forward (if (stringp match) match (car match))
                                    nil t))
            ;; No match yet, try the next rule.
	    (setq match nil))
           ((and (consp (cdr-safe match)) (nth 2 match))
            ;; Matched a replacement rule.
            (let* ((inhibit-read-only t)
                   (buffer-undo-list t)
                   (submatch (nth 1 match))
                   (replacement (nth 2 match))
                   (props (text-properties-at (match-beginning submatch))))
              (replace-match replacement nil nil nil submatch)
              (set-text-properties (match-beginning submatch)
                                   (match-end submatch)
                                   props)
              ;; Replacement done, now keep trying with subsequent rules.
              (setq match nil)
              (goto-char (point-min))))
           ;; Matched a non-replacement (i.e. electric hide) rule, no need to
           ;; keep trying.
           (t (setq regexps nil))))))
Miles Bader's avatar
Miles Bader committed
128
    (if (not match)
129 130
	;; No match for electric hiding, so just make sure our
	;; post-command-hook isn't left around.
Miles Bader's avatar
Miles Bader committed
131 132 133 134
	(remove-hook 'post-command-hook #'minibuf-eldef-update-minibuffer t)
      ;; Yup; set things up so we can frob the prompt as the state of
      ;; the input string changes.
      (setq match (if (consp match) (cdr match) 0))
135
      (setq match (if (consp match) (car match) match))
Miles Bader's avatar
Miles Bader committed
136 137 138 139
      (setq minibuf-eldef-overlay
	    (make-overlay (match-beginning match) (match-end match)))
      (setq minibuf-eldef-showing-default-in-prompt t)
      (setq minibuf-eldef-initial-input
Miles Bader's avatar
Miles Bader committed
140
	    (minibuffer-contents-no-properties))
Miles Bader's avatar
Miles Bader committed
141 142 143 144 145 146 147 148 149 150 151 152
      (setq minibuf-eldef-initial-buffer-length (point-max))
      (add-to-list 'minibuf-eldef-frobbed-minibufs (current-buffer))
      (add-hook 'post-command-hook #'minibuf-eldef-update-minibuffer nil t))))

;; post-command-hook to swap prompts when necessary
(defun minibuf-eldef-update-minibuffer ()
  "Update a minibuffer's prompt to include a default only when applicable.
This is intended to be used as a minibuffer post-command-hook for
`minibuffer-electric-default-mode'; the minibuffer should have already
been set up by `minibuf-eldef-setup-minibuffer'."
  (unless (eq minibuf-eldef-showing-default-in-prompt
	      (and (= (point-max) minibuf-eldef-initial-buffer-length)
Miles Bader's avatar
Miles Bader committed
153
		   (string-equal (minibuffer-contents-no-properties)
Miles Bader's avatar
Miles Bader committed
154 155 156 157 158 159 160 161 162 163 164 165 166 167
				 minibuf-eldef-initial-input)))
    ;; swap state
    (setq minibuf-eldef-showing-default-in-prompt
	  (not minibuf-eldef-showing-default-in-prompt))
    (cond (minibuf-eldef-showing-default-in-prompt
	   (overlay-put minibuf-eldef-overlay 'invisible nil)
	   (overlay-put minibuf-eldef-overlay 'intangible nil))
	  (t
	   (overlay-put minibuf-eldef-overlay 'invisible t)
	   (overlay-put minibuf-eldef-overlay 'intangible t)))))


;;;###autoload
(define-minor-mode minibuffer-electric-default-mode
168
  "Toggle Minibuffer Electric Default mode.
169 170 171 172 173 174 175 176 177 178
With a prefix argument ARG, enable Minibuffer Electric Default
mode if ARG is positive, and disable it otherwise.  If called
from Lisp, enable the mode if ARG is omitted or nil.

Minibuffer Electric Default mode is a global minor mode.  When
enabled, minibuffer prompts that show a default value only show
the default when it's applicable -- that is, when hitting RET
would yield the default value.  If the user modifies the input
such that hitting RET would enter a non-default value, the prompt
is modified to remove the default indication."
Miles Bader's avatar
Miles Bader committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
  :global t
  :group 'minibuffer
  (if minibuffer-electric-default-mode
      ;; Enable the mode
      (add-hook 'minibuffer-setup-hook 'minibuf-eldef-setup-minibuffer)
    ;; Disable the mode
    (remove-hook 'minibuffer-setup-hook 'minibuf-eldef-setup-minibuffer)
    ;; Remove our entry from any post-command-hook variable's it's still in
    (dolist (minibuf minibuf-eldef-frobbed-minibufs)
      (with-current-buffer minibuf
	(remove-hook 'post-command-hook #'minibuf-eldef-update-minibuffer t)))
    (setq minibuf-eldef-frobbed-minibufs nil)))


(provide 'minibuf-eldef)

;;; minibuf-eldef.el ends here