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