summaryrefslogtreecommitdiff
path: root/lisp/ffap.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/ffap.el')
-rw-r--r--lisp/ffap.el213
1 files changed, 157 insertions, 56 deletions
diff --git a/lisp/ffap.el b/lisp/ffap.el
index 737de8b5991..8d3f8bfc37d 100644
--- a/lisp/ffap.el
+++ b/lisp/ffap.el
@@ -1,9 +1,9 @@
;;; ffap.el --- find file (or url) at point
-;; Copyright (C) 1995-1997, 2000-2013 Free Software Foundation, Inc.
+;; Copyright (C) 1995-1997, 2000-2015 Free Software Foundation, Inc.
;; Author: Michelangelo Grigni <mic@mathcs.emory.edu>
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Created: 29 Mar 1993
;; Keywords: files, hypermedia, matching, mouse, convenience
;; X-URL: ftp://ftp.mathcs.emory.edu/pub/mic/emacs/
@@ -90,7 +90,6 @@
;;; Todo list:
-;; * use kpsewhich
;; * let "/dir/file#key" jump to key (tag or regexp) in /dir/file
;; * find file of symbol if TAGS is loaded (like above)
;; * break long menus into multiple panes (like imenu?)
@@ -163,10 +162,16 @@ schemes (e.g. \"ftp\"); in that case, only convert those URLs."
:group 'ffap
:version "24.3")
+(defcustom ffap-lax-url nil
+ "If non-nil, allow lax URL matching."
+ :type 'boolean
+ :group 'ffap
+ :version "25.1")
+
(defcustom ffap-ftp-default-user "anonymous"
- "User name in ftp file names generated by `ffap-host-to-path'.
+ "User name in FTP file names generated by `ffap-host-to-path'.
Note this name may be omitted if it equals the default
-\(either `efs-default-user' or `ange-ftp-default-user'\)."
+\(either `efs-default-user' or `ange-ftp-default-user')."
:type 'string
:group 'ffap)
@@ -185,7 +190,7 @@ Note this name may be omitted if it equals the default
"\\|"
"\\(ftp\\|https?\\|telnet\\|gopher\\|www\\|wais\\)://" ; needs host
"\\)")
- "Regexp matching the beginning of a URI, for FFAP.
+ "Regexp matching the beginning of a URI, for ffap.
If the value is nil, disable URL-matching features in ffap.")
(defcustom ffap-foo-at-bar-prefix "mailto"
@@ -228,7 +233,7 @@ it passes it on to `dired'."
:group 'ffap)
(defcustom ffap-pass-wildcards-to-dired nil
- "If non-nil, pass filenames matching `ffap-dired-wildcards' to dired."
+ "If non-nil, pass filenames matching `ffap-dired-wildcards' to Dired."
:type 'boolean
:group 'ffap)
@@ -259,20 +264,10 @@ ffap most of the time."
:group 'ffap
:risky t)
-(defcustom ffap-url-fetcher
- (if (fboundp 'browse-url)
- 'browse-url ; rely on browse-url-browser-function
- 'w3-fetch)
- ;; Remote control references:
- ;; http://www.ncsa.uiuc.edu/SDG/Software/XMosaic/remote-control.html
- ;; http://home.netscape.com/newsref/std/x-remote.html
+(defcustom ffap-url-fetcher 'browse-url
"A function of one argument, called by ffap to fetch an URL.
-Reasonable choices are `w3-fetch' or a `browse-url-*' function.
For a fancy alternative, get `ffap-url.el'."
- :type '(choice (const w3-fetch)
- (const browse-url) ; in recent versions of browse-url
- (const browse-url-netscape)
- (const browse-url-mosaic)
+ :type '(choice (const browse-url)
function)
:group 'ffap
:risky t)
@@ -291,8 +286,8 @@ For a fancy alternative, get `ffap-url.el'."
(defcustom dired-at-point-require-prefix nil
"If non-nil, reverse the prefix argument to `dired-at-point'.
-This is nil so neophytes notice FFAP. Experts may prefer to
-disable FFAP most of the time."
+This is nil so neophytes notice ffap. Experts may prefer to
+disable ffap most of the time."
:type 'boolean
:group 'ffap
:version "20.3")
@@ -343,7 +338,7 @@ Only considers strings that match `ffap-next-regexp'."
"Search buffer for next file or URL, and run ffap.
Optional argument BACK says to search backwards.
Optional argument WRAP says to try wrapping around if necessary.
-Interactively: use a single prefix to search backwards,
+Interactively: use a single prefix \\[universal-argument] to search backwards,
double prefix to wrap forward, triple to wrap backwards.
Actual search is done by the function `ffap-next-guess'."
(interactive
@@ -413,13 +408,13 @@ See `mail-extr.el' for the known domains."
Depending on the domain (none, known, or unknown), follow the strategy
named by the variable `ffap-machine-p-local', `ffap-machine-p-known',
or `ffap-machine-p-unknown'. Pinging uses `open-network-stream'.
-Optional SERVICE specifies the port used \(default \"discard\"\).
+Optional SERVICE specifies the port used (default \"discard\").
Optional QUIET flag suppresses the \"Pinging...\" message.
Optional STRATEGY overrides the three variables above.
Returned values:
- t means that HOST answered.
-'accept means the relevant variable told us to accept.
-\"mesg\" means HOST exists, but does not respond for some reason."
+ t means that HOST answered.
+`accept' means the relevant variable told us to accept.
+\"mesg\" means HOST exists, but does not respond for some reason."
;; Try some (Emory local):
;; (ffap-machine-p "ftp" nil nil 'ping)
;; (ffap-machine-p "nonesuch" nil nil 'ping)
@@ -470,7 +465,7 @@ Returned values:
;; (file-error "connection failed" "address already in use"
;; "ftp.uu.net" "ffap-machine-p")
((equal mesg "connection failed")
- (if (equal (nth 2 error) "permission denied")
+ (if (string= (downcase (nth 2 error)) "permission denied")
nil ; host does not exist
;; Other errors mean the host exists:
(nth 2 error)))
@@ -642,7 +637,7 @@ Looks at `ffap-ftp-default-user', returns \"\" for \"localhost\"."
(defun ffap-list-env (env &optional empty)
"Return a list of strings parsed from environment variable ENV.
-Optional EMPTY is the default list if \(getenv ENV\) is undefined, and
+Optional EMPTY is the default list if (getenv ENV) is undefined, and
also is substituted for the first empty-string component, if there is one.
Uses `path-separator' to separate the path into substrings."
;; We cannot use parse-colon-path (files.el), since it kills
@@ -769,7 +764,7 @@ This uses `ffap-file-exists-string', which may try adding suffixes from
;; (lisp-interaction-mode . ffap-el-mode) ; maybe
(finder-mode . ffap-el-mode) ; type {C-h p} and try it
(help-mode . ffap-el-mode) ; maybe useful
- (c++-mode . ffap-c-mode) ; search ffap-c-path
+ (c++-mode . ffap-c++-mode) ; search ffap-c++-path
(cc-mode . ffap-c-mode) ; same
("\\.\\([chCH]\\|cc\\|hh\\)\\'" . ffap-c-mode) ; stdio.h
(fortran-mode . ffap-fortran-mode) ; FORTRAN requested by MDB
@@ -787,12 +782,12 @@ This uses `ffap-file-exists-string', which may try adding suffixes from
. ffap-rfc) ; "100% RFC2100 compliant"
(dired-mode . ffap-dired) ; maybe in a subdirectory
)
- "Alist of \(KEY . FUNCTION\) pairs parsed by `ffap-file-at-point'.
+ "Alist of (KEY . FUNCTION) pairs parsed by `ffap-file-at-point'.
If string NAME at point (maybe \"\") is not a file or URL, these pairs
specify actions to try creating such a string. A pair matches if either
KEY is a symbol, and it equals `major-mode', or
KEY is a string, it should match NAME as a regexp.
-On a match, \(FUNCTION NAME\) is called and should return a file, an
+On a match, (FUNCTION NAME) is called and should return a file, an
URL, or nil. If nil, search the alist for further matches.")
(put 'ffap-alist 'risky-local-variable t)
@@ -866,6 +861,28 @@ URL, or nil. If nil, search the alist for further matches.")
(defun ffap-c-mode (name)
(ffap-locate-file name t ffap-c-path))
+(defvar ffap-c++-path
+ (let ((c++-include-dir (with-temp-buffer
+ (when (eq 0 (ignore-errors
+ (call-process "g++" nil t nil "-v")))
+ (goto-char (point-min))
+ (if (re-search-forward "--with-gxx-include-dir=\
+\\([^[:space:]]+\\)"
+ nil 'noerror)
+ (match-string 1)
+ (when (re-search-forward "gcc version \
+\\([[:digit:]]+.[[:digit:]]+.[[:digit:]]+\\)"
+ nil 'noerror)
+ (expand-file-name (match-string 1)
+ "/usr/include/c++/")))))))
+ (if c++-include-dir
+ (cons c++-include-dir ffap-c-path)
+ ffap-c-path))
+ "List of directories to search for include files.")
+
+(defun ffap-c++-mode (name)
+ (ffap-locate-file name t ffap-c++-path))
+
(defvar ffap-fortran-path '("../include" "/usr/include"))
(defun ffap-fortran-mode (name)
@@ -876,6 +893,24 @@ URL, or nil. If nil, search the alist for further matches.")
"Path where `ffap-tex-mode' looks for TeX files.
If t, `ffap-tex-init' will initialize this when needed.")
+(defvar ffap-latex-guess-rules '(("" . ".sty")
+ ("" . ".cls")
+ ("" . ".ltx")
+ ("" . ".tex")
+ ("" . "") ;; in some rare cases the
+ ;; extension is already in
+ ;; the buffer.
+ ("beamertheme" . ".sty")
+ ("beamercolortheme". ".sty")
+ ("beamerfonttheme". ".sty")
+ ("beamerinnertheme". ".sty")
+ ("beameroutertheme". ".sty")
+ ("" . ".ldf"))
+ "List of rules for guessing a filename.
+Each rule is a cons (PREFIX . SUFFIX) used for guessing a
+filename from the word at point by prepending PREFIX and
+appending SUFFIX.")
+
(defun ffap-tex-init ()
;; Compute ffap-tex-path if it is now t.
(and (eq t ffap-tex-path)
@@ -899,9 +934,56 @@ If t, `ffap-tex-init' will initialize this when needed.")
(ffap-locate-file name '(".tex" "") ffap-tex-path))
(defun ffap-latex-mode (name)
- (ffap-tex-init)
- ;; only rare need for ""
- (ffap-locate-file name '(".cls" ".sty" ".tex" "") ffap-tex-path))
+ "`ffap' function suitable for latex buffers.
+This uses the program kpsewhich if available. In this case, the
+variable `ffap-latex-guess-rules' is used for building a filename
+out of NAME."
+ (cond ((file-exists-p name)
+ name)
+ ((not (executable-find "kpsewhich"))
+ (ffap-tex-init)
+ (ffap-locate-file name '(".cls" ".sty" ".tex" "") ffap-tex-path))
+ (t
+ (let ((curbuf (current-buffer))
+ (guess-rules ffap-latex-guess-rules)
+ (preferred-suffix-rules '(("input" . ".tex")
+ ("include" . ".tex")
+ ("usepackage" . ".sty")
+ ("RequirePackageWithOptions" . ".sty")
+ ("RequirePackage" . ".sty")
+ ("documentclass" . ".cls")
+ ("documentstyle" . ".cls")
+ ("LoadClass" . ".cls")
+ ("LoadClassWithOptions" . ".cls")
+ ("bibliography" . ".bib")
+ ("addbibresource" . ""))))
+ ;; We now add preferred suffix in front of suffixes.
+ (when
+ ;; The condition is essentially:
+ ;; (assoc (TeX-current-macro)
+ ;; (mapcar 'car preferred-suffix-rules))
+ ;; but (TeX-current-macro) can take time, so we just
+ ;; check if one of the `car' in preferred-suffix-rules
+ ;; is found before point on the current line. It
+ ;; should cover most cases.
+ (save-excursion
+ (re-search-backward (regexp-opt
+ (mapcar 'car preferred-suffix-rules))
+ (point-at-bol)
+ t))
+ (push (cons "" (cdr (assoc (match-string 0) ; i.e. "(TeX-current-macro)"
+ preferred-suffix-rules)))
+ guess-rules))
+ (setq kpsewhich-args (mapcar (lambda (rule)
+ (concat (car rule) name (cdr rule)))
+ guess-rules))
+ (with-temp-buffer
+ (let ((process-environment (buffer-local-value
+ 'process-environment curbuf))
+ (exec-path (buffer-local-value 'exec-path curbuf)))
+ (apply #'call-process "kpsewhich" nil t nil kpsewhich-args))
+ (when (< (point-min) (point-max))
+ (buffer-substring (goto-char (point-min)) (point-at-eol))))))))
(defun ffap-tex (name)
(ffap-tex-init)
@@ -958,7 +1040,7 @@ If t, `ffap-tex-init' will initialize this when needed.")
(defcustom ffap-rfc-path
(concat (ffap-host-to-filename "ftp.rfc-editor.org") "/in-notes/rfc%s.txt")
"A `format' string making a filename for RFC documents.
-This can be an ange-ftp or tramp remote filename to download, or
+This can be an ange-ftp or Tramp remote filename to download, or
a local filename if you have full set of RFCs locally. See also
`ffap-rfc-directories'."
:type 'string
@@ -986,7 +1068,7 @@ If a given RFC isn't in these then `ffap-rfc-path' is offered."
;; Slightly controversial decisions:
;; * strip trailing "@" and ":"
;; * no commas (good for latex)
- (file "--:\\\\$+<>@-Z_[:alpha:]~*?" "<@" "@>;.,!:")
+ (file "--:\\\\${}+<>@-Z_[:alpha:]~*?" "<@" "@>;.,!:")
;; An url, or maybe a email/news message-id:
(url "--:=&?$+@-Z_[:alpha:]~#,%;*()!'" "^[0-9a-zA-Z]" ":;.,!?")
;; Find a string that does *not* contain a colon:
@@ -995,14 +1077,19 @@ If a given RFC isn't in these then `ffap-rfc-path' is offered."
(machine "-[:alnum:]." "" ".")
;; Mathematica paths: allow backquotes
(math-mode ",-:$+<>@-Z_[:lower:]~`" "<" "@>;.,!?`:")
+ ;; (La)TeX: don't allow braces
+ (latex-mode "--:\\\\$+<>@-Z_[:alpha:]~*?" "<@" "@>;.,!:")
+ (tex-mode "--:\\\\$+<>@-Z_[:alpha:]~*?" "<@" "@>;.,!:")
)
- "Alist of \(MODE CHARS BEG END\), where MODE is a symbol,
-possibly a major-mode name, or one of the symbol
+ "Alist of (MODE CHARS BEG END), where MODE is a symbol,
+possibly a major-mode name, or one of the symbols
`file', `url', `machine', and `nocolon'.
Function `ffap-string-at-point' uses the data fields as follows:
1. find a maximal string of CHARS around point,
2. strip BEG chars before point from the beginning,
-3. strip END chars after point from the end.")
+3. strip END chars after point from the end.
+The arguments CHARS, BEG and END are handled as described in
+`skip-chars-forward'.")
(defvar ffap-string-at-point nil
;; Added at suggestion of RHOGEE (for ff-paths), 7/24/95.
@@ -1010,8 +1097,8 @@ Function `ffap-string-at-point' uses the data fields as follows:
(defun ffap-string-at-point (&optional mode)
"Return a string of characters from around point.
-MODE (defaults to value of `major-mode') is a symbol used to look up string
-syntax parameters in `ffap-string-at-point-mode-alist'.
+MODE (defaults to value of `major-mode') is a symbol used to look up
+string syntax parameters in `ffap-string-at-point-mode-alist'.
If MODE is not found, we use `file' instead of MODE.
If the region is active, return a string from the region.
Sets the variable `ffap-string-at-point' and the variable
@@ -1068,16 +1155,25 @@ Assumes the buffer has not changed."
(declare-function w3-view-this-url "ext:w3" (&optional no-show))
(defun ffap-url-at-point ()
- "Return URL from around point if it exists, or nil."
+ "Return URL from around point if it exists, or nil.
+
+Sets the variable `ffap-string-at-point-region' to the bounds of URL, if any."
(when ffap-url-regexp
(or (and (eq major-mode 'w3-mode) ; In a w3 buffer button?
(w3-view-this-url t))
(let ((thing-at-point-beginning-of-url-regexp ffap-url-regexp)
- (thing-at-point-default-mail-uri-scheme ffap-foo-at-bar-prefix))
- (thing-at-point-url-at-point t
- (if (use-region-p)
- (cons (region-beginning)
- (region-end))))))))
+ (thing-at-point-default-mail-uri-scheme ffap-foo-at-bar-prefix)
+ val)
+ (setq val (thing-at-point-url-at-point ffap-lax-url
+ (if (use-region-p)
+ (cons (region-beginning)
+ (region-end)))))
+ (if val
+ (let ((bounds (thing-at-point-bounds-of-url-at-point
+ ffap-lax-url)))
+ (setq ffap-string-at-point-region
+ (list (car bounds) (cdr bounds)))))
+ val))))
(defvar ffap-gopher-regexp
"^.*\\<\\(Type\\|Name\\|Path\\|Host\\|Port\\) *= *\\(.*\\) *$"
@@ -1085,7 +1181,9 @@ Assumes the buffer has not changed."
The two subexpressions are the KEY and VALUE.")
(defun ffap-gopher-at-point ()
- "If point is inside a gopher bookmark block, return its URL."
+ "If point is inside a gopher bookmark block, return its URL.
+
+Sets the variable `ffap-string-at-point-region' to the bounds of URL, if any."
;; `gopher-parse-bookmark' from gopher.el is not so robust
(save-excursion
(beginning-of-line)
@@ -1094,6 +1192,7 @@ The two subexpressions are the KEY and VALUE.")
(while (and (looking-at ffap-gopher-regexp) (not (bobp)))
(forward-line -1))
(or (looking-at ffap-gopher-regexp) (forward-line 1))
+ (setq ffap-string-at-point-region (list (point) (point)))
(let ((type "1") path host (port "70"))
(while (looking-at ffap-gopher-regexp)
(let ((var (intern
@@ -1104,6 +1203,7 @@ The two subexpressions are the KEY and VALUE.")
(match-end 2))))
(set var val)
(forward-line 1)))
+ (setcdr ffap-string-at-point-region (list (point)))
(if (and path (string-match "^ftp:.*@" path))
(concat "ftp://"
(substring path 4 (1- (match-end 0)))
@@ -1121,7 +1221,7 @@ The two subexpressions are the KEY and VALUE.")
;; Icky regexp avoids: default: 123: foo::bar cs:pub
;; It does match on: mic@cs: cs:/pub mathcs.emory.edu: (point at end)
"\\`\\([^:@]+@[^:@]+:\\|[^@.:]+\\.[^@:]+:\\|[^:]+:[~/]\\)\\([^:]\\|\\'\\)")
- "Strings matching this are coerced to ftp file names by ffap.
+ "Strings matching this are coerced to FTP file names by ffap.
That is, ffap just prepends \"/\". Set to nil to disable.")
(defun ffap-file-at-point ()
@@ -1231,7 +1331,8 @@ which may actually result in an URL rather than a filename."
(not (ffap-file-exists-string dir))
(not (equal dir (setq dir (file-name-directory
(directory-file-name dir)))))))
- (ffap-file-exists-string dir)))
+ (and (not (string= dir "/"))
+ (ffap-file-exists-string dir))))
)
(set-match-data data))))
@@ -1269,7 +1370,7 @@ which may actually result in an URL rather than a filename."
nil
nil
(if dir (cons guess (length dir)) guess)
- (list 'file-name-history)
+ 'file-name-history
(and buffer-file-name
(abbreviate-file-name buffer-file-name)))))
;; Remove the special handler manually. We used to just let-bind
@@ -1410,7 +1511,7 @@ and the functions `ffap-file-at-point' and `ffap-url-at-point'."
(expand-file-name filename)))
;; User does not want to find a non-existent file:
((signal 'file-error (list "Opening file buffer"
- "no such file or directory"
+ "No such file or directory"
filename)))))))
;; Shortcut: allow {M-x ffap} rather than {M-x find-file-at-point}.
@@ -1423,7 +1524,7 @@ and the functions `ffap-file-at-point' and `ffap-url-at-point'."
(defcustom ffap-menu-regexp nil
"If non-nil, regexp overriding `ffap-next-regexp' in `ffap-menu'.
Make this more restrictive for faster menu building.
-For example, try \":/\" for URL (and some ftp) references."
+For example, try \":/\" for URL (and some FTP) references."
:type '(choice (const nil) regexp)
:group 'ffap)
@@ -1443,7 +1544,7 @@ These properties may be used to fontify the menu references.")
"Put up a menu of files and URLs mentioned in this buffer.
Then set mark, jump to choice, and try to fetch it. The menu is
cached in `ffap-menu-alist', and rebuilt by `ffap-menu-rescan'.
-The optional RESCAN argument \(a prefix, interactively\) forces
+The optional RESCAN argument (a prefix, interactively) forces
a rebuild. Searches with `ffap-menu-regexp'."
(interactive "P")
;; (require 'imenu) -- no longer used, but roughly emulated
@@ -1476,7 +1577,7 @@ a rebuild. Searches with `ffap-menu-regexp'."
(defun ffap-menu-ask (title alist cont)
"Prompt from a menu of choices, and then apply some action.
-Arguments are TITLE, ALIST, and CONT \(a continuation function\).
+Arguments are TITLE, ALIST, and CONT (a continuation function).
This uses either a menu or the minibuffer depending on invocation.
The TITLE string is used as either the prompt or menu title.
Each ALIST entry looks like (STRING . DATA) and defines one choice.
@@ -1536,7 +1637,7 @@ Applies `ffap-menu-text-plist' text properties at all matches."
(add-text-properties (car ffap-string-at-point-region) (point)
ffap-menu-text-plist)
(message "Scanning...%2d%% <%s>"
- (/ (* 100 (- (point) (point-min))) range) item)))
+ (floor (* 100.0 (- (point) (point-min))) range) item)))
(or mod (restore-buffer-modified-p nil))))
(message "Scanning...done")
;; Remove duplicates.