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