98 |
'(face file-name-shadow field shadow) |
'(face file-name-shadow field shadow) |
99 |
"Properties given to the `shadowed' part of a filename in the minibuffer. |
"Properties given to the `shadowed' part of a filename in the minibuffer. |
100 |
Only used when `file-name-shadow-mode' is active. |
Only used when `file-name-shadow-mode' is active. |
101 |
If emacs is not running under a window system, |
If Emacs is not running under a window system, |
102 |
`file-name-shadow-tty-properties' is used instead." |
`file-name-shadow-tty-properties' is used instead." |
103 |
:type file-name-shadow-properties-custom-type |
:type file-name-shadow-properties-custom-type |
104 |
:group 'minibuffer) |
:group 'minibuffer) |
121 |
|
|
122 |
;;; Internal variables |
;;; Internal variables |
123 |
|
|
|
;; Regexp to locate dividing point between shadow and real pathname |
|
|
(defconst rfn-eshadow-regexp |
|
|
(cond ((memq system-type '(ms-dos windows-nt)) |
|
|
;; This horrible regexp considers the following patterns as |
|
|
;; starting an absolute pathname, when following a `/' or an `\': |
|
|
;; L: / // ~ $ \\ \\\\ |
|
|
"\\(.*[^/]+/+?\\|/*?\\|\\)\\(~\\|$[^$]\\|$\\'\\|[][\\^a-z]:\\|//?\\([^][\\^a-z/$~]\\|[^/$~][^:]\\|[^/$~]?\\'\\)\\)") |
|
|
(t |
|
|
;; default is for unix-style filenames |
|
|
"\\(.*/\\)\\([/~]\\|$[^$]\\|$\\'\\)")) |
|
|
"Regular expression used to match shadowed filenames. |
|
|
There should be at least one regexp group; the end of the first one |
|
|
is used as the end of the shadowed portion of the filename.") |
|
|
|
|
124 |
;; A list of minibuffers to which we've added a post-command-hook. |
;; A list of minibuffers to which we've added a post-command-hook. |
125 |
(defvar rfn-eshadow-frobbed-minibufs nil) |
(defvar rfn-eshadow-frobbed-minibufs nil) |
126 |
|
|
154 |
(add-to-list 'rfn-eshadow-frobbed-minibufs (current-buffer)) |
(add-to-list 'rfn-eshadow-frobbed-minibufs (current-buffer)) |
155 |
(add-hook 'post-command-hook #'rfn-eshadow-update-overlay nil t))) |
(add-hook 'post-command-hook #'rfn-eshadow-update-overlay nil t))) |
156 |
|
|
157 |
|
(defsubst rfn-eshadow-sifn-equal (goal pos) |
158 |
|
(equal goal (condition-case nil |
159 |
|
(substitute-in-file-name |
160 |
|
(buffer-substring-no-properties pos (point-max))) |
161 |
|
;; `substitute-in-file-name' can fail on partial input. |
162 |
|
(error nil)))) |
163 |
|
|
164 |
;; post-command-hook to update overlay |
;; post-command-hook to update overlay |
165 |
(defun rfn-eshadow-update-overlay () |
(defun rfn-eshadow-update-overlay () |
166 |
"Update `rfn-eshadow-overlay' to cover shadowed part of minibuffer input. |
"Update `rfn-eshadow-overlay' to cover shadowed part of minibuffer input. |
167 |
This is intended to be used as a minibuffer post-command-hook for |
This is intended to be used as a minibuffer `post-command-hook' for |
168 |
`file-name-shadow-mode'; the minibuffer should have already |
`file-name-shadow-mode'; the minibuffer should have already |
169 |
been set up by `rfn-eshadow-setup-minibuffer'." |
been set up by `rfn-eshadow-setup-minibuffer'." |
170 |
;; This is not really a correct implementation; it won't always do the |
(condition-case nil |
171 |
;; right thing in the presence of environment variables that |
(let ((goal (substitute-in-file-name (minibuffer-contents))) |
172 |
;; substitute-in-file-name would expand; currently it just assumes any |
(mid (overlay-end rfn-eshadow-overlay)) |
173 |
;; environment variable contains an absolute filename. |
(start (minibuffer-prompt-end)) |
174 |
(save-excursion |
(end (point-max))) |
175 |
(let ((inhibit-point-motion-hooks t)) |
(unless |
176 |
(goto-char (minibuffer-prompt-end)) |
;; Catch the common case where the shadow does not need to move. |
177 |
;; Update the overlay (which will evaporate if it's empty). |
(and mid |
178 |
(move-overlay rfn-eshadow-overlay |
(or (eq mid end) |
179 |
(point) |
(not (rfn-eshadow-sifn-equal goal (1+ mid)))) |
180 |
(if (looking-at rfn-eshadow-regexp) |
(or (eq mid start) |
181 |
(match-end 1) |
(rfn-eshadow-sifn-equal goal mid))) |
182 |
(point)))))) |
;; Binary search for the greatest position still equivalent to |
183 |
|
;; the whole. |
184 |
|
(while (or (< (1+ start) end) |
185 |
|
(if (and (< (1+ end) (point-max)) |
186 |
|
(rfn-eshadow-sifn-equal goal (1+ end))) |
187 |
|
;; (SIFN end) != goal, but (SIFN (1+end)) == goal, |
188 |
|
;; We've reached a discontinuity: this can happen |
189 |
|
;; e.g. if `end' point to "/:...". |
190 |
|
(setq start (1+ end) end (point-max)))) |
191 |
|
(setq mid (/ (+ start end) 2)) |
192 |
|
(if (rfn-eshadow-sifn-equal goal mid) |
193 |
|
(setq start mid) |
194 |
|
(setq end mid))) |
195 |
|
(move-overlay rfn-eshadow-overlay (minibuffer-prompt-end) start))) |
196 |
|
;; `substitute-in-file-name' can fail on partial input. |
197 |
|
(error nil))) |
198 |
|
|
|
;;; Note this definition must be at the end of the file, because |
|
|
;;; `define-minor-mode' actually calls the mode-function if the |
|
|
;;; associated variable is non-nil, which requires that all needed |
|
|
;;; functions be already defined. [This is arguably a bug in d-m-m] |
|
199 |
;;;###autoload |
;;;###autoload |
200 |
(define-minor-mode file-name-shadow-mode |
(define-minor-mode file-name-shadow-mode |
201 |
"Toggle File-Name Shadow mode. |
"Toggle File-Name Shadow mode. |
223 |
|
|
224 |
(provide 'rfn-eshadow) |
(provide 'rfn-eshadow) |
225 |
|
|
226 |
;;; arch-tag: dcf70a52-0115-4ec2-b1e3-4f8d3541a888 |
;; arch-tag: dcf70a52-0115-4ec2-b1e3-4f8d3541a888 |
227 |
;;; rfn-eshadow.el ends here |
;;; rfn-eshadow.el ends here |