Change release version from 21.4 to 22.1 throughout.
[bpt/emacs.git] / lisp / emacs-lisp / find-func.el
CommitLineData
2cd6a032 1;;; find-func.el --- find the definition of the Emacs Lisp function near point
fffee8be 2
1b60b25a 3;; Copyright (C) 1997, 1999, 2001, 2004, 2005 Free Software Foundation, Inc.
fffee8be
DL
4
5;; Author: Jens Petersen <petersen@kurims.kyoto-u.ac.jp>
6;; Maintainer: petersen@kurims.kyoto-u.ac.jp
2cd6a032 7;; Keywords: emacs-lisp, functions, variables
fffee8be
DL
8;; Created: 97/07/25
9
10;; This file is part of GNU Emacs.
11
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
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25;; Boston, MA 02111-1307, USA.
26
27;;; Commentary:
28;;
29;; The funniest thing about this is that I can't imagine why a package
30;; so obviously useful as this hasn't been written before!!
fffee8be 31;; ;;; find-func
86f28125
RS
32;; (find-function-setup-keys)
33;;
34;; or just:
35;;
2cd6a032 36;; (load "find-func")
fffee8be 37;;
86f28125 38;; if you don't like the given keybindings and away you go! It does
2cd6a032
RS
39;; pretty much what you would expect, putting the cursor at the
40;; definition of the function or variable at point.
fffee8be 41;;
2cd6a032 42;; The code started out from `describe-function', `describe-key'
fffee8be
DL
43;; ("help.el") and `fff-find-loaded-emacs-lisp-function' (Noah Friedman's
44;; "fff.el").
45
e8af40ee 46;;; Code:
fffee8be 47
2cd6a032
RS
48(require 'loadhist)
49
fffee8be 50;;; User variables:
2cd6a032 51
0b5bb3ec 52(defgroup find-function nil
2cd6a032
RS
53 "Finds the definition of the Emacs Lisp symbol near point."
54;; :prefix "find-function"
0b5bb3ec 55 :group 'lisp)
fffee8be 56
54cabb1b
SM
57(defconst find-function-space-re "\\(?:\\s-\\|\n\\|;.*\n\\)+")
58
2cd6a032 59(defcustom find-function-regexp
d3d4fb62
RS
60 ;; Match things like (defun foo ...), (defmacro foo ...),
61 ;; (define-skeleton foo ...), (define-generic-mode 'foo ...),
d8f479c6 62 ;; (define-derived-mode foo ...), (define-minor-mode foo)
54cabb1b
SM
63 (concat
64 "^\\s-*(\\(def\\(ine-skeleton\\|ine-generic-mode\\|ine-derived-mode\\|\
b0339ffd 65ine-minor-mode\\|un-cvs-mode\\|foo\\|[^cfgv]\\w+\\*?\\)\
54cabb1b
SM
66\\|easy-mmode-define-global-mode\\)" find-function-space-re
67 "\\('\\|\(quote \\)?%s\\(\\s-\\|$\\|\(\\|\)\\)")
d3d4fb62
RS
68 "The regexp used by `find-function' to search for a function definition.
69Note it must contain a `%s' at the place where `format'
2cd6a032 70should insert the function name. The default value avoids `defconst',
b0339ffd 71`defgroup', `defvar', `defface'.
fffee8be 72
2cd6a032
RS
73Please send improvements and fixes to the maintainer."
74 :type 'regexp
86f28125 75 :group 'find-function
d8f479c6 76 :version "21.1")
2cd6a032
RS
77
78(defcustom find-variable-regexp
1b60b25a 79 (concat"^\\s-*(def[^fumag]\\(\\w\\|\\s_\\)+\\*?" find-function-space-re "%s\\(\\s-\\|$\\)")
2cd6a032 80 "The regexp used by `find-variable' to search for a variable definition.
0acffda1
RS
81Note it must contain a `%s' at the place where `format'
82should insert the variable name. The default value
1b60b25a 83avoids `defun', `defmacro', `defalias', `defadvice', `defgroup', `defface'.
2cd6a032
RS
84
85Please send improvements and fixes to the maintainer."
86 :type 'regexp
86f28125 87 :group 'find-function
a469e8ba 88 :version "21.1")
fffee8be 89
0acffda1
RS
90(defcustom find-face-regexp
91 (concat"^\\s-*(defface" find-function-space-re "%s\\(\\s-\\|$\\)")
92 "The regexp used by `find-face' to search for a face definition.
93Note it must contain a `%s' at the place where `format'
94should insert the face name.
95
96Please send improvements and fixes to the maintainer."
97 :type 'regexp
98 :group 'find-function
bf247b6e 99 :version "22.1")
0acffda1
RS
100
101(defvar find-function-regexp-alist
102 '((nil . find-function-regexp)
103 (defvar . find-variable-regexp)
104 (defface . find-face-regexp))
105 "Alist mapping definition types into regexp variables.
106Each regexp variable's value should actually be a format string
107to be used to substitute the desired symbol name into the regexp.")
108(put 'find-function-regexp-alist 'risky-local-variable t)
109
0b5bb3ec 110(defcustom find-function-source-path nil
d3d4fb62 111 "The default list of directories where `find-function' searches.
fffee8be 112
d3d4fb62 113If this variable is nil then `find-function' searches `load-path' by
0b5bb3ec
SE
114default."
115 :type '(repeat directory)
116 :group 'find-function)
fffee8be 117
2cd6a032
RS
118(defcustom find-function-recenter-line 1
119 "The window line-number from which to start displaying a symbol definition.
120A value of nil implies center the beginning of the definition.
cb0fd101
JPW
121See `find-function' and `find-variable'."
122 :type '(choice (const :tag "Center" nil)
123 integer)
86f28125 124 :group 'find-function
a6223467 125 :version "20.3")
2cd6a032
RS
126
127(defcustom find-function-after-hook nil
128 "Hook run after finding symbol definition.
129
130See the functions `find-function' and `find-variable'."
86f28125 131 :group 'find-function
a6223467 132 :version "20.3")
2cd6a032
RS
133
134;;; Functions:
135
f77a6050
SM
136(defun find-library-suffixes ()
137 (let ((suffixes nil))
138 (dolist (suffix load-suffixes (nreverse suffixes))
139 (unless (string-match "elc" suffix) (push suffix suffixes)))))
140
141(defun find-library-name (library)
142 "Return the full name of the elisp source of LIBRARY."
143 ;; If the library is byte-compiled, try to find a source library by
144 ;; the same name.
1a40d81d
SM
145 (if (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
146 (setq library (replace-match "" t t library)))
f77a6050
SM
147 (or (locate-file library
148 (or find-function-source-path load-path)
43ce8949 149 (append (find-library-suffixes) '("")))
4dbf594b 150 (error "Can't find library %s" library)))
f77a6050 151
4479a2f8
SM
152(defvar find-function-C-source-directory
153 (let ((dir (expand-file-name "src" source-directory)))
154 (when (and (file-directory-p dir) (file-readable-p dir))
155 dir))
156 "Directory where the C source files of Emacs can be found.
157If nil, do not try to find the source code of functions and variables
158defined in C.")
159
0acffda1 160(defun find-function-C-source (fun-or-var file type)
4479a2f8 161 "Find the source location where SUBR-OR-VAR is defined in FILE.
0acffda1 162TYPE should be nil to find a function, or `defvar' to find a variable."
4479a2f8
SM
163 (unless find-function-C-source-directory
164 (setq find-function-C-source-directory
165 (read-directory-name "Emacs C source dir: " nil nil t)))
166 (setq file (expand-file-name file find-function-C-source-directory))
167 (unless (file-readable-p file)
168 (error "The C source file %s is not available"
169 (file-name-nondirectory file)))
0acffda1 170 (unless type
4479a2f8
SM
171 (setq fun-or-var (indirect-function fun-or-var)))
172 (with-current-buffer (find-file-noselect file)
173 (goto-char (point-min))
174 (unless (re-search-forward
0acffda1 175 (if type
4479a2f8
SM
176 (concat "DEFVAR[A-Z_]*[ \t\n]*([ \t\n]*\""
177 (regexp-quote (symbol-name fun-or-var))
178 "\"")
179 (concat "DEFUN[ \t\n]*([ \t\n]*\""
180 (regexp-quote (subr-name fun-or-var))
181 "\""))
182 nil t)
183 (error "Can't find source for %s" fun-or-var))
184 (cons (current-buffer) (match-beginning 0))))
185
f77a6050
SM
186;;;###autoload
187(defun find-library (library)
188 "Find the elisp source of LIBRARY."
189 (interactive
190 (list
191 (completing-read "Library name: "
192 'locate-file-completion
193 (cons (or find-function-source-path load-path)
194 (find-library-suffixes)))))
195 (let ((buf (find-file-noselect (find-library-name library))))
196 (condition-case nil (switch-to-buffer buf) (error (pop-to-buffer buf)))))
197
a4f43048 198;;;###autoload
0acffda1
RS
199(defun find-function-search-for-symbol (symbol type library)
200 "Search for SYMBOL's definition of type TYPE in LIBRARY.
201If TYPE is nil, look for a function definition.
202Otherwise, TYPE specifies the kind of definition,
203and it is interpreted via `find-function-regexp-alist'.
204The search is done in the source for library LIBRARY."
2cd6a032
RS
205 (if (null library)
206 (error "Don't know where `%s' is defined" symbol))
2bef95e5
RS
207 ;; Some functions are defined as part of the construct
208 ;; that defines something else.
4479a2f8 209 (while (and (symbolp symbol) (get symbol 'definition-name))
2bef95e5 210 (setq symbol (get symbol 'definition-name)))
4479a2f8 211 (if (string-match "\\`src/\\(.*\\.c\\)\\'" library)
0acffda1 212 (find-function-C-source symbol (match-string 1 library) type)
78b6524e
DL
213 (if (string-match "\\.el\\(c\\)\\'" library)
214 (setq library (substring library 0 (match-beginning 1))))
0acffda1
RS
215 (let* ((filename (find-library-name library))
216 (regexp-symbol (cdr (assq type find-function-regexp-alist))))
78b6524e 217 (with-current-buffer (find-file-noselect filename)
0acffda1 218 (let ((regexp (format (symbol-value regexp-symbol)
2cd6a032 219 (regexp-quote (symbol-name symbol))))
77186c62 220 (case-fold-search))
fe75e6d0
MR
221 (with-syntax-table emacs-lisp-mode-syntax-table
222 (goto-char (point-min))
223 (if (or (re-search-forward regexp nil t)
224 (re-search-forward
54cabb1b 225 (concat "^([^ ]+" find-function-space-re "['(]"
fe75e6d0 226 (regexp-quote (symbol-name symbol))
b0339ffd 227 "\\_>")
fe75e6d0
MR
228 nil t))
229 (progn
230 (beginning-of-line)
231 (cons (current-buffer) (point)))
232 (error "Cannot find definition of `%s' in library `%s'"
233 symbol library))))))))
2cd6a032 234
8a8a9abe 235;;;###autoload
2cd6a032 236(defun find-function-noselect (function)
d3d4fb62 237 "Return a pair (BUFFER . POINT) pointing to the definition of FUNCTION.
fffee8be
DL
238
239Finds the Emacs Lisp library containing the definition of FUNCTION
2cd6a032 240in a buffer and the point of the definition. The buffer is
fffee8be
DL
241not selected.
242
2cd6a032 243If the file where FUNCTION is defined is not known, then it is
d3d4fb62 244searched for in `find-function-source-path' if non nil, otherwise
2cd6a032 245in `load-path'."
fffee8be
DL
246 (if (not function)
247 (error "You didn't specify a function"))
2cd6a032
RS
248 (and (subrp (symbol-function function))
249 (error "%s is a primitive function" function))
fffee8be 250 (let ((def (symbol-function function))
2cd6a032 251 aliases)
fffee8be
DL
252 (while (symbolp def)
253 (or (eq def function)
254 (if aliases
255 (setq aliases (concat aliases
2cd6a032 256 (format ", which is an alias for `%s'"
fffee8be 257 (symbol-name def))))
2cd6a032
RS
258 (setq aliases (format "`%s' an alias for `%s'"
259 function (symbol-name def)))))
fffee8be
DL
260 (setq function (symbol-function function)
261 def (symbol-function function)))
262 (if aliases
263 (message aliases))
2cd6a032
RS
264 (let ((library
265 (cond ((eq (car-safe def) 'autoload)
266 (nth 1 def))
9f14330b 267 ((symbol-file function 'defun)))))
2cd6a032 268 (find-function-search-for-symbol function nil library))))
fffee8be 269
0acffda1 270(defun find-function-read (&optional type)
2cd6a032 271 "Read and return an interned symbol, defaulting to the one near point.
fffee8be 272
0acffda1
RS
273If TYPE is nil, insist on a symbol with a function definition.
274Otherwise TYPE should be `defvar' or `defface'.
275If TYPE is nil, defaults using `function-called-at-point',
276otherwise uses `variable-at-point'."
277 (let ((symb (if (null type)
278 (function-called-at-point)
279 (if (eq type 'defvar)
280 (variable-at-point)
281 (variable-at-point t))))
282 (predicate (cdr (assq type '((nil . fboundp) (defvar . boundp)
283 (defface . facep)))))
284 (prompt (cdr (assq type '((nil . "function") (defvar . "variable")
285 (defface . "face")))))
fffee8be
DL
286 (enable-recursive-minibuffers t)
287 val)
2cd6a032
RS
288 (if (equal symb 0)
289 (setq symb nil))
0acffda1
RS
290 (setq val (completing-read
291 (concat "Find "
292 prompt
293 (if symb
294 (format " (default %s)" symb))
295 ": ")
296 obarray predicate t nil))
fffee8be 297 (list (if (equal val "")
2cd6a032
RS
298 symb
299 (intern val)))))
fffee8be 300
0acffda1 301(defun find-function-do-it (symbol type switch-fn)
d3d4fb62 302 "Find Emacs Lisp SYMBOL in a buffer and display it.
0acffda1
RS
303TYPE is nil to search for a function definition,
304or else `defvar' or `defface'.
305
306The variable `find-function-recenter-line' controls how
307to recenter the display. SWITCH-FN is the function to call
308to display and select the buffer.
309See also `find-function-after-hook'.
2cd6a032 310
0acffda1 311Set mark before moving, if the buffer already existed."
86f28125
RS
312 (let* ((orig-point (point))
313 (orig-buf (window-buffer))
2cd6a032 314 (orig-buffers (buffer-list))
86f28125 315 (buffer-point (save-excursion
0acffda1 316 (find-definition-noselect symbol type)))
86f28125
RS
317 (new-buf (car buffer-point))
318 (new-point (cdr buffer-point)))
2cd6a032 319 (when buffer-point
86f28125 320 (when (memq new-buf orig-buffers)
2cd6a032 321 (push-mark orig-point))
86f28125
RS
322 (funcall switch-fn new-buf)
323 (goto-char new-point)
2cd6a032 324 (recenter find-function-recenter-line)
8d8ca3cf 325 (run-hooks 'find-function-after-hook))))
fffee8be 326
b07745ec 327;;;###autoload
2cd6a032 328(defun find-function (function)
c7efc289 329 "Find the definition of the FUNCTION near point.
fffee8be
DL
330
331Finds the Emacs Lisp library containing the definition of the function
436c08c2 332near point (selected by `function-called-at-point') in a buffer and
0acffda1
RS
333places point before the definition.
334Set mark before moving, if the buffer already existed.
fffee8be 335
2cd6a032 336The library where FUNCTION is defined is searched for in
d3d4fb62 337`find-function-source-path', if non nil, otherwise in `load-path'.
2cd6a032
RS
338See also `find-function-recenter-line' and `find-function-after-hook'."
339 (interactive (find-function-read))
340 (find-function-do-it function nil 'switch-to-buffer))
fffee8be 341
b07745ec 342;;;###autoload
2cd6a032 343(defun find-function-other-window (function)
c7efc289 344 "Find, in another window, the definition of FUNCTION near point.
fffee8be 345
2cd6a032
RS
346See `find-function' for more details."
347 (interactive (find-function-read))
348 (find-function-do-it function nil 'switch-to-buffer-other-window))
fffee8be 349
b07745ec 350;;;###autoload
2cd6a032 351(defun find-function-other-frame (function)
c7efc289 352 "Find, in ananother frame, the definition of FUNCTION near point.
fffee8be 353
2cd6a032
RS
354See `find-function' for more details."
355 (interactive (find-function-read))
356 (find-function-do-it function nil 'switch-to-buffer-other-frame))
357
e1acbda2 358;;;###autoload
1c787e34
SM
359(defun find-variable-noselect (variable &optional file)
360 "Return a pair `(BUFFER . POINT)' pointing to the definition of SYMBOL.
2cd6a032
RS
361
362Finds the Emacs Lisp library containing the definition of SYMBOL
0acffda1
RS
363in a buffer, and the point of the definition. It does not switch
364to the buffer or display it.
2cd6a032 365
1c787e34 366The library where VARIABLE is defined is searched for in FILE or
d3d4fb62 367`find-function-source-path', if non nil, otherwise in `load-path'."
2cd6a032
RS
368 (if (not variable)
369 (error "You didn't specify a variable"))
9f14330b 370 (let ((library (or file (symbol-file variable 'defvar))))
0acffda1 371 (find-function-search-for-symbol variable 'defvar library)))
2cd6a032
RS
372
373;;;###autoload
374(defun find-variable (variable)
c7efc289 375 "Find the definition of the VARIABLE near point.
2cd6a032
RS
376
377Finds the Emacs Lisp library containing the definition of the variable
378near point (selected by `variable-at-point') in a buffer and
0acffda1
RS
379places point before the definition.
380
381Set mark before moving, if the buffer already existed.
2cd6a032
RS
382
383The library where VARIABLE is defined is searched for in
d3d4fb62 384`find-function-source-path', if non nil, otherwise in `load-path'.
2cd6a032 385See also `find-function-recenter-line' and `find-function-after-hook'."
436c08c2
RS
386 (interactive (find-function-read 'defvar))
387 (find-function-do-it variable 'defvar 'switch-to-buffer))
fffee8be 388
2cd6a032
RS
389;;;###autoload
390(defun find-variable-other-window (variable)
c7efc289 391 "Find, in another window, the definition of VARIABLE near point.
2cd6a032
RS
392
393See `find-variable' for more details."
436c08c2
RS
394 (interactive (find-function-read 'defvar))
395 (find-function-do-it variable 'defvar 'switch-to-buffer-other-window))
2cd6a032
RS
396
397;;;###autoload
398(defun find-variable-other-frame (variable)
c7efc289 399 "Find, in annother frame, the definition of VARIABLE near point.
2cd6a032
RS
400
401See `find-variable' for more details."
436c08c2
RS
402 (interactive (find-function-read 'defvar))
403 (find-function-do-it variable 'defvar 'switch-to-buffer-other-frame))
fffee8be 404
0acffda1
RS
405;;;###autoload
406(defun find-definition-noselect (symbol type &optional file)
407 "Return a pair `(BUFFER . POINT)' pointing to the definition of SYMBOL.
408TYPE says what type of definition: nil for a function,
409`defvar' or `defface' for a variable or face. This functoin
410does not switch to the buffer or display it.
411
412The library where SYMBOL is defined is searched for in FILE or
413`find-function-source-path', if non nil, otherwise in `load-path'."
414 (if (not symbol)
415 (error "You didn't specify a symbol"))
416 (if (null type)
417 (find-function-noselect symbol)
418 (let ((library (or file (symbol-file symbol type))))
419 (find-function-search-for-symbol symbol type library))))
420
c7ce7fc4
RS
421;; For symmetry, this should be called find-face; but some programs
422;; assume that, if that name is defined, it means something else.
0acffda1 423;;;###autoload
c7ce7fc4 424(defun find-face-definition (face)
0acffda1
RS
425 "Find the definition of FACE. FACE defaults to the name near point.
426
427Finds the Emacs Lisp library containing the definition of the face
428near point (selected by `variable-at-point') in a buffer and
429places point before the definition.
430
431Set mark before moving, if the buffer already existed.
432
433The library where FACE is defined is searched for in
434`find-function-source-path', if non nil, otherwise in `load-path'.
435See also `find-function-recenter-line' and `find-function-after-hook'."
436 (interactive (find-function-read 'defface))
437 (find-function-do-it face 'defface 'switch-to-buffer))
438
b07745ec 439;;;###autoload
fffee8be
DL
440(defun find-function-on-key (key)
441 "Find the function that KEY invokes. KEY is a string.
0acffda1 442Set mark before moving, if the buffer already existed."
fffee8be 443 (interactive "kFind function on key: ")
cb4f3fc8
RS
444 (let (defn)
445 (save-excursion
446 (let* ((event (and (eventp key) (aref key 0))) ; Null event OK below.
447 (start (event-start event))
448 (modifiers (event-modifiers event))
449 (window (and (or (memq 'click modifiers) (memq 'down modifiers)
450 (memq 'drag modifiers))
451 (posn-window start))))
452 ;; For a mouse button event, go to the button it applies to
453 ;; to get the right key bindings. And go to the right place
454 ;; in case the keymap depends on where you clicked.
455 (when (windowp window)
456 (set-buffer (window-buffer window))
457 (goto-char (posn-point start)))
458 (setq defn (key-binding key))))
459 (let ((key-desc (key-description key)))
460 (if (or (null defn) (integerp defn))
461 (message "%s is unbound" key-desc)
462 (if (consp defn)
463 (message "%s runs %s" key-desc (prin1-to-string defn))
464 (find-function-other-window defn))))))
2cd6a032
RS
465
466;;;###autoload
467(defun find-function-at-point ()
468 "Find directly the function at point in the other window."
469 (interactive)
436c08c2 470 (let ((symb (function-called-at-point)))
2cd6a032
RS
471 (when symb
472 (find-function-other-window symb))))
473
474;;;###autoload
475(defun find-variable-at-point ()
476 "Find directly the function at point in the other window."
477 (interactive)
478 (let ((symb (variable-at-point)))
479 (when (and symb (not (equal symb 0)))
480 (find-variable-other-window symb))))
fffee8be 481
86f28125
RS
482;;;###autoload
483(defun find-function-setup-keys ()
484 "Define some key bindings for the find-function family of functions."
485 (define-key ctl-x-map "F" 'find-function)
486 (define-key ctl-x-4-map "F" 'find-function-other-window)
487 (define-key ctl-x-5-map "F" 'find-function-other-frame)
488 (define-key ctl-x-map "K" 'find-function-on-key)
489 (define-key ctl-x-map "V" 'find-variable)
490 (define-key ctl-x-4-map "V" 'find-variable-other-window)
491 (define-key ctl-x-5-map "V" 'find-variable-other-frame))
492
fffee8be
DL
493(provide 'find-func)
494
1b60b25a 495;; arch-tag: 43ecd81c-74dc-4d9a-8f63-a61e55670d64
fffee8be 496;;; find-func.el ends here