Mercurial > emacs
comparison lisp/ffap.el @ 18291:130a48e6cc13
(ffap-soft-value): Make this a function again; the macro
version does intern-soft too early. Deleted XEmacs-specific code.
(ffap-string-at-point-mode-alist): added "=" and
"&" to the url syntax, as suggested by SJE.
(ffap-read-file-or-url): fixed the HIST argument to
completing-read (only visible in XEmacs?), as reported by
Christoph Wedler <wedler@fmi.uni-passau.de>.
(ffap-kpathsea-expand-path) New func, replaces ffap-add-subdirs,
a first attempt at kpathsea emulation. Also convert "" to "." in
path lists, for XEmacs. Suggestions from SJE.
Added mouse-track support (but no binding), as
suggested by MDB. Moved Emacs mouse bindings from
"down-mouse" events to ordinary mouse events.
(ffap-alist): added ffap-fortran-mode, as requested by MDB.
Rewrote and merged XEmacs support, eliminating file
ffap-xe.el. Modified ffap-other-frame to work in dedicated
frames, fixing a bug reported by JENS.
(ffap-menu-rescan): avoid modifying the buffer.
Two bugs reported by Christoph Wedler <wedler@fmi.uni-passau.de>:
(ffap-fixup-url): avoid autoloading through url-normalize-url.
(ffap-read-file-or-url): for XEmacs, give extra HACK-HOMEDIR arg
to `abbreviate-file-name'.
(ffap-file-at-point): suppress errors from `ffap-alist'.
(ffap-url-at-point): modified regexp to accept
mail hostnames ending with a digit. Fixes bug report of SJE.
(ffap-url-at-point): use higher level function
(w3-view-this-url t) suggested by wmperry, instead of
w3-zone-at/w3-zone-data or widget-at/widget-get.
(ffap-url-at-point): modified to work with
w3-version "WWW 2.3.64 1996/06/02 06:20:23" alpha, which
uses the 'widget package rather than the old w3-zone-at.
Bug was reported by JENS.
Adopted comments and doc strings to Emacs coding
conventions. Reorganized. Retired v18 support.
(ffap-bindings): Offers a default installation.
(ffap-string-at-point): Modified arguments.
(ffap-gnus-hook): Updated for Gnus 5.
(ffap-tex-init): Delayed initialization of `ffap-tex-path'.
(ffap-dired): New entry in `ffap-alist'.
(ffap-menu-rescan): May fontify the choices in buffer.
(ffap-read-file-or-url): `PC-completion-as-file-name-predicate'
used if available, to work with complete.el.
author | Karl Heuer <kwzh@gnu.org> |
---|---|
date | Wed, 18 Jun 1997 04:24:37 +0000 |
parents | fcbe987b332a |
children | 83ff1ecdb0e3 |
comparison
equal
deleted
inserted
replaced
18290:d87d578b953d | 18291:130a48e6cc13 |
---|---|
1 ;;; ffap.el --- find file or URL at point | 1 ;;; ffap.el --- find file (or url) at point |
2 | 2 ;; |
3 ;; Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. |
4 | 4 ;; |
5 ;; Author: Michelangelo Grigni <mic@mathcs.emory.edu> | 5 ;; Author: Michelangelo Grigni <mic@mathcs.emory.edu> |
6 ;; Created: 29 Mar 1993 | 6 ;; Created: 29 Mar 1993 |
7 ;; Keywords: files, hypermedia, matching, mouse | 7 ;; Keywords: files, hypermedia, matching, mouse |
8 ;; X-Latest: ftp://ftp.mathcs.emory.edu:/pub/mic/emacs/ | 8 ;; X-URL: ftp://ftp.mathcs.emory.edu:/pub/mic/emacs/ |
9 | 9 |
10 ;; This file is part of GNU Emacs. | 10 ;; This file is part of GNU Emacs. |
11 | 11 |
12 ;; GNU Emacs is free software; you can redistribute it and/or modify | 12 ;; GNU Emacs is free software; you can redistribute it and/or modify |
13 ;; it under the terms of the GNU General Public License as published by | 13 ;; it under the terms of the GNU General Public License as published by |
45 ;; | 45 ;; |
46 ;; C-x C-f find-file-at-point (abbreviated as ffap) | 46 ;; C-x C-f find-file-at-point (abbreviated as ffap) |
47 ;; C-x 4 f ffap-other-window | 47 ;; C-x 4 f ffap-other-window |
48 ;; C-x 5 f ffap-other-frame | 48 ;; C-x 5 f ffap-other-frame |
49 ;; S-mouse-3 ffap-at-mouse | 49 ;; S-mouse-3 ffap-at-mouse |
50 ;; C-S-mouse-3 ffap-menu | |
50 ;; | 51 ;; |
51 ;; ffap-bindings also adds hooks to make the following local bindings | 52 ;; ffap-bindings also adds hooks to make the following local bindings |
52 ;; in vm, gnus, and rmail: | 53 ;; in vm, gnus, and rmail: |
53 ;; | 54 ;; |
54 ;; M-l ffap-next, or ffap-gnus-next in gnus | 55 ;; M-l ffap-next, or ffap-gnus-next in gnus (l == "link") |
55 ;; M-m ffap-menu, or ffap-gnus-menu in gnus | 56 ;; M-m ffap-menu, or ffap-gnus-menu in gnus (m == "menu") |
56 ;; | 57 ;; |
57 ;; If you do not like these bindings, modify the variable | 58 ;; If you do not like these bindings, modify the variable |
58 ;; `ffap-bindings', or write your own. | 59 ;; `ffap-bindings', or write your own. |
59 ;; | 60 ;; |
60 ;; If you use ange-ftp, browse-url, complete, efs, or w3, it is best | 61 ;; If you use ange-ftp, browse-url, complete, efs, or w3, it is best |
69 ;; ffap uses w3 (if found) or else browse-url to fetch URL's. For | 70 ;; ffap uses w3 (if found) or else browse-url to fetch URL's. For |
70 ;; a hairier `ffap-url-fetcher', try ffap-url.el (same ftp site). | 71 ;; a hairier `ffap-url-fetcher', try ffap-url.el (same ftp site). |
71 ;; Also, you can add `ffap-menu-rescan' to various hooks to fontify | 72 ;; Also, you can add `ffap-menu-rescan' to various hooks to fontify |
72 ;; the file and URL references within a buffer. | 73 ;; the file and URL references within a buffer. |
73 | 74 |
75 | |
76 ;;; Change Log: | |
77 ;; | |
78 ;; The History and Contributors moved to ffap.LOG (same ftp site), | |
79 ;; which also has some old examples and commentary from ffap 1.5. | |
80 | |
81 | |
74 ;;; Todo list: | 82 ;;; Todo list: |
75 ;; * recognize paths inside /usr/bin:/bin:/etc, ./ffap.el:80: | 83 ;; * use kpsewhich |
76 ;; * let "/path/file#key" jump to key (offset or regexp) in /path/file | 84 ;; * let "/path/file#key" jump to key (tag or regexp) in /path/file |
77 ;; * find file of symbol if TAGS is loaded (like above) | 85 ;; * find file of symbol if TAGS is loaded (like above) |
78 ;; * break up long menus into multiple panes (like imenu?) | 86 ;; * break long menus into multiple panes (like imenu?) |
79 ;; * notice node in "(dired)Virtual Dired" (handle the space?) | 87 ;; * notice node in "(dired)Virtual Dired" (quotes, parentheses, whitespace) |
80 ;; * notice "machine.dom blah blah blah path/file" (how?) | 88 ;; * notice "machine.dom blah blah blah path/file" (how?) |
81 ;; * if w3 becomes standard, could rewrite to use its functions | 89 ;; * as w3 becomes standard, rewrite to rely more on its functions |
82 ;; * regexp options for ffap-string-at-point, like font-lock (MCOOK) | 90 ;; * regexp options for ffap-string-at-point, like font-lock (MCOOK) |
83 ;; * v19: could replace `ffap-locate-file' with a quieter `locate-library' | 91 ;; * v19: could replace `ffap-locate-file' with a quieter `locate-library' |
84 ;; * support for custom.el | 92 ;; * handle "$(VAR)" in Makefiles |
85 ;; + handle "$(HOME)" in Makefiles? | 93 ;; * use the font-lock machinery |
86 ;; + modify `font-lock-keywords' to do fontification | |
87 | 94 |
88 | 95 |
89 ;;; Code: | 96 ;;; Code: |
90 | 97 |
91 (provide 'ffap) | 98 (provide 'ffap) |
92 | 99 |
93 ;; Versions: This file is tested with Emacs 19.30. It mostly works | 100 ;; The code is organized in pages, separated by formfeed characters. |
94 ;; with XEmacs, but get ffap-xe.el for the popup menu. Emacs 18 is | 101 ;; See the next two pages for standard customization ideas. |
95 ;; now abandoned (get ffap-15.el instead). | |
96 | |
97 (defvar ffap-xemacs (and (string-match "X[Ee]macs" emacs-version) t) | |
98 "Whether ffap thinks it is running under XEmacs.") | |
99 | |
100 | 102 |
101 | 103 |
102 ;;; User Variables: | 104 ;;; User Variables: |
103 | 105 |
104 (defgroup ffap nil | 106 (defgroup ffap nil |
105 "Find file or URL at point." | 107 "Find file or URL at point." |
106 :group 'matching) | 108 :group 'matching) |
107 | 109 |
108 | 110 |
109 ;; This function is used inside defvars: | 111 (defun ffap-soft-value (name &optional default) |
110 (defmacro ffap-soft-value (name &optional default) | |
111 "Return value of symbol with NAME, if it is interned. | 112 "Return value of symbol with NAME, if it is interned. |
112 Otherwise return nil (or the optional DEFAULT value)." | 113 Otherwise return nil (or the optional DEFAULT value)." |
113 ;; Bug: (ffap-soft-value "nil" 5) --> 5 | 114 ;; Bug: (ffap-soft-value "nil" 5) --> 5 |
114 (let ((sym (intern-soft name))) | 115 (let ((sym (intern-soft name))) |
115 (if (and sym (boundp sym)) | 116 (if (and sym (boundp sym)) (symbol-value sym) default))) |
116 sym | |
117 `(quote ,default)))) | |
118 | |
119 | 117 |
120 (defcustom ffap-ftp-regexp | 118 (defcustom ffap-ftp-regexp |
121 (and | 119 ;; This used to test for ange-ftp or efs being present, but it should be |
122 (or (featurep 'ange-ftp) | 120 ;; harmless (and simpler) to give it this value unconditionally. |
123 (featurep 'efs) | 121 "\\`/[^/:]+:" |
124 (and (boundp 'file-name-handler-alist) ; v19 | 122 "*Paths matching this regexp are treated as remote ftp paths by ffap. |
125 (or (rassq 'ange-ftp-hook-function file-name-handler-alist) | 123 If nil, ffap neither recognizes nor generates such paths." |
126 (rassq 'efs-file-handler-function file-name-handler-alist)))) | |
127 ;; Apparently this is good enough for both ange-ftp and efs: | |
128 "\\`/[^/:]+:") | |
129 "*Treat paths matching this as remote ftp paths. nil to disable. | |
130 nil also disables the generation of such paths by ffap." | |
131 :type '(choice (const :tag "Disable" nil) | 124 :type '(choice (const :tag "Disable" nil) |
125 (const :tag "Standard" "\\`/[^/:]+:") | |
132 regexp) | 126 regexp) |
133 :group 'ffap) | 127 :group 'ffap) |
134 | 128 |
135 (defcustom ffap-url-unwrap-local t | 129 (defcustom ffap-url-unwrap-local t |
136 "*If non-nil, convert \"file:\" URL to local path before prompting." | 130 "*If non-nil, convert \"file:\" url to local path before prompting." |
137 :type 'boolean | 131 :type 'boolean |
138 :group 'ffap) | 132 :group 'ffap) |
139 | 133 |
140 (defcustom ffap-url-unwrap-remote t | 134 (defcustom ffap-url-unwrap-remote t |
141 "*If non-nil, convert \"ftp:\" URL to remote path before prompting. | 135 "*If non-nil, convert \"ftp:\" url to remote path before prompting. |
142 This is ignored if `ffap-ftp-regexp' is nil." | 136 This is ignored if `ffap-ftp-regexp' is nil." |
143 :type 'boolean | 137 :type 'boolean |
144 :group 'ffap) | 138 :group 'ffap) |
145 | 139 |
146 (defcustom ffap-ftp-default-user | 140 (defcustom ffap-ftp-default-user "anonymous" |
147 "anonymous" | |
148 "*User name in ftp paths generated by `ffap-host-to-path'. | 141 "*User name in ftp paths generated by `ffap-host-to-path'. |
149 nil to rely on `efs-default-user' or `ange-ftp-default-user'." | 142 Note this name may be omitted if it equals the default |
150 :type '(choice (const :tag "Default" nil) | 143 \(either `efs-default-user' or `ange-ftp-default-user'\)." |
151 string) | 144 :type 'string |
152 :group 'ffap) | 145 :group 'ffap) |
153 | 146 |
154 (defcustom ffap-rfs-regexp | 147 (defcustom ffap-rfs-regexp |
155 ;; Remote file access built into file system? HP rfa or Andrew afs: | 148 ;; Remote file access built into file system? HP rfa or Andrew afs: |
156 "\\`/\\(afs\\|net\\)/." | 149 "\\`/\\(afs\\|net\\)/." |
172 "Regexp matching URL's. nil to disable URL features in ffap.") | 165 "Regexp matching URL's. nil to disable URL features in ffap.") |
173 | 166 |
174 (defcustom ffap-foo-at-bar-prefix "mailto" | 167 (defcustom ffap-foo-at-bar-prefix "mailto" |
175 "*Presumed URL prefix type of strings like \"<foo.9z@bar>\". | 168 "*Presumed URL prefix type of strings like \"<foo.9z@bar>\". |
176 Sensible values are nil, \"news\", or \"mailto\"." | 169 Sensible values are nil, \"news\", or \"mailto\"." |
177 :type 'string | 170 :type '(choice (const "mailto") |
171 (const "news") | |
172 (const :tag "Disable" nil) | |
173 ;; string -- possible, but not really useful | |
174 ) | |
178 :group 'ffap) | 175 :group 'ffap) |
179 | 176 |
180 | 177 |
181 ;;; Peanut Gallery: | 178 ;;; Peanut Gallery (More User Variables): |
182 ;; | 179 ;; |
183 ;; Users of ffap occasionally suggest new features. If I consider | 180 ;; Users of ffap occasionally suggest new features. If I consider |
184 ;; those features interesting but not clear winners (a matter of | 181 ;; those features interesting but not clear winners (a matter of |
185 ;; personal taste) I try to leave options to enable them. Read | 182 ;; personal taste) I try to leave options to enable them. Read |
186 ;; through this section for features that you like, put an appropriate | 183 ;; through this section for features that you like, put an appropriate |
187 ;; enabler in your .emacs file. | 184 ;; enabler in your .emacs file. |
188 | 185 |
189 (defcustom ffap-dired-wildcards nil ; "[*?][^/]*$" | 186 (defcustom ffap-dired-wildcards nil |
190 ;; Suggestion from RHOGEE, 07 Jul 1994. Disabled, dired is still | 187 ;; Suggestion from RHOGEE, 07 Jul 1994. Disabled, dired is still |
191 ;; available by "C-x C-d <pattern>", and valid filenames may | 188 ;; available by "C-x C-d <pattern>", and valid filenames may |
192 ;; sometimes contain wildcard characters. | 189 ;; sometimes contain wildcard characters. |
193 "*A regexp matching filename wildcard characters, or nil. | 190 "*A regexp matching filename wildcard characters, or nil. |
194 If `find-file-at-point' gets a filename matching this pattern, | 191 If `find-file-at-point' gets a filename matching this pattern, |
195 it passes it on to `dired' instead of `find-file'." | 192 it passes it on to `dired' instead of `find-file'." |
196 :type '(choice (const :tag "off" nil) | 193 :type '(choice (const :tag "Disable" nil) |
197 regexp) | 194 (const :tag "Enable" "[*?][^/]*\\'") |
195 ;; regexp -- probably not useful | |
196 ) | |
198 :group 'ffap) | 197 :group 'ffap) |
199 | 198 |
200 (defcustom ffap-newfile-prompt nil ; t | 199 (defcustom ffap-newfile-prompt nil |
201 ;; Suggestion from RHOGEE, 11 Jul 1994. Disabled, I think this is | 200 ;; Suggestion from RHOGEE, 11 Jul 1994. Disabled, I think this is |
202 ;; better handled by `find-file-not-found-hooks'. | 201 ;; better handled by `find-file-not-found-hooks'. |
203 "*Whether `find-file-at-point' prompts about a nonexistent file." | 202 "*Whether `find-file-at-point' prompts about a nonexistent file." |
204 :type 'boolean | 203 :type 'boolean |
205 :group 'ffap) | 204 :group 'ffap) |
226 ;; http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/remote-control.html | 225 ;; http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/remote-control.html |
227 ;; http://home.netscape.com/newsref/std/x-remote.html | 226 ;; http://home.netscape.com/newsref/std/x-remote.html |
228 "*A function of one argument, called by ffap to fetch an URL. | 227 "*A function of one argument, called by ffap to fetch an URL. |
229 Reasonable choices are `w3-fetch' or `browse-url-netscape'. | 228 Reasonable choices are `w3-fetch' or `browse-url-netscape'. |
230 For a fancier alternative, get ffap-url.el." | 229 For a fancier alternative, get ffap-url.el." |
231 :type 'function | 230 :type '(choice (const w3-fetch) |
231 (const browse-url-netscape) | |
232 (const browse-url-mosaic) | |
233 function) | |
232 :group 'ffap) | 234 :group 'ffap) |
233 (put 'ffap-url-fetcher 'risky-local-variable t) | 235 (put 'ffap-url-fetcher 'risky-local-variable t) |
234 | 236 |
235 | 237 |
236 ;;; Command ffap-next: | 238 ;;; Compatibility (XEmacs code suppressed in this version): |
239 | |
240 (progn | |
241 (defalias 'ffap-make-overlay 'make-overlay) | |
242 (defalias 'ffap-delete-overlay 'delete-overlay) ; reusable | |
243 (defalias 'ffap-move-overlay 'move-overlay) | |
244 (defalias 'ffap-overlay-put 'overlay-put) ; 'face | |
245 (defalias 'ffap-find-face 'internal-find-face) | |
246 (defun ffap-mouse-event nil ; current mouse event, or nil | |
247 (and (listp last-nonmenu-event) last-nonmenu-event)) | |
248 (defun ffap-event-buffer (event) (window-buffer (car (event-start event)))) | |
249 ) | |
250 | |
251 | |
252 ;;; Find Next Thing in buffer (`ffap-next'): | |
237 ;; | 253 ;; |
238 ;; Original ffap-next-url (URL's only) from RPECK 30 Mar 1995. Since | 254 ;; Original ffap-next-url (URL's only) from RPECK 30 Mar 1995. Since |
239 ;; then, broke it up into ffap-next-guess (noninteractive) and | 255 ;; then, broke it up into ffap-next-guess (noninteractive) and |
240 ;; ffap-next (a command). It now work on files as well as url's. | 256 ;; ffap-next (a command). It now work on files as well as url's. |
241 | 257 |
305 (if (interactive-p) | 321 (if (interactive-p) |
306 (call-interactively 'ffap-next) | 322 (call-interactively 'ffap-next) |
307 (ffap-next back wrap)))) | 323 (ffap-next back wrap)))) |
308 | 324 |
309 | 325 |
310 ;;; Remote machines and paths: | 326 ;;; Machines (`ffap-machine-p'): |
311 | |
312 (defun ffap-replace-path-component (fullname name) | |
313 "In remote FULLNAME, replace path with NAME. May return nil." | |
314 ;; Use ange-ftp or efs if loaded, but do not load them otherwise. | |
315 (let (found) | |
316 (mapcar | |
317 (function (lambda (sym) (and (fboundp sym) (setq found sym)))) | |
318 '( | |
319 efs-replace-path-component | |
320 ange-ftp-replace-path-component | |
321 ange-ftp-replace-name-component | |
322 )) | |
323 (and found | |
324 (fset 'ffap-replace-path-component found) | |
325 (funcall found fullname name)))) | |
326 ;; (ffap-replace-path-component "/who@foo.com:/whatever" "/new") | |
327 | |
328 (defun ffap-file-exists-string (file) | |
329 ;; With certain packages (ange-ftp, jka-compr?) file-exists-p | |
330 ;; sometimes returns a nicer string than it is given. Otherwise, it | |
331 ;; just returns nil or t. | |
332 "Return FILE \(maybe modified\) if it exists, else nil." | |
333 (and file ; quietly reject nil | |
334 (let ((exists (file-exists-p file))) | |
335 (and exists (if (stringp exists) exists file))))) | |
336 | 327 |
337 ;; I cannot decide a "best" strategy here, so these are variables. In | 328 ;; I cannot decide a "best" strategy here, so these are variables. In |
338 ;; particular, if `Pinging...' is broken or takes too long on your | 329 ;; particular, if `Pinging...' is broken or takes too long on your |
339 ;; machine, try setting these all to accept or reject. | 330 ;; machine, try setting these all to accept or reject. |
340 (defcustom ffap-machine-p-local 'reject ; this happens often | 331 (defcustom ffap-machine-p-local 'reject ; this happens often |
341 "*A symbol, one of: ping, accept, reject. | 332 "*A symbol, one of: `ping', `accept', `reject'. |
342 What `ffap-machine-p' does with hostnames that have no domain." | 333 What `ffap-machine-p' does with hostnames that have no domain." |
343 :type '(choice (const ping) | 334 :type '(choice (const ping) |
344 (const accept) | 335 (const accept) |
345 (const reject)) | 336 (const reject)) |
346 :group 'ffap) | 337 :group 'ffap) |
432 ;; Other errors mean the host exists: | 423 ;; Other errors mean the host exists: |
433 (nth 2 error))) | 424 (nth 2 error))) |
434 ;; Could be "Unknown service": | 425 ;; Could be "Unknown service": |
435 (t (signal (car error) (cdr error)))))))))))) | 426 (t (signal (car error) (cdr error)))))))))))) |
436 | 427 |
428 | |
429 ;;; Possibly Remote Resources: | |
430 | |
431 (defun ffap-replace-path-component (fullname name) | |
432 "In remote FULLNAME, replace path with NAME. May return nil." | |
433 ;; Use ange-ftp or efs if loaded, but do not load them otherwise. | |
434 (let (found) | |
435 (mapcar | |
436 (function (lambda (sym) (and (fboundp sym) (setq found sym)))) | |
437 '( | |
438 efs-replace-path-component | |
439 ange-ftp-replace-path-component | |
440 ange-ftp-replace-name-component | |
441 )) | |
442 (and found | |
443 (fset 'ffap-replace-path-component found) | |
444 (funcall found fullname name)))) | |
445 ;; (ffap-replace-path-component "/who@foo.com:/whatever" "/new") | |
446 | |
447 (defun ffap-file-exists-string (file) | |
448 ;; With certain packages (ange-ftp, jka-compr?) file-exists-p | |
449 ;; sometimes returns a nicer string than it is given. Otherwise, it | |
450 ;; just returns nil or t. | |
451 "Return FILE \(maybe modified\) if it exists, else nil." | |
452 (and file ; quietly reject nil | |
453 (let ((exists (file-exists-p file))) | |
454 (and exists (if (stringp exists) exists file))))) | |
455 | |
456 | |
437 (defun ffap-file-remote-p (filename) | 457 (defun ffap-file-remote-p (filename) |
438 "If FILENAME looks remote, return it \(maybe slightly improved\)." | 458 "If FILENAME looks remote, return it \(maybe slightly improved\)." |
439 ;; (ffap-file-remote-p "/user@foo.bar.com:/pub") | 459 ;; (ffap-file-remote-p "/user@foo.bar.com:/pub") |
440 ;; (ffap-file-remote-p "/cssun.mathcs.emory.edu://path") | 460 ;; (ffap-file-remote-p "/cssun.mathcs.emory.edu://path") |
441 ;; (ffap-file-remote-p "/ffap.el:80") | 461 ;; (ffap-file-remote-p "/ffap.el:80") |
456 "Return machine name at point if it exists, or nil." | 476 "Return machine name at point if it exists, or nil." |
457 (let ((mach (ffap-string-at-point 'machine))) | 477 (let ((mach (ffap-string-at-point 'machine))) |
458 (and (ffap-machine-p mach) mach))) | 478 (and (ffap-machine-p mach) mach))) |
459 | 479 |
460 (defsubst ffap-host-to-path (host) | 480 (defsubst ffap-host-to-path (host) |
461 "Convert HOST to something like \"/anonymous@HOST:\". | 481 "Convert HOST to something like \"/USER@HOST:\" or \"/HOST:\". |
462 Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"." | 482 Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"." |
463 (if (equal host "localhost") "" | 483 (if (equal host "localhost") |
464 (concat "/" | 484 "" |
465 ffap-ftp-default-user (and ffap-ftp-default-user "@") | 485 (let ((user ffap-ftp-default-user)) |
466 host ":"))) | 486 ;; Avoid including the user if it is same as default: |
487 (if (or (equal user (ffap-soft-value "ange-ftp-default-user")) | |
488 (equal user (ffap-soft-value "efs-default-user"))) | |
489 (setq user nil)) | |
490 (concat "/" user (and user "@") host ":")))) | |
467 | 491 |
468 (defun ffap-fixup-machine (mach) | 492 (defun ffap-fixup-machine (mach) |
469 ;; Convert a hostname into an url, an ftp path, or nil. | 493 ;; Convert a hostname into an url, an ftp path, or nil. |
470 (cond | 494 (cond |
471 ((not (and ffap-url-regexp (stringp mach))) nil) | 495 ((not (and ffap-url-regexp (stringp mach))) nil) |
536 (cond | 560 (cond |
537 ((not (stringp url)) nil) | 561 ((not (stringp url)) nil) |
538 ((and ffap-url-unwrap-local (ffap-url-unwrap-local url))) | 562 ((and ffap-url-unwrap-local (ffap-url-unwrap-local url))) |
539 ((and ffap-url-unwrap-remote ffap-ftp-regexp | 563 ((and ffap-url-unwrap-remote ffap-ftp-regexp |
540 (ffap-url-unwrap-remote url))) | 564 (ffap-url-unwrap-remote url))) |
541 ;; Do not load w3 just for this: | 565 ;; This might autoload the url package, oh well: |
542 (t (let ((normal (and (fboundp 'url-normalize-url) | 566 (t (let ((normal (and (fboundp 'url-normalize-url) |
543 (url-normalize-url url)))) | 567 (url-normalize-url url)))) |
544 ;; In case url-normalize-url is confused: | 568 ;; In case url-normalize-url is confused: |
545 (or (and normal (not (zerop (length normal))) normal) | 569 (or (and normal (not (zerop (length normal))) normal) |
546 url))))) | 570 url))))) |
547 | 571 |
548 | 572 |
549 ;;; `ffap-alist': | 573 ;;; Path Handling: |
550 ;; | 574 ;; |
551 ;; Search actions depending on the major-mode or extensions of the | 575 ;; The upcoming ffap-alist actions need various utilities to prepare |
552 ;; current name. Note all the little defun's could be broken out, at | 576 ;; and search paths of directories. Too many features here. |
553 ;; some loss of locality. A good example of featuritis. | 577 |
554 | 578 ;; (defun ffap-last (l) (while (cdr l) (setq l (cdr l))) l) |
555 ;; First, some helpers for functions in `ffap-alist': | 579 ;; (defun ffap-splice (func inlist) |
580 ;; "Equivalent to (apply 'nconc (mapcar FUNC INLIST)), but less consing." | |
581 ;; (let* ((head (cons 17 nil)) (last head)) | |
582 ;; (while inlist | |
583 ;; (setcdr last (funcall func (car inlist))) | |
584 ;; (setq last (ffap-last last) inlist (cdr inlist))) | |
585 ;; (cdr head))) | |
556 | 586 |
557 (defun ffap-list-env (env &optional empty) | 587 (defun ffap-list-env (env &optional empty) |
558 ;; Replace this with parse-colon-path (lisp/files.el)? | 588 "Return a list of strings parsed from environment variable ENV. |
559 "Directory list parsed from path envinronment variable ENV. | 589 Optional EMPTY is the default list if \(getenv ENV\) is undefined, and |
560 Optional EMPTY is default if (getenv ENV) is undefined, and is also | 590 also is substituted for the first empty-string component, if there is one. |
561 substituted for the first empty-string component, if there is one. | 591 Uses `path-separator' to separate the path into substrings." |
562 Uses `path-separator' to separate the path into directories." | 592 ;; We cannot use parse-colon-path (files.el), since it kills |
563 ;; Derived from psg-list-env in RHOGEE's ff-paths and | 593 ;; "//" entries using file-name-as-directory. |
564 ;; bib-cite packages. The `empty' argument is intended to mimic | 594 ;; Similar: dired-split, TeX-split-string, and RHOGEE's psg-list-env |
565 ;; the semantics of TeX/BibTeX variables, it is substituted for | 595 ;; in ff-paths and bib-cite. The EMPTY arg may help mimic kpathsea. |
566 ;; any empty string entry. | |
567 (if (or empty (getenv env)) ; should return something | 596 (if (or empty (getenv env)) ; should return something |
568 (let ((start 0) match dir ret) | 597 (let ((start 0) match dir ret) |
569 (setq env (concat (getenv env) path-separator)) | 598 (setq env (concat (getenv env) path-separator)) |
570 (while (setq match (string-match path-separator env start)) | 599 (while (setq match (string-match path-separator env start)) |
571 (setq dir (substring env start match) start (1+ match)) | 600 (setq dir (substring env start match) start (1+ match)) |
572 ;;(and (file-directory-p dir) (not (member dir ret)) ...) | 601 ;;(and (file-directory-p dir) (not (member dir ret)) ...) |
573 (setq ret (cons dir ret))) | 602 (setq ret (cons dir ret))) |
574 (setq ret (nreverse ret)) | 603 (setq ret (nreverse ret)) |
575 (and empty (setq match (member "" ret)) | 604 (and empty (setq match (member "" ret)) |
576 (progn | 605 (progn ; allow string or list here |
577 (setcdr match (append (cdr-safe empty) (cdr match))) | 606 (setcdr match (append (cdr-safe empty) (cdr match))) |
578 (setcar match (or (car-safe empty) empty)))) | 607 (setcar match (or (car-safe empty) empty)))) |
579 ret))) | 608 ret))) |
580 | 609 |
581 (defun ffap-reduce-path (path) | 610 (defun ffap-reduce-path (path) |
587 (or (member (car tem) ret) | 616 (or (member (car tem) ret) |
588 (not (file-directory-p (car tem))) | 617 (not (file-directory-p (car tem))) |
589 (progn (setcdr tem ret) (setq ret tem)))) | 618 (progn (setcdr tem ret) (setq ret tem)))) |
590 (nreverse ret))) | 619 (nreverse ret))) |
591 | 620 |
592 (defun ffap-add-subdirs (path) | 621 (defun ffap-all-subdirs (dir &optional depth) |
593 "Return PATH augmented with its immediate subdirectories." | 622 "Return list all subdirectories under DIR, starting with itself. |
594 ;; (ffap-add-subdirs '("/notexist" "~")) | 623 Directories beginning with \".\" are ignored, and directory symlinks |
595 (let (ret subs) | 624 are listed but never searched (to avoid loops). |
596 (while path | 625 Optional DEPTH limits search depth." |
597 (mapcar | 626 (and (file-exists-p dir) |
598 (function | 627 (ffap-all-subdirs-loop (expand-file-name dir) (or depth -1)))) |
599 (lambda (f) (and (file-directory-p f) (setq ret (cons f ret))))) | 628 |
600 (condition-case nil | 629 (defun ffap-all-subdirs-loop (dir depth) ; internal |
601 (directory-files (car path) t "[^.]") | 630 (setq depth (1- depth)) |
602 (error nil))) | 631 (cons dir |
603 (setq ret (cons (car path) ret) | 632 (and (not (eq depth -1)) |
604 path (cdr path))) | 633 (apply 'nconc |
605 (nreverse ret))) | 634 (mapcar |
635 (function | |
636 (lambda (d) | |
637 (cond | |
638 ((not (file-directory-p d)) nil) | |
639 ((file-symlink-p d) (list d)) | |
640 (t (ffap-all-subdirs-loop d depth))))) | |
641 (directory-files dir t "\\`[^.]") | |
642 ))))) | |
643 | |
644 (defvar ffap-kpathsea-depth 1 | |
645 "Bound on depth of subdirectory search in `ffap-kpathsea-expand-path'. | |
646 Set to 0 to avoid all searching, or nil for no limit.") | |
647 | |
648 (defun ffap-kpathsea-expand-path (path) | |
649 "Replace each \"//\"-suffixed dir in PATH by a list of its subdirs. | |
650 The subdirs begin with the original directory, and the depth of the | |
651 search is bounded by `ffap-kpathsea-depth'. This is intended to mimic | |
652 kpathsea, a library used by some versions of TeX." | |
653 (apply 'nconc | |
654 (mapcar | |
655 (function | |
656 (lambda (dir) | |
657 (if (string-match "[^/]//\\'" dir) | |
658 (ffap-all-subdirs (substring dir 0 -2) ffap-kpathsea-depth) | |
659 (list dir)))) | |
660 path))) | |
661 | |
662 (defvar ffap-locate-jka-suffixes t | |
663 "List of compression suffixes tried by `ffap-locate-file'. | |
664 | |
665 If not a list, it will be initialized by `ffap-locate-file', depending | |
666 on whether you use jka-compr (a.k.a. `auto-compression-mode'). | |
667 Typical values are nil or '(\".gz\" \".Z\").") ; .z is dead | |
668 | |
669 (defun ffap-locate-file (file &optional nosuffix path) | |
670 ;; Note the Emacs 20 version of locate-library could almost | |
671 ;; replace this function, except that it does not let us overrride | |
672 ;; the list of suffixes. | |
673 "A generic path-searching function, mimics `load' by default. | |
674 Returns path to file that \(load FILE\) would load, or nil. | |
675 Optional NOSUFFIX, if nil or t, is like the fourth argument | |
676 for load: whether to try the suffixes (\".elc\" \".el\" \"\"). | |
677 If a nonempty list, it is a list of suffixes to try instead. | |
678 Optional PATH is a list of directories instead of `load-path'." | |
679 (or path (setq path load-path)) | |
680 (if (file-name-absolute-p file) | |
681 (setq path (list (file-name-directory file)) | |
682 file (file-name-nondirectory file))) | |
683 (let ((suffixes-to-try | |
684 (cond | |
685 ((consp nosuffix) nosuffix) | |
686 (nosuffix '("")) | |
687 (t '(".elc" ".el" ""))))) | |
688 ;; Note we no longer check for old versions of jka-compr, that | |
689 ;; would aggressively try to convert any foo to foo.gz. | |
690 (or (listp ffap-locate-jka-suffixes) | |
691 (setq ffap-locate-jka-suffixes | |
692 (and (rassq 'jka-compr-handler file-name-handler-alist) | |
693 '(".gz" ".Z")))) ; ".z" is dead, "" is implicit | |
694 (if ffap-locate-jka-suffixes ; | |
695 (setq suffixes-to-try | |
696 (apply 'nconc | |
697 (mapcar | |
698 (function | |
699 (lambda (suf) | |
700 (cons suf | |
701 (mapcar | |
702 (function (lambda (x) (concat suf x))) | |
703 ffap-locate-jka-suffixes)))) | |
704 suffixes-to-try)))) | |
705 (let (found suffixes) | |
706 (while (and path (not found)) | |
707 (setq suffixes suffixes-to-try) | |
708 (while (and suffixes (not found)) | |
709 (let ((try (expand-file-name | |
710 (concat file (car suffixes)) | |
711 (car path)))) | |
712 (if (and (file-exists-p try) (not (file-directory-p try))) | |
713 (setq found try))) | |
714 (setq suffixes (cdr suffixes))) | |
715 (setq path (cdr path))) | |
716 found))) | |
717 | |
718 | |
719 ;;; Action List (`ffap-alist'): | |
720 ;; | |
721 ;; These search actions depend on the major-mode or regexps matching | |
722 ;; the current name. The little functions and their variables are | |
723 ;; deferred to the next section, at some loss of "code locality". A | |
724 ;; good example of featuritis. Trim this list for speed. | |
606 | 725 |
607 (defvar ffap-alist | 726 (defvar ffap-alist |
608 ;; A big mess! Parts are probably useless. | |
609 '( | 727 '( |
610 ("\\.info\\'" . ffap-info) | 728 ("" . ffap-completable) ; completion, slow on some systems |
611 ;; Since so many info files do not have .info extension, also do this: | 729 ("\\.info\\'" . ffap-info) ; gzip.info |
612 ("\\`info/" . ffap-info-2) | 730 ("\\`info/" . ffap-info-2) ; info/emacs |
613 ("\\`[-a-z]+\\'" . ffap-info-3) | 731 ("\\`[-a-z]+\\'" . ffap-info-3) ; (emacs)Top [only in the parentheses] |
614 ("\\.elc?\\'" . ffap-el) | 732 ("\\.elc?\\'" . ffap-el) ; simple.el, simple.elc |
615 (emacs-lisp-mode . ffap-el-mode) | 733 (emacs-lisp-mode . ffap-el-mode) ; rmail, gnus, simple, custom |
616 (finder-mode . ffap-el-mode) ; v19: {C-h p} | 734 (finder-mode . ffap-el-mode) ; type {C-h p} and try it |
617 (help-mode . ffap-el-mode) ; v19.29 | 735 (help-mode . ffap-el-mode) ; maybe useful |
618 (c++-mode . ffap-c-mode) | 736 (c++-mode . ffap-c-mode) ; search ffap-c-path |
619 (cc-mode . ffap-c-mode) | 737 (cc-mode . ffap-c-mode) ; same |
620 ("\\.\\([chCH]\\|cc\\|hh\\)\\'" . ffap-c-mode) | 738 ("\\.\\([chCH]\\|cc\\|hh\\)\\'" . ffap-c-mode) ; stdio.h |
621 (tex-mode . ffap-tex-mode) | 739 (fortran-mode . ffap-fortran-mode) ; FORTRAN requested by MDB |
622 (latex-mode . ffap-latex-mode) | 740 ("\\.[fF]\\'" . ffap-fortran-mode) |
741 (tex-mode . ffap-tex-mode) ; search ffap-tex-path | |
742 (latex-mode . ffap-latex-mode) ; similar | |
623 ("\\.\\(tex\\|sty\\|doc\\|cls\\)\\'" . ffap-tex) | 743 ("\\.\\(tex\\|sty\\|doc\\|cls\\)\\'" . ffap-tex) |
624 ("\\.bib\\'" . ffap-bib) | 744 ("\\.bib\\'" . ffap-bib) ; search ffap-bib-path |
625 ("\\`\\." . ffap-home) | 745 ("\\`\\." . ffap-home) ; .emacs, .bashrc, .profile |
626 ("\\`~/" . ffap-lcd) | 746 ("\\`~/" . ffap-lcd) ; |~/misc/ffap.el.Z| |
627 ("^[Rr][Ff][Cc][- #]?\\([0-9]+\\)" ; no $ | 747 ("^[Rr][Ff][Cc][- #]?\\([0-9]+\\)" ; no $ |
628 . ffap-rfc) | 748 . ffap-rfc) ; "100% RFC2100 compliant" |
629 ("\\`[^/]*\\'" . ffap-dired)) | 749 (dired-mode . ffap-dired) ; maybe in a subdirectory |
750 ) | |
630 "Alist of \(KEY . FUNCTION\) pairs parsed by `ffap-file-at-point'. | 751 "Alist of \(KEY . FUNCTION\) pairs parsed by `ffap-file-at-point'. |
631 If string NAME at point (maybe \"\") is not a file or url, these pairs | 752 If string NAME at point (maybe \"\") is not a file or url, these pairs |
632 specify actions to try creating such a string. A pair matches if either | 753 specify actions to try creating such a string. A pair matches if either |
633 KEY is a symbol, and it equals `major-mode', or | 754 KEY is a symbol, and it equals `major-mode', or |
634 KEY is a string, it should matches NAME as a regexp. | 755 KEY is a string, it should matches NAME as a regexp. |
635 On a match, \(FUNCTION NAME\) is called and should return a file, an | 756 On a match, \(FUNCTION NAME\) is called and should return a file, an |
636 url, or nil. If nil, search the alist for further matches.") | 757 url, or nil. If nil, search the alist for further matches.") |
637 | 758 |
638 (put 'ffap-alist 'risky-local-variable t) | 759 (put 'ffap-alist 'risky-local-variable t) |
639 | 760 |
640 (defun ffap-home (name) (locate-library name t '("~"))) | 761 |
762 ;;; Action Definitions: | |
763 ;; | |
764 ;; Define various default members of `ffap-alist'. | |
765 | |
766 (defun ffap-completable (name) | |
767 (let* ((dir (or (file-name-directory name) default-directory)) | |
768 (cmp (file-name-completion (file-name-nondirectory name) dir))) | |
769 (and cmp (concat dir cmp)))) | |
770 | |
771 (defun ffap-home (name) (ffap-locate-file name t '("~"))) | |
641 | 772 |
642 (defun ffap-info (name) | 773 (defun ffap-info (name) |
643 (locate-library | 774 (ffap-locate-file |
644 name '("" ".info") | 775 name '("" ".info") |
645 (or (ffap-soft-value "Info-directory-list") | 776 (or (ffap-soft-value "Info-directory-list") |
646 (ffap-soft-value "Info-default-directory-list") | 777 (ffap-soft-value "Info-default-directory-list") |
647 ;; v18: | 778 ))) |
648 (list (ffap-soft-value "Info-directory" "~/info/"))))) | |
649 | 779 |
650 (defun ffap-info-2 (name) (ffap-info (substring name 5))) | 780 (defun ffap-info-2 (name) (ffap-info (substring name 5))) |
651 | 781 |
652 ;; This ignores the node! "(emacs)Top" same as "(emacs)Intro" | |
653 (defun ffap-info-3 (name) | 782 (defun ffap-info-3 (name) |
783 ;; This ignores the node! "(emacs)Top" same as "(emacs)Intro" | |
654 (and (equal (ffap-string-around) "()") (ffap-info name))) | 784 (and (equal (ffap-string-around) "()") (ffap-info name))) |
655 | 785 |
656 (defun ffap-el (name) (locate-library name t)) | 786 (defun ffap-el (name) (ffap-locate-file name t)) |
657 | 787 |
658 ;; Need better defaults here! | 788 (defun ffap-el-mode (name) |
659 (defvar ffap-c-path '("/usr/include" "/usr/local/include")) | 789 ;; If name == "foo.el" we will skip it, since ffap-el already |
790 ;; searched for it once. (This assumes the default ffap-alist.) | |
791 (and (not (string-match "\\.el\\'" name)) | |
792 (ffap-locate-file name '(".el")))) | |
793 | |
794 (defvar ffap-c-path | |
795 ;; Need smarter defaults here! Suggestions welcome. | |
796 '("/usr/include" "/usr/local/include")) | |
660 (defun ffap-c-mode (name) | 797 (defun ffap-c-mode (name) |
661 (locate-library name t ffap-c-path)) | 798 (ffap-locate-file name t ffap-c-path)) |
662 | 799 |
663 (defun ffap-el-mode (name) | 800 (defvar ffap-fortran-path '("../include" "/usr/include")) |
664 ;; We do not bother with "" here, since it was considered above. | 801 |
665 ;; Also ignore "elc", for speed (who else reads elc files?) | 802 (defun ffap-fortran-mode (name) |
666 (and (not (string-match "\\.el\\'" name)) | 803 (ffap-locate-file name t ffap-fortran-path)) |
667 (locate-library name '(".el")))) | 804 |
668 | |
669 ;; Complicated because auctex may not be loaded yet. | |
670 (defvar ffap-tex-path | 805 (defvar ffap-tex-path |
671 t ; delayed initialization | 806 t ; delayed initialization |
672 "Path where `ffap-tex-mode' looks for tex files. | 807 "Path where `ffap-tex-mode' looks for tex files. |
673 If t, `ffap-tex-init' will initialize this when needed.") | 808 If t, `ffap-tex-init' will initialize this when needed.") |
674 | 809 |
675 (defun ffap-tex-init nil | 810 (defun ffap-tex-init nil |
676 ;; Compute ffap-tex-path if it is now t. | 811 ;; Compute ffap-tex-path if it is now t. |
677 (and (eq t ffap-tex-path) | 812 (and (eq t ffap-tex-path) |
813 ;; this may be slow, so say something | |
678 (message "Initializing ffap-tex-path ...") | 814 (message "Initializing ffap-tex-path ...") |
679 (setq ffap-tex-path | 815 (setq ffap-tex-path |
680 (ffap-reduce-path | 816 (ffap-reduce-path |
681 (append | 817 (cons |
682 (list ".") | 818 "." |
683 (ffap-list-env "TEXINPUTS") | 819 (ffap-kpathsea-expand-path |
684 ;; (ffap-list-env "BIBINPUTS") | 820 (append |
685 (ffap-add-subdirs | 821 (ffap-list-env "TEXINPUTS") |
686 (ffap-list-env "TEXINPUTS_SUBDIR" | 822 ;; (ffap-list-env "BIBINPUTS") |
687 (ffap-soft-value | 823 (ffap-soft-value |
688 "TeX-macro-global" | 824 "TeX-macro-global" ; AUCTeX |
689 '("/usr/local/lib/tex/macros" | 825 '("/usr/local/lib/tex/macros" |
690 "/usr/local/lib/tex/inputs") | 826 "/usr/local/lib/tex/inputs"))))))))) |
691 )))))))) | |
692 | 827 |
693 (defun ffap-tex-mode (name) | 828 (defun ffap-tex-mode (name) |
694 (ffap-tex-init) | 829 (ffap-tex-init) |
695 (locate-library name '(".tex" "") ffap-tex-path)) | 830 (ffap-locate-file name '(".tex" "") ffap-tex-path)) |
696 | 831 |
697 (defun ffap-latex-mode (name) | 832 (defun ffap-latex-mode (name) |
698 (ffap-tex-init) | 833 (ffap-tex-init) |
699 ;; Any real need for "" here? | 834 ;; only rare need for "" |
700 (locate-library name '(".cls" ".sty" ".tex" "") | 835 (ffap-locate-file name '(".cls" ".sty" ".tex" "") ffap-tex-path)) |
701 ffap-tex-path)) | |
702 | 836 |
703 (defun ffap-tex (name) | 837 (defun ffap-tex (name) |
704 (ffap-tex-init) | 838 (ffap-tex-init) |
705 (locate-library name t ffap-tex-path)) | 839 (ffap-locate-file name t ffap-tex-path)) |
840 | |
841 (defvar ffap-bib-path | |
842 (ffap-list-env "BIBINPUTS" | |
843 (ffap-reduce-path | |
844 '( | |
845 ;; a few wild guesses, need better | |
846 "/usr/local/lib/tex/macros/bib" ; Solaris? | |
847 "/usr/lib/texmf/bibtex/bib" ; Linux? | |
848 )))) | |
706 | 849 |
707 (defun ffap-bib (name) | 850 (defun ffap-bib (name) |
708 (locate-library | 851 (ffap-locate-file name t ffap-bib-path)) |
709 name t | |
710 (ffap-list-env "BIBINPUTS" '("/usr/local/lib/tex/macros/bib")))) | |
711 | 852 |
712 (defun ffap-dired (name) | 853 (defun ffap-dired (name) |
713 (let ((pt (point)) dir try) | 854 (let ((pt (point)) dir try) |
714 (save-excursion | 855 (save-excursion |
715 (and (progn | 856 (and (progn |
748 (concat (ffap-host-to-path "ds.internic.net") "/rfc/rfc%s.txt")) | 889 (concat (ffap-host-to-path "ds.internic.net") "/rfc/rfc%s.txt")) |
749 | 890 |
750 (defun ffap-rfc (name) | 891 (defun ffap-rfc (name) |
751 (format ffap-rfc-path | 892 (format ffap-rfc-path |
752 (substring name (match-beginning 1) (match-end 1)))) | 893 (substring name (match-beginning 1) (match-end 1)))) |
894 | |
753 | 895 |
754 ;;; At-Point Functions: | 896 ;;; At-Point Functions: |
755 | 897 |
756 (defvar ffap-string-at-point-mode-alist | 898 (defvar ffap-string-at-point-mode-alist |
757 '( | 899 '( |
759 ;; Slightly controversial decisions: | 901 ;; Slightly controversial decisions: |
760 ;; * strip trailing "@" and ":" | 902 ;; * strip trailing "@" and ":" |
761 ;; * no commas (good for latex) | 903 ;; * no commas (good for latex) |
762 (file "--:$+<>@-Z_a-z~" "<@" "@>;.,!?:") | 904 (file "--:$+<>@-Z_a-z~" "<@" "@>;.,!?:") |
763 ;; An url, or maybe a email/news message-id: | 905 ;; An url, or maybe a email/news message-id: |
764 (url "--:?$+@-Z_a-z~#,%" "^A-Za-z0-9" ":;.,!?") | 906 (url "--:=&?$+@-Z_a-z~#,%" "^A-Za-z0-9" ":;.,!?") |
765 ;; Find a string that does *not* contain a colon: | 907 ;; Find a string that does *not* contain a colon: |
766 (nocolon "--9$+<>@-Z_a-z~" "<@" "@>;.,!?") | 908 (nocolon "--9$+<>@-Z_a-z~" "<@" "@>;.,!?") |
767 ;; A machine: | 909 ;; A machine: |
768 (machine "-a-zA-Z0-9." "" ".") | 910 (machine "-a-zA-Z0-9." "" ".") |
769 ;; Mathematica paths: allow backquotes | 911 ;; Mathematica paths: allow backquotes |
800 (setcar ffap-string-at-point-region (point))) | 942 (setcar ffap-string-at-point-region (point))) |
801 (save-excursion | 943 (save-excursion |
802 (skip-chars-forward (car args)) | 944 (skip-chars-forward (car args)) |
803 (skip-chars-backward (nth 2 args) pt) | 945 (skip-chars-backward (nth 2 args) pt) |
804 (setcar (cdr ffap-string-at-point-region) (point)))))) | 946 (setcar (cdr ffap-string-at-point-region) (point)))))) |
805 (or ffap-xemacs (set-text-properties 0 (length str) nil str)) | 947 (set-text-properties 0 (length str) nil str) |
806 (setq ffap-string-at-point str))) | 948 (setq ffap-string-at-point str))) |
807 | 949 |
808 (defun ffap-string-around nil | 950 (defun ffap-string-around nil |
809 ;; Sometimes useful to decide how to treat a string. | 951 ;; Sometimes useful to decide how to treat a string. |
810 "Return string of two chars around last `ffap-string-at-point'. | 952 "Return string of two chars around last `ffap-string-at-point'. |
833 (defun ffap-url-at-point nil | 975 (defun ffap-url-at-point nil |
834 "Return url from around point if it exists, or nil." | 976 "Return url from around point if it exists, or nil." |
835 ;; Could use w3's url-get-url-at-point instead. Both handle "URL:", | 977 ;; Could use w3's url-get-url-at-point instead. Both handle "URL:", |
836 ;; ignore non-relative links, trim punctuation. The other will | 978 ;; ignore non-relative links, trim punctuation. The other will |
837 ;; actually look back if point is in whitespace, but I would rather | 979 ;; actually look back if point is in whitespace, but I would rather |
838 ;; ffap be non-rabid in such situations. | 980 ;; ffap be less aggressive in such situations. |
839 (and | 981 (and |
840 ffap-url-regexp | 982 ffap-url-regexp |
841 (or | 983 (or |
842 ;; In a w3 buffer button zone? | 984 ;; In a w3 buffer button? |
843 (let (tem) | 985 (and (eq major-mode 'w3-mode) |
844 (and (eq major-mode 'w3-mode) | 986 ;; interface recommended by wmperry: |
845 ;; assume: (boundp 'w3-zone-at) (boundp 'w3-zone-data) | 987 (w3-view-this-url t)) |
846 (setq tem (w3-zone-at (point))) | |
847 (consp (setq tem (w3-zone-data tem))) | |
848 (nth 2 tem))) | |
849 ;; Is there a reason not to strip trailing colon? | 988 ;; Is there a reason not to strip trailing colon? |
850 (let ((name (ffap-string-at-point 'url))) | 989 (let ((name (ffap-string-at-point 'url))) |
851 ;; (case-fold-search t), why? | |
852 (cond | 990 (cond |
853 ((string-match "^url:" name) (setq name (substring name 4))) | 991 ((string-match "^url:" name) (setq name (substring name 4))) |
854 ((and (string-match "\\`[^:</>@]+@[^:</>@]+[a-zA-Z]\\'" name) | 992 ((and (string-match "\\`[^:</>@]+@[^:</>@]+[a-zA-Z0-9]\\'" name) |
855 ;; "foo@bar": could be "mailto" or "news" (a Message-ID). | 993 ;; "foo@bar": could be "mailto" or "news" (a Message-ID). |
856 ;; If not adorned with "<>", it must be "mailto". | 994 ;; Without "<>" it must be "mailto". Otherwise could be |
857 ;; Otherwise could be either, so consult `ffap-foo-at-bar-prefix'. | 995 ;; either, so consult `ffap-foo-at-bar-prefix'. |
858 (let ((prefix (if (and (equal (ffap-string-around) "<>") | 996 (let ((prefix (if (and (equal (ffap-string-around) "<>") |
859 ;; At least a couple of odd characters: | 997 ;; Expect some odd characters: |
860 (string-match "[$.0-9].*[$.0-9].*@" name)) | 998 (string-match "[$.0-9].*[$.0-9].*@" name)) |
861 ;; Could be news: | 999 ;; Could be news: |
862 ffap-foo-at-bar-prefix | 1000 ffap-foo-at-bar-prefix |
863 "mailto"))) | 1001 "mailto"))) |
864 (and prefix (setq name (concat prefix ":" name)))))) | 1002 (and prefix (setq name (concat prefix ":" name)))))) |
963 (while (and alist (not try)) | 1101 (while (and alist (not try)) |
964 (setq tem (car alist) alist (cdr alist)) | 1102 (setq tem (car alist) alist (cdr alist)) |
965 (if (or (eq major-mode (car tem)) | 1103 (if (or (eq major-mode (car tem)) |
966 (and (stringp (car tem)) | 1104 (and (stringp (car tem)) |
967 (string-match (car tem) name))) | 1105 (string-match (car tem) name))) |
968 (and (setq try (funcall (cdr tem) name)) | 1106 (and (setq try |
1107 (condition-case nil | |
1108 (funcall (cdr tem) name) | |
1109 (error nil))) | |
969 (setq try (or | 1110 (setq try (or |
970 (ffap-url-p try) ; not a file! | 1111 (ffap-url-p try) ; not a file! |
971 (ffap-file-remote-p try) | 1112 (ffap-file-remote-p try) |
972 (ffap-file-exists-string try)))))) | 1113 (ffap-file-exists-string try)))))) |
973 try)) | 1114 try)) |
997 (ffap-replace-path-component remote-dir name)))))) | 1138 (ffap-replace-path-component remote-dir name)))))) |
998 ) | 1139 ) |
999 (store-match-data data)))) | 1140 (store-match-data data)))) |
1000 | 1141 |
1001 | 1142 |
1002 ;;; ffap-read-file-or-url: | 1143 ;;; Prompting (`ffap-read-file-or-url'): |
1003 ;; | 1144 ;; |
1004 ;; We want to complete filenames as in read-file-name, but also url's | 1145 ;; We want to complete filenames as in read-file-name, but also url's |
1005 ;; which read-file-name-internal would truncate at the "//" string. | 1146 ;; which read-file-name-internal would truncate at the "//" string. |
1006 ;; The solution here is to replace read-file-name-internal with | 1147 ;; The solution here is to replace read-file-name-internal with |
1007 ;; `ffap-read-file-or-url-internal', which checks the minibuffer | 1148 ;; `ffap-read-file-or-url-internal', which checks the minibuffer |
1081 'read-file-name-internal | 1222 'read-file-name-internal |
1082 minibuffer-completion-table))) | 1223 minibuffer-completion-table))) |
1083 ad-do-it)))) | 1224 ad-do-it)))) |
1084 | 1225 |
1085 | 1226 |
1086 ;;; Highlighting: | 1227 ;;; Highlighting (`ffap-highlight'): |
1087 ;; | 1228 ;; |
1088 ;; Based on overlay highlighting in Emacs 19.28 isearch.el. | 1229 ;; Based on overlay highlighting in Emacs 19.28 isearch.el. |
1089 | 1230 |
1090 (defvar ffap-highlight (and window-system t) | 1231 (defvar ffap-highlight (and window-system t) |
1091 "If non-nil, ffap highlights the current buffer substring.") | 1232 "If non-nil, ffap highlights the current buffer substring.") |
1092 | 1233 |
1093 (defvar ffap-highlight-overlay nil "Overlay used by `ffap-highlight'.") | 1234 (defvar ffap-highlight-overlay nil |
1235 "Overlay used by `ffap-highlight'.") | |
1094 | 1236 |
1095 (defun ffap-highlight (&optional remove) | 1237 (defun ffap-highlight (&optional remove) |
1096 "If `ffap-highlight' is set, highlight the guess in this buffer. | 1238 "If `ffap-highlight' is set, highlight the guess in this buffer. |
1097 That is, the last buffer substring found by `ffap-string-at-point'. | 1239 That is, the last buffer substring found by `ffap-string-at-point'. |
1098 Optional argument REMOVE means to remove any such highlighting. | 1240 Optional argument REMOVE means to remove any such highlighting. |
1099 Uses the face `ffap' if it is defined, or else `highlight'." | 1241 Uses the face `ffap' if it is defined, or else `highlight'." |
1100 (cond | 1242 (cond |
1101 (remove (and ffap-highlight-overlay (delete-overlay ffap-highlight-overlay))) | 1243 (remove |
1244 (and ffap-highlight-overlay | |
1245 (ffap-delete-overlay ffap-highlight-overlay))) | |
1102 ((not ffap-highlight) nil) | 1246 ((not ffap-highlight) nil) |
1103 (ffap-highlight-overlay | 1247 (ffap-highlight-overlay |
1104 (move-overlay ffap-highlight-overlay | 1248 (ffap-move-overlay ffap-highlight-overlay |
1105 (car ffap-string-at-point-region) | 1249 (car ffap-string-at-point-region) |
1106 (nth 1 ffap-string-at-point-region) | 1250 (nth 1 ffap-string-at-point-region) |
1107 (current-buffer))) | 1251 (current-buffer))) |
1108 (t | 1252 (t |
1109 (setq ffap-highlight-overlay (apply 'make-overlay ffap-string-at-point-region)) | 1253 (setq ffap-highlight-overlay |
1110 (overlay-put ffap-highlight-overlay 'face | 1254 (apply 'ffap-make-overlay ffap-string-at-point-region)) |
1111 (if (internal-find-face 'ffap nil) | 1255 (ffap-overlay-put ffap-highlight-overlay 'face |
1112 'ffap 'highlight))))) | 1256 (if (ffap-find-face 'ffap) |
1113 | 1257 'ffap 'highlight))))) |
1114 | 1258 |
1115 ;;; The big enchilada: | 1259 |
1260 ;;; The big cheese (`ffap'): | |
1116 | 1261 |
1117 (defun ffap-guesser nil | 1262 (defun ffap-guesser nil |
1118 "Return file or url or nil, guessed from text around point." | 1263 "Return file or URL or nil, guessed from text around point." |
1119 (or (and ffap-url-regexp | 1264 (or (and ffap-url-regexp |
1120 (ffap-fixup-url (or (ffap-url-at-point) | 1265 (ffap-fixup-url (or (ffap-url-at-point) |
1121 (ffap-gopher-at-point)))) | 1266 (ffap-gopher-at-point)))) |
1122 (ffap-file-at-point) ; may yield url! | 1267 (ffap-file-at-point) ; may yield url! |
1123 (ffap-fixup-machine (ffap-machine-at-point)))) | 1268 (ffap-fixup-machine (ffap-machine-at-point)))) |
1134 )) | 1279 )) |
1135 (ffap-highlight t))) | 1280 (ffap-highlight t))) |
1136 | 1281 |
1137 ;;;###autoload | 1282 ;;;###autoload |
1138 (defun find-file-at-point (&optional filename) | 1283 (defun find-file-at-point (&optional filename) |
1139 "Find FILENAME (or url), guessing default from text around point. | 1284 "Find FILENAME, guessing a default from text around point. |
1140 If `ffap-dired-wildcards' is set, wildcard patterns are passed to dired. | 1285 If `ffap-url-regexp' is not nil, the FILENAME may also be an URL. |
1141 See also the functions `ffap-file-at-point', `ffap-url-at-point'. | 1286 With a prefix, this command behaves exactly like `ffap-file-finder'. |
1142 With a prefix, this command behaves *exactly* like `ffap-file-finder'. | |
1143 If `ffap-require-prefix' is set, the prefix meaning is reversed. | 1287 If `ffap-require-prefix' is set, the prefix meaning is reversed. |
1288 See also the variables `ffap-dired-wildcards', `ffap-newfile-prompt', | |
1289 and the functions `ffap-file-at-point' and `ffap-url-at-point'. | |
1144 | 1290 |
1145 See <ftp://ftp.mathcs.emory.edu/pub/mic/emacs/> for latest version." | 1291 See <ftp://ftp.mathcs.emory.edu/pub/mic/emacs/> for latest version." |
1146 (interactive) | 1292 (interactive) |
1147 (if (and (interactive-p) | 1293 (if (and (interactive-p) |
1148 (if ffap-require-prefix (not current-prefix-arg) | 1294 (if ffap-require-prefix (not current-prefix-arg) |
1168 ;; User does not want to find a non-existent file: | 1314 ;; User does not want to find a non-existent file: |
1169 ((signal 'file-error (list "Opening file buffer" | 1315 ((signal 'file-error (list "Opening file buffer" |
1170 "no such file or directory" | 1316 "no such file or directory" |
1171 filename)))))) | 1317 filename)))))) |
1172 | 1318 |
1173 ;; M-x shortcut: | 1319 ;; Shortcut: allow {M-x ffap} rather than {M-x find-file-at-point}. |
1174 ;;###autoload | 1320 ;; The defun is for autoload.el; the defalias takes over at load time. |
1321 ;;;###autoload | |
1322 (defun ffap (&optional filename) | |
1323 "A short alias for the find-file-at-point command.") | |
1175 (defalias 'ffap 'find-file-at-point) | 1324 (defalias 'ffap 'find-file-at-point) |
1176 | 1325 |
1177 | 1326 |
1178 ;;; Menu support: | 1327 ;;; Menu support (`ffap-menu'): |
1179 ;; | |
1180 ;; Bind ffap-menu to a key if you want, since it also works in tty mode. | |
1181 ;; Or just use it through the ffap-at-mouse binding (next section). | |
1182 | 1328 |
1183 (defvar ffap-menu-regexp nil | 1329 (defvar ffap-menu-regexp nil |
1184 "*If non-nil, overrides `ffap-next-regexp' during `ffap-menu'. | 1330 "*If non-nil, overrides `ffap-next-regexp' during `ffap-menu'. |
1185 Make this more restrictive for faster menu building. | 1331 Make this more restrictive for faster menu building. |
1186 For example, try \":/\" for url (and some ftp) references.") | 1332 For example, try \":/\" for URL (and some ftp) references.") |
1187 | 1333 |
1188 (defvar ffap-menu-alist nil | 1334 (defvar ffap-menu-alist nil |
1189 "Buffer local cache of menu presented by `ffap-menu'.") | 1335 "Buffer local cache of menu presented by `ffap-menu'.") |
1190 (make-variable-buffer-local 'ffap-menu-alist) | 1336 (make-variable-buffer-local 'ffap-menu-alist) |
1191 | 1337 |
1192 (defvar ffap-menu-text-plist | 1338 (defvar ffap-menu-text-plist |
1193 (and window-system | 1339 (and window-system |
1194 ;; These choices emulate goto-addr: | 1340 '(face bold mouse-face highlight) ; keymap <mousy-map> |
1195 (if ffap-xemacs | 1341 ) |
1196 '(face bold highlight t) ; keymap <map> | |
1197 '(face bold mouse-face highlight) ; keymap <mousy-map> | |
1198 )) | |
1199 "Text properties applied to strings found by `ffap-menu-rescan'. | 1342 "Text properties applied to strings found by `ffap-menu-rescan'. |
1200 These properties may be used to fontify the menu references.") | 1343 These properties may be used to fontify the menu references.") |
1201 | 1344 |
1202 ;;;###autoload | 1345 ;;;###autoload |
1203 (defun ffap-menu (&optional rescan) | 1346 (defun ffap-menu (&optional rescan) |
1235 (find-file-at-point (car choice))) | 1378 (find-file-at-point (car choice))) |
1236 (ffap-highlight t)))) | 1379 (ffap-highlight t)))) |
1237 | 1380 |
1238 (defun ffap-menu-ask (title alist cont) | 1381 (defun ffap-menu-ask (title alist cont) |
1239 "Prompt from a menu of choices, and then apply some action. | 1382 "Prompt from a menu of choices, and then apply some action. |
1240 Arguments are TITLE, ALIST, and CONT (a continuation). | 1383 Arguments are TITLE, ALIST, and CONT \(a continuation function\). |
1241 This uses either a menu or the minibuffer depending on invocation. | 1384 This uses either a menu or the minibuffer depending on invocation. |
1242 The TITLE string is used as either the prompt or menu title. | 1385 The TITLE string is used as either the prompt or menu title. |
1243 Each \(string . data\) ALIST entry defines a choice \(data is ignored\). | 1386 Each \(string . data\) ALIST entry defines a choice. |
1244 Once the user makes a choice, function CONT is applied to the entry. | 1387 Function CONT is applied to the entry chosen by the user." |
1245 Always returns nil." | 1388 ;; Note: this function is used with a different continuation |
1246 ;; Bug: minibuffer prompting assumes the strings are unique. | 1389 ;; by the ffap-url add-on package. |
1247 (let ((choice | 1390 ;; Could try rewriting to use easymenu.el or lmenu.el. |
1248 (if (and (fboundp 'x-popup-menu) ; Emacs 19 or XEmacs 19.13 | 1391 (let (choice) |
1249 (boundp 'last-nonmenu-event) ; not in XEmacs 19.13 | 1392 (cond |
1250 (listp last-nonmenu-event)) | 1393 ;; Emacs mouse: |
1251 (x-popup-menu | 1394 ((and (fboundp 'x-popup-menu) (ffap-mouse-event)) |
1252 t | 1395 (setq choice |
1253 (list "" | 1396 (x-popup-menu |
1254 (cons title | 1397 t |
1255 (mapcar | 1398 (list "" (cons title |
1256 (function (lambda (i) (cons (car i) i))) | 1399 (mapcar (function (lambda (i) (cons (car i) i))) |
1257 alist)))) | 1400 alist)))))) |
1258 ;; Immediately popup completion buffer: | 1401 ;; minibuffer with completion buffer: |
1259 (prog1 | 1402 (t |
1260 (let ((minibuffer-setup-hook 'minibuffer-completion-help)) | 1403 (let ((minibuffer-setup-hook 'minibuffer-completion-help)) |
1261 ;; BUG: this code assumes that "" is not a valid choice | 1404 ;; Bug: prompting may assume unique strings, no "". |
1262 (completing-read | 1405 (setq choice |
1263 (format "%s (default %s): " title (car (car alist))) | 1406 (completing-read |
1264 alist nil t | 1407 (format "%s (default %s): " title (car (car alist))) |
1265 ;; (cons (car (car alist)) 0) | 1408 alist nil t |
1266 nil | 1409 ;; (cons (car (car alist)) 0) |
1267 )) | 1410 nil))) |
1268 ;; Redraw original screen: | 1411 (sit-for 0) ; redraw original screen |
1269 (sit-for 0))))) | 1412 ;; Convert string to its entry, or else the default: |
1270 ;; Defaulting: convert "" to (car (car alist)) | 1413 (setq choice (or (assoc choice alist) (car alist)))) |
1271 (and (equal choice "") (setq choice (car (car alist)))) | 1414 ) |
1272 (and (stringp choice) (setq choice (assoc choice alist))) | 1415 (if choice |
1273 (if choice (funcall cont choice) (message "No choice made!"))) | 1416 (funcall cont choice) |
1274 nil) ; return nothing | 1417 (message "No choice made!") ; possible with menus |
1418 nil))) | |
1275 | 1419 |
1276 (defun ffap-menu-rescan nil | 1420 (defun ffap-menu-rescan nil |
1277 "Search buffer for `ffap-menu-regexp' to build `ffap-menu-alist'. | 1421 "Search buffer for `ffap-menu-regexp' to build `ffap-menu-alist'. |
1278 Applies `ffap-menu-text-plist' text properties at all matches." | 1422 Applies `ffap-menu-text-plist' text properties at all matches." |
1279 (interactive) | 1423 (interactive) |
1280 (let ((ffap-next-regexp (or ffap-menu-regexp ffap-next-regexp)) | 1424 (let ((ffap-next-regexp (or ffap-menu-regexp ffap-next-regexp)) |
1281 (range (- (point-max) (point-min))) item | 1425 (range (- (point-max) (point-min))) |
1426 (mod (buffer-modified-p)) ; was buffer modified? | |
1282 buffer-read-only ; to set text-properties | 1427 buffer-read-only ; to set text-properties |
1428 item | |
1283 ;; Avoid repeated searches of the *mode-alist: | 1429 ;; Avoid repeated searches of the *mode-alist: |
1284 (major-mode (if (assq major-mode ffap-string-at-point-mode-alist) | 1430 (major-mode (if (assq major-mode ffap-string-at-point-mode-alist) |
1285 major-mode | 1431 major-mode |
1286 'file)) | 1432 'file))) |
1287 ) | |
1288 (setq ffap-menu-alist nil) | 1433 (setq ffap-menu-alist nil) |
1289 (save-excursion | 1434 (unwind-protect |
1290 (goto-char (point-min)) | 1435 (save-excursion |
1291 (while (setq item (ffap-next-guess)) | 1436 (goto-char (point-min)) |
1292 (setq ffap-menu-alist (cons (cons item (point)) ffap-menu-alist)) | 1437 (while (setq item (ffap-next-guess)) |
1293 (add-text-properties (car ffap-string-at-point-region) (point) | 1438 (setq ffap-menu-alist (cons (cons item (point)) ffap-menu-alist)) |
1294 ffap-menu-text-plist) | 1439 (add-text-properties (car ffap-string-at-point-region) (point) |
1295 (message "Scanning...%2d%% <%s>" | 1440 ffap-menu-text-plist) |
1296 (/ (* 100 (- (point) (point-min))) range) item)))) | 1441 (message "Scanning...%2d%% <%s>" |
1442 (/ (* 100 (- (point) (point-min))) range) item))) | |
1443 (or mod (set-buffer-modified-p nil)))) | |
1297 (message "Scanning...done") | 1444 (message "Scanning...done") |
1298 ;; Remove duplicates. | 1445 ;; Remove duplicates. |
1299 (setq ffap-menu-alist ; sort by item | 1446 (setq ffap-menu-alist ; sort by item |
1300 (sort ffap-menu-alist | 1447 (sort ffap-menu-alist |
1301 (function | 1448 (function |
1302 (lambda (a b) (string-lessp (car a) (car b)))))) | 1449 (lambda (a b) (string-lessp (car a) (car b)))))) |
1303 (let ((ptr ffap-menu-alist)) | 1450 (let ((ptr ffap-menu-alist)) ; remove duplicates |
1304 (while (cdr ptr) | 1451 (while (cdr ptr) |
1305 (if (equal (car (car ptr)) (car (car (cdr ptr)))) | 1452 (if (equal (car (car ptr)) (car (car (cdr ptr)))) |
1306 (setcdr ptr (cdr (cdr ptr))) | 1453 (setcdr ptr (cdr (cdr ptr))) |
1307 (setq ptr (cdr ptr))))) | 1454 (setq ptr (cdr ptr))))) |
1308 (setq ffap-menu-alist ; sort by position | 1455 (setq ffap-menu-alist ; sort by position |
1309 (sort ffap-menu-alist | 1456 (sort ffap-menu-alist |
1310 (function | 1457 (function |
1311 (lambda (a b) (< (cdr a) (cdr b))))))) | 1458 (lambda (a b) (< (cdr a) (cdr b))))))) |
1312 | 1459 |
1313 | 1460 |
1314 ;;; Mouse Support: | 1461 ;;; Mouse Support (`ffap-at-mouse'): |
1315 ;; | 1462 ;; |
1316 ;; See the suggested binding in ffap-bindings (near eof). | 1463 ;; See the suggested binding in ffap-bindings (near eof). |
1317 | 1464 |
1318 (defvar ffap-at-mouse-fallback 'ffap-menu | 1465 (defvar ffap-at-mouse-fallback nil ; ffap-menu? too time-consuming |
1319 "Invoked by `ffap-at-mouse' if no file or url at click. | 1466 "Command invoked by `ffap-at-mouse' if nothing found at click, or nil. |
1320 A command symbol, or nil for nothing.") | 1467 Ignored when `ffap-at-mouse' is called programmatically.") |
1321 (put 'ffap-at-mouse-fallback 'risky-local-variable t) | 1468 (put 'ffap-at-mouse-fallback 'risky-local-variable t) |
1322 | 1469 |
1470 ;;;###autoload | |
1323 (defun ffap-at-mouse (e) | 1471 (defun ffap-at-mouse (e) |
1324 "Find file or url guessed from text around mouse point. | 1472 "Find file or url guessed from text around mouse click. |
1325 If none is found, call `ffap-at-mouse-fallback'." | 1473 Interactively, calls `ffap-at-mouse-fallback' if nothing is found. |
1474 Returns t or nil to indicate success." | |
1326 (interactive "e") | 1475 (interactive "e") |
1327 (let ((guess | 1476 (let ((guess |
1328 ;; Maybe less surprising without the save-excursion? | 1477 ;; Maybe less surprising without the save-excursion? |
1329 (save-excursion | 1478 (save-excursion |
1330 (mouse-set-point e) | 1479 (mouse-set-point e) |
1331 ;; Would like to do nothing unless click was *on* text. How? | 1480 ;; Would prefer to do nothing unless click was *on* text. How |
1332 ;; (cdr (posn-col-row (event-start e))) is always same as | 1481 ;; to tell that the click was beyond the end of current line? |
1333 ;; current column. For posn-x-y, need pixel-width! | |
1334 (ffap-guesser)))) | 1482 (ffap-guesser)))) |
1335 (cond | 1483 (cond |
1336 (guess | 1484 (guess |
1485 (set-buffer (ffap-event-buffer e)) | |
1337 (ffap-highlight) | 1486 (ffap-highlight) |
1338 (unwind-protect | 1487 (unwind-protect |
1339 (progn | 1488 (progn |
1340 (sit-for 0) ; display | 1489 (sit-for 0) ; display |
1341 (message "Guessing `%s'" guess) | 1490 (message "Finding `%s'" guess) |
1342 (find-file-at-point guess)) | 1491 (find-file-at-point guess) |
1492 t) ; success: return non-nil | |
1343 (ffap-highlight t))) | 1493 (ffap-highlight t))) |
1344 ((and (interactive-p) | 1494 ((interactive-p) |
1345 ffap-at-mouse-fallback) | 1495 (if ffap-at-mouse-fallback |
1346 (call-interactively ffap-at-mouse-fallback)) | 1496 (call-interactively ffap-at-mouse-fallback) |
1347 ((message "No file or URL found at mouse click."))))) | 1497 (message "No file or url found at mouse click."))) |
1348 | 1498 ;; failure: return nil |
1349 | 1499 ))) |
1350 ;;; ffap-other-* commands | 1500 |
1351 ;; Suggested by KPC. | 1501 |
1502 ;;; ffap-other-* commands: | |
1503 ;; | |
1504 ;; Requested by KPC. | |
1505 | |
1506 ;; There could be a real `ffap-noselect' function, but we would need | |
1507 ;; at least two new user variables, and there is no w3-fetch-noselect. | |
1508 ;; So instead, we just fake it with a slow save-window-excursion. | |
1352 | 1509 |
1353 (defun ffap-other-window nil | 1510 (defun ffap-other-window nil |
1354 "Like `ffap', but put buffer in another window." | 1511 "Like `ffap', but put buffer in another window. |
1512 Only intended for interactive use." | |
1355 (interactive) | 1513 (interactive) |
1356 (switch-to-buffer-other-window | 1514 (switch-to-buffer-other-window |
1357 (save-window-excursion (call-interactively 'ffap) (current-buffer)))) | 1515 (save-window-excursion (call-interactively 'ffap) (current-buffer)))) |
1358 | 1516 |
1359 (defun ffap-other-frame nil | 1517 (defun ffap-other-frame nil |
1360 "Like `ffap', but put buffer in another frame." | 1518 "Like `ffap', but put buffer in another frame. |
1519 Only intended for interactive use." | |
1361 (interactive) | 1520 (interactive) |
1362 (switch-to-buffer-other-frame | 1521 ;; Extra code works around dedicated windows (noted by JENS, 7/96): |
1363 (save-window-excursion (call-interactively 'ffap) (current-buffer)))) | 1522 (let* ((win (selected-window)) (wdp (window-dedicated-p win))) |
1523 (unwind-protect | |
1524 (progn | |
1525 (set-window-dedicated-p win nil) | |
1526 (switch-to-buffer-other-frame | |
1527 (save-window-excursion | |
1528 (call-interactively 'ffap) | |
1529 (current-buffer)))) | |
1530 (set-window-dedicated-p win wdp)))) | |
1364 | 1531 |
1365 | 1532 |
1366 ;;; Bug Reporter: | 1533 ;;; Bug Reporter: |
1367 | 1534 |
1368 (defun ffap-bug nil | 1535 (defun ffap-bug nil |
1373 (interactive) | 1540 (interactive) |
1374 (require 'reporter) | 1541 (require 'reporter) |
1375 (let ((reporter-prompt-for-summary-p t)) | 1542 (let ((reporter-prompt-for-summary-p t)) |
1376 (reporter-submit-bug-report | 1543 (reporter-submit-bug-report |
1377 "Michelangelo Grigni <mic@mathcs.emory.edu>" | 1544 "Michelangelo Grigni <mic@mathcs.emory.edu>" |
1378 "ffap 1.6" | 1545 "ffap" ; version? just rely on Emacs version |
1379 (mapcar 'intern (all-completions "ffap-" obarray 'boundp))))) | 1546 (mapcar 'intern (all-completions "ffap-" obarray 'boundp))))) |
1380 | 1547 |
1381 (fset 'ffap-submit-bug 'ffap-bug) ; another likely name | 1548 (fset 'ffap-submit-bug 'ffap-bug) ; another likely name |
1382 | 1549 |
1383 | 1550 |
1422 (defun ffap-gnus-menu nil | 1589 (defun ffap-gnus-menu nil |
1423 "Run `ffap-menu' in the gnus article buffer." | 1590 "Run `ffap-menu' in the gnus article buffer." |
1424 (interactive) (ffap-gnus-wrapper '(ffap-menu))) | 1591 (interactive) (ffap-gnus-wrapper '(ffap-menu))) |
1425 | 1592 |
1426 | 1593 |
1427 ;;; ffap-bindings: offer default global bindings | 1594 ;;; Offer default global bindings (`ffap-bindings'): |
1428 | 1595 |
1429 (defvar ffap-bindings | 1596 (defvar ffap-bindings |
1430 (nconc | 1597 '( |
1431 (cond | 1598 (global-set-key [S-mouse-3] 'ffap-at-mouse) |
1432 ((not (eq window-system 'x)) | 1599 (global-set-key [C-S-mouse-3] 'ffap-menu) |
1433 nil) | 1600 (global-set-key "\C-x\C-f" 'find-file-at-point) |
1434 ;; GNU coding standards say packages should not bind S-mouse-*. | 1601 (global-set-key "\C-x4f" 'ffap-other-window) |
1435 ;; Is it ok to simply suggest such a binding to the user? | 1602 (global-set-key "\C-x5f" 'ffap-other-frame) |
1436 (ffap-xemacs | 1603 (add-hook 'gnus-summary-mode-hook 'ffap-gnus-hook) |
1437 '((global-set-key '(shift button3) 'ffap-at-mouse))) | 1604 (add-hook 'gnus-article-mode-hook 'ffap-gnus-hook) |
1438 (t | 1605 (add-hook 'vm-mode-hook 'ffap-ro-mode-hook) |
1439 '((global-set-key [S-down-mouse-3] 'ffap-at-mouse)))) | 1606 (add-hook 'rmail-mode-hook 'ffap-ro-mode-hook) |
1440 '( | 1607 ;; (setq dired-x-hands-off-my-keys t) ; the default |
1441 (global-set-key "\C-x\C-f" 'find-file-at-point) | 1608 ) |
1442 (global-set-key "\C-x4f" 'ffap-other-window) | 1609 "List of binding forms evaluated by function `ffap-bindings'. |
1443 (global-set-key "\C-x5f" 'ffap-other-frame) | |
1444 (add-hook 'gnus-summary-mode-hook 'ffap-gnus-hook) | |
1445 (add-hook 'gnus-article-mode-hook 'ffap-gnus-hook) | |
1446 (add-hook 'vm-mode-hook 'ffap-ro-mode-hook) | |
1447 (add-hook 'rmail-mode-hook 'ffap-ro-mode-hook) | |
1448 ;; (setq dired-x-hands-off-my-keys t) ; the default | |
1449 )) | |
1450 "List of forms evaluated by function `ffap-bindings'. | |
1451 A reasonable ffap installation needs just these two lines: | 1610 A reasonable ffap installation needs just these two lines: |
1452 (require 'ffap) | 1611 (require 'ffap) |
1453 (ffap-bindings) | 1612 (ffap-bindings) |
1454 These are only suggestions, they may be modified or ignored.") | 1613 Of course if you do not like these bindings, just roll your own!") |
1455 | 1614 |
1456 (defun ffap-bindings nil | 1615 (defun ffap-bindings nil |
1457 "Evaluate the forms in variable `ffap-bindings'." | 1616 "Evaluate the forms in variable `ffap-bindings'." |
1458 (eval (cons 'progn ffap-bindings))) | 1617 (eval (cons 'progn ffap-bindings))) |
1459 | 1618 |
1462 ;; (setq ffap-alist ; remove a feature in `ffap-alist' | 1621 ;; (setq ffap-alist ; remove a feature in `ffap-alist' |
1463 ;; (delete (assoc 'c-mode ffap-alist) ffap-alist)) | 1622 ;; (delete (assoc 'c-mode ffap-alist) ffap-alist)) |
1464 ;; | 1623 ;; |
1465 ;; (setq ffap-alist ; add something to `ffap-alist' | 1624 ;; (setq ffap-alist ; add something to `ffap-alist' |
1466 ;; (cons | 1625 ;; (cons |
1467 ;; (cons "^[Yy][Ss][Nn][0-9]+$" | 1626 ;; (cons "^YSN[0-9]+$" |
1468 ;; (defun ffap-ysn (name) | 1627 ;; (defun ffap-ysn (name) |
1469 ;; (concat | 1628 ;; (concat |
1470 ;; "http://snorri.chem.washington.edu/ysnarchive/issuefiles/" | 1629 ;; "http://www.physics.uiuc.edu/" |
1630 ;; "ysn/httpd/htdocs/ysnarchive/issuefiles/" | |
1471 ;; (substring name 3) ".html"))) | 1631 ;; (substring name 3) ".html"))) |
1472 ;; ffap-alist)) | 1632 ;; ffap-alist)) |
1473 | 1633 |
1474 | 1634 |
1475 ;;; XEmacs: | |
1476 ;; Extended suppport in another file, for copyright reasons. | |
1477 (or (not ffap-xemacs) | |
1478 (load "ffap-xe" t t) | |
1479 (message "ffap warning: ffap-xe.el not found")) | |
1480 | |
1481 | |
1482 ;;; ffap.el ends here | 1635 ;;; ffap.el ends here |