summaryrefslogtreecommitdiff
path: root/lisp/find-file.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/find-file.el')
-rw-r--r--lisp/find-file.el310
1 files changed, 97 insertions, 213 deletions
diff --git a/lisp/find-file.el b/lisp/find-file.el
index 8cc9c972ed4..4fd4f4e06b8 100644
--- a/lisp/find-file.el
+++ b/lisp/find-file.el
@@ -1,4 +1,4 @@
-;;; find-file.el --- find a file corresponding to this one given a pattern
+;;; find-file.el --- find a file corresponding to this one given a pattern -*- lexical-binding: t; -*-
;; Author: Henry Guillaume <henri@tibco.com, henry@c032.aone.net.au>
;; Maintainer: emacs-devel@gnu.org
@@ -39,8 +39,8 @@
;; and just has a different extension as described by the ff-other-file-alist
;; variable:
;;
-;; '(("\\.cc$" (".hh" ".h"))
-;; ("\\.hh$" (".cc" ".C" ".CC" ".cxx" ".cpp")))
+;; '(("\\.cc\\'" (".hh" ".h"))
+;; ("\\.hh\\'" (".cc" ".C" ".CC" ".cxx" ".cpp")))
;;
;; If the current file has a .cc extension, ff-find-other-file will attempt
;; to look for a .hh file, and then a .h file in some directory as described
@@ -55,8 +55,8 @@
;; format above can be changed to include a function to be called when the
;; current file matches the regexp:
;;
-;; '(("\\.cc$" cc--function)
-;; ("\\.hh$" hh-function))
+;; '(("\\.cc\\'" cc--function)
+;; ("\\.hh\\'" hh-function))
;;
;; These functions must return a list consisting of the possible names of the
;; corresponding file, with or without path. There is no real need for more
@@ -64,10 +64,10 @@
;; file-alist:
;;
;; (setq cc-other-file-alist
-;; '(("\\.cc$" ff-cc-hh-converter)
-;; ("\\.hh$" ff-cc-hh-converter)
-;; ("\\.c$" (".h"))
-;; ("\\.h$" (".c" ".cc" ".C" ".CC" ".cxx" ".cpp"))))
+;; '(("\\.cc\\'" ff-cc-hh-converter)
+;; ("\\.hh\\'" ff-cc-hh-converter)
+;; ("\\.c\\'" (".h"))
+;; ("\\.h\\'" (".c" ".cc" ".C" ".CC" ".cxx" ".cpp"))))
;;
;; ff-cc-hh-converter is included at the end of this file as a reference.
;;
@@ -130,62 +130,51 @@
(defcustom ff-pre-find-hook nil
"List of functions to be called before the search for the file starts."
- :type 'hook
- :group 'ff)
+ :type 'hook)
(defcustom ff-pre-load-hook nil
"List of functions to be called before the other file is loaded."
- :type 'hook
- :group 'ff)
+ :type 'hook)
(defcustom ff-post-load-hook nil
"List of functions to be called after the other file is loaded."
- :type 'hook
- :group 'ff)
+ :type 'hook)
(defcustom ff-not-found-hook nil
"List of functions to be called if the other file could not be found."
- :type 'hook
- :group 'ff)
+ :type 'hook)
(defcustom ff-file-created-hook nil
"List of functions to be called if the other file needs to be created."
- :type 'hook
- :group 'ff)
+ :type 'hook)
(defcustom ff-case-fold-search nil
"Non-nil means ignore cases in matches (see `case-fold-search').
If you have extensions in different cases, you will want this to be nil."
- :type 'boolean
- :group 'ff)
+ :type 'boolean)
(defcustom ff-always-in-other-window nil
"If non-nil, find the corresponding file in another window by default.
To override this, give an argument to `ff-find-other-file'."
- :type 'boolean
- :group 'ff)
+ :type 'boolean)
(defcustom ff-ignore-include nil
"If non-nil, ignore `#include' lines."
- :type 'boolean
- :group 'ff)
+ :type 'boolean)
(defcustom ff-always-try-to-create t
"If non-nil, always attempt to create the other file if it was not found."
- :type 'boolean
- :group 'ff)
+ :type 'boolean)
(defcustom ff-quiet-mode nil
"If non-nil, trace which directories are being searched."
- :type 'boolean
- :group 'ff)
+ :type 'boolean)
;;;###autoload
(defcustom ff-special-constructs
;; C/C++ include, for NeXTstep too
`((,(purecopy "^#\\s *\\(include\\|import\\)\\s +[<\"]\\(.*\\)[>\"]") .
- (lambda ()
- (buffer-substring (match-beginning 2) (match-end 2)))))
+ ,(lambda () (match-string 2))))
;; We include `ff-treat-as-special' documentation here so that autoload
;; can make it available to be read prior to loading this file.
"List of special constructs recognized by `ff-treat-as-special'.
@@ -194,8 +183,7 @@ If REGEXP matches the current line (from the beginning of the line),
`ff-treat-as-special' calls function EXTRACT with no args.
If EXTRACT returns nil, keep trying. Otherwise, return the
filename that EXTRACT returned."
- :type '(repeat (cons regexp function))
- :group 'ff)
+ :type '(repeat (cons regexp function)))
(defvaralias 'ff-related-file-alist 'ff-other-file-alist)
(defcustom ff-other-file-alist 'cc-other-file-alist
@@ -207,8 +195,7 @@ directory specified in `ff-search-directories'. If a file is not found,
a new one is created with the first matching extension (`.cc' yields `.hh').
This alist should be set by the major mode."
:type '(choice (repeat (list regexp (choice (repeat string) function)))
- symbol)
- :group 'ff)
+ symbol))
(defcustom ff-search-directories 'cc-search-directories
"List of directories to search for a specific file.
@@ -231,14 +218,12 @@ not exist, it is replaced (silently) with an empty string.
The stars are *not* wildcards: they are searched for together with
the preceding slash. The star represents all the subdirectories except
`..', and each of these subdirectories will be searched in turn."
- :type '(choice (repeat directory) symbol)
- :group 'ff)
+ :type '(choice (repeat directory) symbol))
(defcustom cc-search-directories
'("." "/usr/include" "/usr/local/include/*")
"See the description of the `ff-search-directories' variable."
- :type '(repeat directory)
- :group 'ff)
+ :type '(repeat directory))
(defcustom cc-other-file-alist
'(("\\.cc\\'" (".hh" ".h"))
@@ -269,17 +254,15 @@ since the search algorithm searches sequentially through each directory
specified in `ff-search-directories'. If a file is not found, a new one
is created with the first matching extension (`.cc' yields `.hh')."
:version "24.4" ; add .m
- :type '(repeat (list regexp (choice (repeat string) function)))
- :group 'ff)
+ :type '(repeat (list regexp (choice (repeat string) function))))
(defcustom modula2-other-file-alist
'(
- ("\\.mi$" (".md")) ;; Modula-2 module definition
- ("\\.md$" (".mi")) ;; and implementation.
+ ("\\.mi\\'" (".md")) ;; Modula-2 module definition
+ ("\\.md\\'" (".mi")) ;; and implementation.
)
"See the description for the `ff-search-directories' variable."
- :type '(repeat (list regexp (choice (repeat string) function)))
- :group 'ff)
+ :type '(repeat (list regexp (choice (repeat string) function))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -308,22 +291,24 @@ See also the documentation for `ff-find-other-file'.
If optional IN-OTHER-WINDOW is non-nil, find the file in another window."
(interactive "P")
- (let ((ignore ff-ignore-include))
- (setq ff-ignore-include t)
- (ff-find-the-other-file in-other-window)
- (setq ff-ignore-include ignore)))
+ (let ((ff-ignore-include t))
+ (ff-find-the-other-file in-other-window)))
;;;###autoload
-(defalias 'ff-find-related-file 'ff-find-other-file)
+(defalias 'ff-find-related-file #'ff-find-other-file)
;;;###autoload
-(defun ff-find-other-file (&optional in-other-window ignore-include)
+(defun ff-find-other-file (&optional in-other-window ignore-include event)
"Find the header or source file corresponding to this file.
Being on a `#include' line pulls in that file.
If optional IN-OTHER-WINDOW is non-nil, find the file in the other window.
If optional IGNORE-INCLUDE is non-nil, ignore being on `#include' lines.
+If optional EVENT is non-nil (default `last-nonmenu-event', move
+point to the end position of that event before calling the
+various ff-* hooks.
+
Variables of interest include:
- `ff-case-fold-search'
@@ -369,11 +354,17 @@ Variables of interest include:
- `ff-file-created-hook'
List of functions to be called if the other file has been created."
- (interactive "P")
- (let ((ignore ff-ignore-include))
- (setq ff-ignore-include ignore-include)
- (ff-find-the-other-file in-other-window)
- (setq ff-ignore-include ignore)))
+ (interactive (list current-prefix-arg nil last-nonmenu-event))
+ ;; We want to preserve point in the current buffer. But the point of
+ ;; ff-find-the-other-file is to make the the other file buffer
+ ;; current, so we can't use save-excursion here (see bug 48535).
+ (let ((start-buffer (current-buffer))
+ (start-point (point)))
+ (posn-set-point (event-end event))
+ (let ((ff-ignore-include ignore-include))
+ (ff-find-the-other-file in-other-window))
+ (with-current-buffer start-buffer
+ (goto-char start-point))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Support functions
@@ -413,9 +404,9 @@ If optional IN-OTHER-WINDOW is non-nil, find the file in another window."
(message "Working...")
(setq dirs
- (if (symbolp ff-search-directories)
- (ff-list-replace-env-vars (symbol-value ff-search-directories))
- (ff-list-replace-env-vars ff-search-directories)))
+ (ff-list-replace-env-vars (if (symbolp ff-search-directories)
+ (symbol-value ff-search-directories)
+ ff-search-directories)))
(setq fname (ff-treat-as-special))
@@ -454,11 +445,10 @@ If optional IN-OTHER-WINDOW is non-nil, find the file in another window."
;; if we have a function to generate new names,
;; invoke it with the name of the current file
(if (and (atom action) (fboundp action))
- (progn
- (setq suffixes (funcall action (ff-buffer-file-name))
- match (cons (car match) (list suffixes))
- stub nil
- default-name (car suffixes)))
+ (setq suffixes (funcall action (ff-buffer-file-name))
+ match (cons (car match) (list suffixes))
+ stub nil
+ default-name (car suffixes))
;; otherwise build our filename stub
(cond
@@ -472,7 +462,8 @@ If optional IN-OTHER-WINDOW is non-nil, find the file in another window."
(t
(setq format (concat "\\(.+\\)" (car match)))
(string-match format fname)
- (setq stub (substring fname (match-beginning 1) (match-end 1)))
+ ;; FIXME: What if `string-match' failed?
+ (setq stub (match-string 1 fname))
))
;; if we find nothing, we should try to get a file like this one
@@ -522,89 +513,6 @@ If optional IN-OTHER-WINDOW is non-nil, find the file in another window."
found)) ;; return buffer-name or filename
-(defun ff-other-file-name ()
- "Return name of the header or source file corresponding to the current file.
-Being on a `#include' line pulls in that file, but see the help on
-the `ff-ignore-include' variable."
-
- (let (match ;; matching regexp for this file
- suffixes ;; set of replacing regexps for the matching regexp
- action ;; function to generate the names of the other files
- fname ;; basename of this file
- pos ;; where we start matching filenames
- stub ;; name of the file without extension
- alist ;; working copy of the list of file extensions
- pathname ;; the pathname of the file or the #include line
- format ;; what we have to match
- found ;; name of the file or buffer found - nil if none
- dirs) ;; local value of ff-search-directories
-
- (message "Working...")
-
- (setq dirs
- (if (symbolp ff-search-directories)
- (ff-list-replace-env-vars (symbol-value ff-search-directories))
- (ff-list-replace-env-vars ff-search-directories)))
-
- (setq fname (ff-treat-as-special))
-
- (cond
- ((and (not ff-ignore-include) fname)
- (setq found (ff-get-file-name dirs fname nil)))
-
- ;; let's just get the corresponding file
- (t
- (setq alist (if (symbolp ff-other-file-alist)
- (symbol-value ff-other-file-alist)
- ff-other-file-alist)
- pathname (or (ff-buffer-file-name) "/none.none"))
-
- (setq fname (file-name-nondirectory pathname)
- match (car alist))
-
- ;; find the table entry corresponding to this file
- (setq pos (ff-string-match (car match) fname))
- (while (and match (if (and pos (>= pos 0)) nil (not pos)))
- (setq alist (cdr alist))
- (setq match (car alist))
- (setq pos (ff-string-match (car match) fname)))
-
- ;; no point going on if we haven't found anything
- (when match
-
- ;; otherwise, suffixes contains what we need
- (setq suffixes (car (cdr match))
- action (car (cdr match))
- found nil)
-
- ;; if we have a function to generate new names,
- ;; invoke it with the name of the current file
- (if (and (atom action) (fboundp action))
- (progn
- (setq suffixes (funcall action (ff-buffer-file-name))
- match (cons (car match) (list suffixes))
- stub nil))
-
- ;; otherwise build our filename stub
- (cond
-
- ;; get around the problem that 0 and nil both mean false!
- ((= pos 0)
- (setq format "")
- (setq stub "")
- )
-
- (t
- (setq format (concat "\\(.+\\)" (car match)))
- (string-match format fname)
- (setq stub (substring fname (match-beginning 1) (match-end 1)))
- )))
-
- ;; do the real work - find the file
- (setq found
- (ff-get-file-name dirs stub suffixes)))))
- found)) ;; return buffer-name or filename
-
(defun ff-get-file (search-dirs filename &optional suffix-list other-window)
"Find a file in the SEARCH-DIRS with the given FILENAME (or filename stub).
If (optional) SUFFIX-LIST is nil, search for FILENAME, otherwise search
@@ -709,11 +617,10 @@ name of the first file found."
;; otherwise dir matches the '/*', so search each dir separately
(progn
- (if (match-beginning 2)
- (setq rest (substring dir (match-beginning 2) (match-end 2)))
- (setq rest "")
- )
- (setq dir (substring dir (match-beginning 1) (match-end 1)))
+ (setq rest (if (match-beginning 2)
+ (match-string 2 dir)
+ ""))
+ (setq dir (match-string 1 dir))
(let ((dirlist (ff-all-dirs-under dir '("..")))
this-dir compl-dirs)
@@ -743,8 +650,8 @@ name of the first file found."
(defun ff-string-match (regexp string &optional start)
"Like `string-match', but set `case-fold-search' temporarily.
The value used comes from `ff-case-fold-search'."
- (let ((case-fold-search ff-case-fold-search))
- (if regexp
+ (if regexp
+ (let ((case-fold-search ff-case-fold-search))
(string-match regexp string start))))
(defun ff-list-replace-env-vars (search-list)
@@ -752,12 +659,12 @@ The value used comes from `ff-case-fold-search'."
(let (list
(var (car search-list)))
(while search-list
- (if (string-match "\\(.*\\)\\$[({]*\\([a-zA-Z0-9_]+\\)[)}]*\\(.*\\)" var)
+ (if (string-match "\\(.*\\)\\$[({]*\\([[:alnum:]_]+\\)[)}]*\\(.*\\)" var)
(setq var
(concat
- (substring var (match-beginning 1) (match-end 1))
- (getenv (substring var (match-beginning 2) (match-end 2)))
- (substring var (match-beginning 3) (match-end 3)))))
+ (match-string 1 var)
+ (getenv (match-string 2 var))
+ (match-string 3 var))))
(setq search-list (cdr search-list))
(setq list (cons var list))
(setq var (car search-list)))
@@ -782,11 +689,7 @@ See variable `ff-special-constructs'."
(setq match (cdr elem)))
fname)))
-(defun ff-basename (string)
- "Return the basename of pathname STRING."
- (setq string (concat "/" string))
- (string-match ".*/\\([^/]+\\)$" string)
- (setq string (substring string (match-beginning 1) (match-end 1))))
+(define-obsolete-function-alias 'ff-basename #'file-name-nondirectory "28.1")
(defun ff-all-dirs-under (here &optional exclude)
"Get all the directory files under directory HERE.
@@ -800,7 +703,7 @@ Exclude all files in the optional EXCLUDE list."
(setq file (car files))
(if (and
(file-directory-p file)
- (not (member (ff-basename file) exclude)))
+ (not (member (file-name-nondirectory file) exclude)))
(setq dirlist (cons file dirlist)))
(setq files (cdr files)))
(setq dirlist (reverse dirlist))))
@@ -820,84 +723,65 @@ or `switch-to-buffer' / `switch-to-buffer-other-window' function pairs.
If optional NEW-FILE is t, then a special hook (`ff-file-created-hook') is
called before `ff-post-load-hook'."
(run-hooks 'ff-pre-load-hook 'ff-pre-load-hooks)
- (if (or
- (and in-other-window (not ff-always-in-other-window))
- (and (not in-other-window) ff-always-in-other-window))
- (funcall f2 file)
- (funcall f1 file))
+ (funcall (if (or
+ (and in-other-window (not ff-always-in-other-window))
+ (and (not in-other-window) ff-always-in-other-window))
+ f2 f1)
+ file)
(if new-file
(run-hooks 'ff-file-created-hook 'ff-file-created-hooks))
(run-hooks 'ff-post-load-hook 'ff-post-load-hooks))
(defun ff-find-file (file &optional in-other-window new-file)
"Like `find-file', but may show the file in another window."
- (ff-switch-file 'find-file
- 'find-file-other-window
+ (ff-switch-file #'find-file
+ #'find-file-other-window
file in-other-window new-file))
(defun ff-switch-to-buffer (buffer-or-name &optional in-other-window)
"Like `switch-to-buffer', but may show the buffer in another window."
- (ff-switch-file 'switch-to-buffer
- 'switch-to-buffer-other-window
+ (ff-switch-file #'switch-to-buffer
+ #'switch-to-buffer-other-window
buffer-or-name in-other-window nil))
;;;###autoload
-(defun ff-mouse-find-other-file (event)
- "Visit the file you click on."
- (interactive "e")
- (save-excursion
- (mouse-set-point event)
- (ff-find-other-file nil)))
+(define-obsolete-function-alias
+ 'ff-mouse-find-other-file #'ff-find-other-file "28.1")
;;;###autoload
-(defun ff-mouse-find-other-file-other-window (event)
- "Visit the file you click on in another window."
- (interactive "e")
- (save-excursion
- (mouse-set-point event)
- (ff-find-other-file t)))
+(define-obsolete-function-alias
+ 'ff-mouse-find-other-file-other-window #'ff-find-other-file-other-window "28.1")
+;;;###autoload
+(defun ff-find-other-file-other-window (event)
+ "Visit the file you point at in another window."
+ (interactive (list last-nonmenu-event))
+ (ff-find-other-file t nil event))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This section offers an example of user defined function to select files
-(defun ff-upcase-p (string &optional start end)
- "Return t if STRING is all uppercase.
-Given START and/or END, checks between these characters."
- (let (match str)
- (if (not start)
- (setq start 0))
- (if (not end)
- (setq end (length string)))
- (if (= start end)
- (setq end (1+ end)))
- (setq str (substring string start end))
- (if (and
- (ff-string-match "[A-Z]+" str)
- (setq match (match-data))
- (= (car match) 0)
- (= (car (cdr match)) (length str)))
- t
- nil)))
+(defun ff-upcase-p (string)
+ "Return t if STRING is all uppercase."
+ ;; FIXME: Why `ff-string-match' since `[:upper:]' only makes
+ ;; sense when `case-fold-search' is nil?
+ (ff-string-match "\\`[[:upper:]]*\\'" string))
(defun ff-cc-hh-converter (arg)
"Discriminate file extensions.
Build up a new file list based possibly on part of the directory name
and the name of the file passed in."
(ff-string-match "\\(.*\\)/\\([^/]+\\)/\\([^.]+\\).\\([^/]+\\)$" arg)
- (let ((dire (if (match-beginning 2)
- (substring arg (match-beginning 2) (match-end 2)) nil))
- (file (if (match-beginning 3)
- (substring arg (match-beginning 3) (match-end 3)) nil))
- (extn (if (match-beginning 4)
- (substring arg (match-beginning 4) (match-end 4)) nil))
+ (let ((dire (match-string 2 arg))
+ (file (match-string 3 arg))
+ (extn (match-string 4 arg))
return-list)
(cond
;; fooZapJunk.cc => ZapJunk.{hh,h} or fooZapJunk.{hh,h}
((and (string= extn "cc")
- (ff-string-match "^\\([a-z]+\\)\\([A-Z].+\\)$" file))
- (let ((stub (substring file (match-beginning 2) (match-end 2))))
- (setq dire (upcase (substring file (match-beginning 1) (match-end 1))))
+ (ff-string-match "^\\([[:lower:]]+\\)\\([[:upper:]].+\\)$" file))
+ (let ((stub (match-string 2 file)))
+ (setq dire (upcase (match-string 1 file)))
(setq return-list (list (concat stub ".hh")
(concat stub ".h")
(concat file ".hh")