Add "Package:" file headers to denote built-in packages.
[bpt/emacs.git] / lisp / rfn-eshadow.el
CommitLineData
519f7ff6
MB
1;;; rfn-eshadow.el --- Highlight `shadowed' part of read-file-name input text
2;;
0d30b337 3;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
114f9c96 4;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
519f7ff6
MB
5;;
6;; Author: Miles Bader <miles@gnu.org>
476cf013 7;; Keywords: convenience minibuffer
bd78fa1d 8;; Package: emacs
519f7ff6
MB
9
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
519f7ff6 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
519f7ff6
MB
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
519f7ff6
MB
24
25;;; Commentary:
26;;
476cf013 27;; Defines the mode `file-name-shadow-mode'.
519f7ff6
MB
28;;
29;; The `read-file-name' function passes its result through
30;; `substitute-in-file-name', so any part of the string preceding
31;; multiple slashes (or a drive indicator on MS-DOS/MS-Windows) is
32;; ignored.
33;;
476cf013 34;; If `file-name-shadow-mode' is active, any part of the
519f7ff6 35;; minibuffer text that would be ignored because of this is given the
476cf013 36;; properties in `file-name-shadow-properties', which may
519f7ff6
MB
37;; be used to make the ignored text invisible, dim, etc.
38;;
39
40;;; Code:
41
42\f
43;;; Customization
44
476cf013 45(defconst file-name-shadow-properties-custom-type
519f7ff6
MB
46 '(list
47 (checklist :inline t
48 (const :tag "Invisible"
49 :doc "Make shadowed part of filename invisible"
50 :format "%t%n%h"
51 :inline t
52 (invisible t intangible t))
53 (list :inline t
54 :format "%v"
55 :tag "Face"
56 :doc "Display shadowed part of filename using a different face"
57 (const :format "" face)
476cf013 58 (face :value file-name-shadow))
519f7ff6
MB
59 (list :inline t
60 :format "%t: %v%h"
61 :tag "Brackets"
62 ;; Note the 4 leading spaces in the doc string;
63 ;; this is hack to get around the fact that the
64 ;; newline after the second string widget comes
65 ;; from the string widget, and doesn't indent
66 ;; correctly. We could use a :size attribute to
67 ;; make the second string widget not have a
68 ;; terminating newline, but this makes it impossible
69 ;; to enter trailing whitespace, and it's desirable
70 ;; that it be possible.
71 :doc " Surround shadowed part of filename with brackets"
72 (const :format "" before-string)
73 (string :format "%v" :size 4 :value "{")
74 (const :format "" after-string)
75 ;; see above about why the 2nd string doesn't use :size
76 (string :format " and: %v" :value "} "))
77 (list :inline t
78 :format "%t: %v%n%h"
79 :tag "String"
80 :doc "Display a string instead of the shadowed part of filename"
81 (const :format "" display)
82 (string :format "%v" :size 15 :value "<...ignored...>"))
83 (const :tag "Avoid"
84 :doc "Try to keep cursor out of shadowed part of filename"
85 :format "%t%n%h"
86 :inline t
87 (field shadow)))
88 (repeat :inline t
89 :tag "Other Properties"
90 (list :inline t
91 :format "%v"
92 (symbol :tag "Property")
93 (sexp :tag "Value")))))
94
476cf013 95(defcustom file-name-shadow-properties
1e8780b1
DN
96 ;; FIXME: should we purecopy this?
97'(face file-name-shadow field shadow)
519f7ff6 98 "Properties given to the `shadowed' part of a filename in the minibuffer.
476cf013 99Only used when `file-name-shadow-mode' is active.
2fe3d6ec 100If Emacs is not running under a window system,
476cf013
MB
101`file-name-shadow-tty-properties' is used instead."
102 :type file-name-shadow-properties-custom-type
496f36c0
LT
103 :group 'minibuffer
104 :version "22.1")
519f7ff6 105
476cf013 106(defcustom file-name-shadow-tty-properties
1e8780b1 107 (purecopy '(before-string "{" after-string "} " field shadow))
519f7ff6 108 "Properties given to the `shadowed' part of a filename in the minibuffer.
82db9283
JB
109Only used when `file-name-shadow-mode' is active and Emacs
110is not running under a window-system; if Emacs is running under a window
476cf013
MB
111system, `file-name-shadow-properties' is used instead."
112 :type file-name-shadow-properties-custom-type
496f36c0
LT
113 :group 'minibuffer
114 :version "22.1")
519f7ff6 115
476cf013 116(defface file-name-shadow
c3423c97 117 '((t :inherit shadow))
476cf013 118 "Face used by `file-name-shadow-mode' for the shadow."
496f36c0
LT
119 :group 'minibuffer
120 :version "22.1")
519f7ff6 121
d037d501
MA
122(defvar rfn-eshadow-setup-minibuffer-hook nil
123 "Minibuffer setup functions from other packages.")
124
125(defvar rfn-eshadow-update-overlay-hook nil
126 "Customer overlay functions from other packages")
127
519f7ff6
MB
128\f
129;;; Internal variables
130
519f7ff6
MB
131;; A list of minibuffers to which we've added a post-command-hook.
132(defvar rfn-eshadow-frobbed-minibufs nil)
133
134;; An overlay covering the shadowed part of the filename (local to the
135;; minibuffer).
136(defvar rfn-eshadow-overlay)
137(make-variable-buffer-local 'rfn-eshadow-overlay)
138
139\f
140;;; Hook functions
141
142;; This function goes on minibuffer-setup-hook
143(defun rfn-eshadow-setup-minibuffer ()
476cf013 144 "Set up a minibuffer for `file-name-shadow-mode'.
519f7ff6
MB
145The prompt and initial input should already have been inserted."
146 (when minibuffer-completing-file-name
147 (setq rfn-eshadow-overlay
148 (make-overlay (minibuffer-prompt-end) (minibuffer-prompt-end)))
149 ;; Give rfn-eshadow-overlay the user's props.
150 (let ((props
151 (if window-system
476cf013
MB
152 file-name-shadow-properties
153 file-name-shadow-tty-properties)))
519f7ff6
MB
154 (while props
155 (overlay-put rfn-eshadow-overlay (pop props) (pop props))))
156 ;; Turn on overlay evaporation so that we don't have to worry about
157 ;; odd effects when the overlay sits empty at the beginning of the
158 ;; minibuffer.
159 (overlay-put rfn-eshadow-overlay 'evaporate t)
160 ;; Add our post-command hook, and make sure can remove it later.
161 (add-to-list 'rfn-eshadow-frobbed-minibufs (current-buffer))
d037d501
MA
162 (add-hook 'post-command-hook #'rfn-eshadow-update-overlay nil t)
163 ;; Run custom hook
164 (run-hooks 'rfn-eshadow-setup-minibuffer-hook)))
519f7ff6 165
2fe3d6ec
SM
166(defsubst rfn-eshadow-sifn-equal (goal pos)
167 (equal goal (condition-case nil
168 (substitute-in-file-name
169 (buffer-substring-no-properties pos (point-max)))
170 ;; `substitute-in-file-name' can fail on partial input.
171 (error nil))))
172
519f7ff6
MB
173;; post-command-hook to update overlay
174(defun rfn-eshadow-update-overlay ()
175 "Update `rfn-eshadow-overlay' to cover shadowed part of minibuffer input.
2fe3d6ec 176This is intended to be used as a minibuffer `post-command-hook' for
476cf013 177`file-name-shadow-mode'; the minibuffer should have already
519f7ff6 178been set up by `rfn-eshadow-setup-minibuffer'."
2fe3d6ec
SM
179 (condition-case nil
180 (let ((goal (substitute-in-file-name (minibuffer-contents)))
181 (mid (overlay-end rfn-eshadow-overlay))
182 (start (minibuffer-prompt-end))
183 (end (point-max)))
184 (unless
185 ;; Catch the common case where the shadow does not need to move.
186 (and mid
187 (or (eq mid end)
188 (not (rfn-eshadow-sifn-equal goal (1+ mid))))
189 (or (eq mid start)
190 (rfn-eshadow-sifn-equal goal mid)))
191 ;; Binary search for the greatest position still equivalent to
192 ;; the whole.
193 (while (or (< (1+ start) end)
194 (if (and (< (1+ end) (point-max))
195 (rfn-eshadow-sifn-equal goal (1+ end)))
196 ;; (SIFN end) != goal, but (SIFN (1+end)) == goal,
197 ;; We've reached a discontinuity: this can happen
198 ;; e.g. if `end' point to "/:...".
199 (setq start (1+ end) end (point-max))))
200 (setq mid (/ (+ start end) 2))
201 (if (rfn-eshadow-sifn-equal goal mid)
202 (setq start mid)
203 (setq end mid)))
d037d501
MA
204 (move-overlay rfn-eshadow-overlay (minibuffer-prompt-end) start))
205 ;; Run custom hook
206 (run-hooks 'rfn-eshadow-update-overlay-hook))
2fe3d6ec
SM
207 ;; `substitute-in-file-name' can fail on partial input.
208 (error nil)))
519f7ff6 209\f
476cf013 210(define-minor-mode file-name-shadow-mode
1ced831d
MB
211 "Toggle File-Name Shadow mode.
212When active, any part of a filename being read in the minibuffer
213that would be ignored (because the result is passed through
214`substitute-in-file-name') is given the properties in
476cf013 215`file-name-shadow-properties', which can be used to make
b2c8e6ab 216that portion dim, invisible, or otherwise less visually noticeable.
519f7ff6
MB
217
218With prefix argument ARG, turn on if positive, otherwise off.
219Returns non-nil if the new state is enabled."
220 :global t
adba8116
SM
221 ;; We'd like to use custom-initialize-set here so the setup is done
222 ;; before dumping, but at the point where the defcustom is evaluated,
223 ;; the corresponding function isn't defined yet, so
224 ;; custom-initialize-set signals an error.
225 :initialize 'custom-initialize-delay
da80e0da 226 :init-value t
519f7ff6 227 :group 'minibuffer
496f36c0 228 :version "22.1"
476cf013 229 (if file-name-shadow-mode
519f7ff6
MB
230 ;; Enable the mode
231 (add-hook 'minibuffer-setup-hook 'rfn-eshadow-setup-minibuffer)
232 ;; Disable the mode
233 (remove-hook 'minibuffer-setup-hook 'rfn-eshadow-setup-minibuffer)
234 ;; Remove our entry from any post-command-hook variable's it's still in
235 (dolist (minibuf rfn-eshadow-frobbed-minibufs)
236 (with-current-buffer minibuf
237 (remove-hook 'post-command-hook #'rfn-eshadow-update-overlay t)))
238 (setq rfn-eshadow-frobbed-minibufs nil)))
239
240
241(provide 'rfn-eshadow)
242
2fe3d6ec 243;; arch-tag: dcf70a52-0115-4ec2-b1e3-4f8d3541a888
519f7ff6 244;;; rfn-eshadow.el ends here