diff options
Diffstat (limited to 'lisp/ffap.el')
-rw-r--r-- | lisp/ffap.el | 213 |
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. |