;; Maintainer: FSF
;; Keywords: c, matching, tools
-;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+;; Copyright (C) 1994, 1995, 2002 Free Software Foundation, Inc.
;; This file is part of GNU Emacs.
;;
;; THERE ARE FIVE AVAILABLE HOOKS, called in this order if non-nil:
;;
-;; - ff-pre-find-hooks - called before the search for the other file starts
-;; - ff-not-found-hooks - called when the other file could not be found
-;; - ff-pre-load-hooks - called just before the other file is 'loaded'
-;; - ff-file-created-hooks - called when the other file is created
-;; - ff-post-load-hooks - called just after the other file is 'loaded'
+;; - ff-pre-find-hook - called before the search for the other file starts
+;; - ff-not-found-hook - called when the other file could not be found
+;; - ff-pre-load-hook - called just before the other file is 'loaded'
+;; - ff-file-created-hook - called when the other file is created
+;; - ff-post-load-hook - called just after the other file is 'loaded'
;;
-;; The *load-hooks allow you to place point where you want it in the other
+;; The *load-hook allow you to place point where you want it in the other
;; file.
;; CREDITS:
:link '(emacs-commentary-link "find-file")
:group 'find-file)
-(defcustom ff-pre-find-hooks nil
+(defcustom ff-pre-find-hook nil
"*List of functions to be called before the search for the file starts."
:type 'hook
:group 'ff)
-(defcustom ff-pre-load-hooks nil
+(defcustom ff-pre-load-hook nil
"*List of functions to be called before the other file is loaded."
:type 'hook
:group 'ff)
-(defcustom ff-post-load-hooks nil
+(defcustom ff-post-load-hook nil
"*List of functions to be called after the other file is loaded."
:type 'hook
:group 'ff)
-(defcustom ff-not-found-hooks nil
+(defcustom ff-not-found-hook nil
"*List of functions to be called if the other file could not be found."
:type 'hook
:group 'ff)
-(defcustom ff-file-created-hooks nil
+(defcustom ff-file-created-hook nil
"*List of functions to be called if the other file needs to be created."
:type 'hook
:group 'ff)
etc. and an associated method for extracting the filename from that
construct.")
+(defvaralias 'ff-related-file-alist 'ff-other-file-alist)
(defcustom ff-other-file-alist 'cc-other-file-alist
"*Alist of extensions to find given the current file's extension.
;; No user definable variables beyond this point!
;; ==============================================
-(make-variable-buffer-local 'ff-pre-find-hooks)
-(make-variable-buffer-local 'ff-pre-load-hooks)
-(make-variable-buffer-local 'ff-post-load-hooks)
-(make-variable-buffer-local 'ff-not-found-hooks)
-(make-variable-buffer-local 'ff-file-created-hooks)
+(make-variable-buffer-local 'ff-pre-find-hook)
+(make-variable-buffer-local 'ff-pre-load-hook)
+(make-variable-buffer-local 'ff-post-load-hook)
+(make-variable-buffer-local 'ff-not-found-hook)
+(make-variable-buffer-local 'ff-file-created-hook)
(make-variable-buffer-local 'ff-case-fold-search)
(make-variable-buffer-local 'ff-always-in-other-window)
(make-variable-buffer-local 'ff-ignore-include)
(ff-find-the-other-file in-other-window)
(setq ff-ignore-include ignore)))
+;;;###autoload
+(defalias 'ff-find-related-file 'ff-find-other-file)
+
;;;###autoload
(defun ff-find-other-file (&optional in-other-window ignore-include)
"Find the header or source file corresponding to this file.
List of directories searched through with each extension specified in
`ff-other-file-alist' that matches this file's extension.
- - `ff-pre-find-hooks'
+ - `ff-pre-find-hook'
List of functions to be called before the search for the file starts.
- - `ff-pre-load-hooks'
+ - `ff-pre-load-hook'
List of functions to be called before the other file is loaded.
- - `ff-post-load-hooks'
+ - `ff-post-load-hook'
List of functions to be called after the other file is loaded.
- - `ff-not-found-hooks'
+ - `ff-not-found-hook'
List of functions to be called if the other file could not be found.
- - `ff-file-created-hooks'
+ - `ff-file-created-hook'
List of functions to be called if the other file has been created."
(interactive "P")
(let ((ignore ff-ignore-include))
dirs ;; local value of ff-search-directories
no-match) ;; whether we know about this kind of file
- (if ff-pre-find-hooks
- (run-hooks 'ff-pre-find-hooks))
+ (run-hooks 'ff-pre-find-hook 'ff-pre-find-hooks)
(message "Working...")
(buffer-file-name)
"/none.none"))
- (string-match ".*/\\(.+\\)$" pathname)
- (setq fname (substring pathname (match-beginning 1) (match-end 1))
+ (setq fname (file-name-nondirectory pathname)
no-match nil
match (car alist))
((not found) ;; could not find the other file
- (if ff-not-found-hooks ;; run the hooks
- (run-hooks 'ff-not-found-hooks))
+ (run-hooks 'ff-not-found-hook 'ff-not-found-hooks)
(cond
(ff-always-try-to-create ;; try to create the file
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
+ default-name ;; file we should create if none found
+ format ;; what we have to match
+ found ;; name of the file or buffer found - nil if none
+ dirs ;; local value of ff-search-directories
+ no-match) ;; whether we know about this kind of file
+
+ (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)))
+
+ (save-excursion
+ (beginning-of-line 1)
+ (setq fname (ff-treat-as-special)))
+
+ (cond
+ ((and (not ff-ignore-include) fname)
+ (setq default-name 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 (if (buffer-file-name)
+ (buffer-file-name)
+ "/none.none"))
+
+ (setq fname (file-name-nondirectory pathname)
+ no-match nil
+ 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
+ (if (not match)
+ (setq no-match t)
+
+ ;; 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 (buffer-file-name))
+ match (cons (car match) (list suffixes))
+ stub nil
+ default-name (car suffixes)))
+
+ ;; 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)))
+ ))
+
+ ;; if we find nothing, we should try to get a file like this one
+ (setq default-name
+ (concat stub (car (car (cdr match))))))
+
+ ;; 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 fname, otherwise search
F1 and F2 are typically `find-file' / `find-file-other-window'
or `switch-to-buffer' / `switch-to-buffer-other-window' function pairs.
-If optional NEW-FILE is t, then a special hook (`ff-file-created-hooks') is
-called before `ff-post-load-hooks'."
- (if ff-pre-load-hooks
- (run-hooks 'ff-pre-load-hooks))
+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))
(if new-file
- (if ff-file-created-hooks
- (run-hooks 'ff-file-created-hooks)))
- (if ff-post-load-hooks
- (run-hooks 'ff-post-load-hooks)))
+ (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."
(defvar ff-function-name nil "Name of the function we are in.")
-(eval-when-compile (require 'ada-mode))
+;(eval-when-compile (require 'ada-mode))
-;; bind with (setq ff-pre-load-hooks 'ff-which-function-are-we-in)
+;; bind with (setq ff-pre-load-hook 'ff-which-function-are-we-in)
;;
(defun ff-which-function-are-we-in ()
"Return the name of the function whose definition/declaration point is in.
(match-end 0)))
))))
-;; bind with (setq ff-post-load-hooks 'ff-set-point-accordingly)
+;; bind with (setq ff-post-load-hook 'ff-set-point-accordingly)
;;
(defun ff-set-point-accordingly ()
"Find the function specified in `ff-function-name'.