]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/find-func.el
(prin1-char): Use eventp.
[gnu-emacs] / lisp / emacs-lisp / find-func.el
1 ;;; find-func.el --- find the definition of the Emacs Lisp function near point
2
3 ;; Copyright (C) 1997, 1999, 2001, 2004 Free Software Foundation, Inc.
4
5 ;; Author: Jens Petersen <petersen@kurims.kyoto-u.ac.jp>
6 ;; Maintainer: petersen@kurims.kyoto-u.ac.jp
7 ;; Keywords: emacs-lisp, functions, variables
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!!
31 ;; ;;; find-func
32 ;; (find-function-setup-keys)
33 ;;
34 ;; or just:
35 ;;
36 ;; (load "find-func")
37 ;;
38 ;; if you don't like the given keybindings and away you go! It does
39 ;; pretty much what you would expect, putting the cursor at the
40 ;; definition of the function or variable at point.
41 ;;
42 ;; The code started out from `describe-function', `describe-key'
43 ;; ("help.el") and `fff-find-loaded-emacs-lisp-function' (Noah Friedman's
44 ;; "fff.el").
45
46 ;;; Code:
47
48 (require 'loadhist)
49
50 ;;; User variables:
51
52 (defgroup find-function nil
53 "Finds the definition of the Emacs Lisp symbol near point."
54 ;; :prefix "find-function"
55 :group 'lisp)
56
57 (defconst find-function-space-re "\\(?:\\s-\\|\n\\|;.*\n\\)+")
58
59 (defcustom find-function-regexp
60 ;; Match things like (defun foo ...), (defmacro foo ...),
61 ;; (define-skeleton foo ...), (define-generic-mode 'foo ...),
62 ;; (define-derived-mode foo ...), (define-minor-mode foo)
63 (concat
64 "^\\s-*(\\(def\\(ine-skeleton\\|ine-generic-mode\\|ine-derived-mode\\|\
65 \[^cgv\W]\\w+\\*?\\)\\|define-minor-mode\
66 \\|easy-mmode-define-global-mode\\)" find-function-space-re
67 "\\('\\|\(quote \\)?%s\\(\\s-\\|$\\|\(\\|\)\\)")
68 "The regexp used by `find-function' to search for a function definition.
69 Note it must contain a `%s' at the place where `format'
70 should insert the function name. The default value avoids `defconst',
71 `defgroup', `defvar'.
72
73 Please send improvements and fixes to the maintainer."
74 :type 'regexp
75 :group 'find-function
76 :version "21.1")
77
78 (defcustom find-variable-regexp
79 (concat"^\\s-*(def[^umag]\\(\\w\\|\\s_\\)+\\*?" find-function-space-re "%s\\(\\s-\\|$\\)")
80 "The regexp used by `find-variable' to search for a variable definition.
81 It should match right up to the variable name. The default value
82 avoids `defun', `defmacro', `defalias', `defadvice', `defgroup'.
83
84 Please send improvements and fixes to the maintainer."
85 :type 'regexp
86 :group 'find-function
87 :version "21.1")
88
89 (defcustom find-function-source-path nil
90 "The default list of directories where `find-function' searches.
91
92 If this variable is nil then `find-function' searches `load-path' by
93 default."
94 :type '(repeat directory)
95 :group 'find-function)
96
97 (defcustom find-function-recenter-line 1
98 "The window line-number from which to start displaying a symbol definition.
99 A value of nil implies center the beginning of the definition.
100 See `find-function' and `find-variable'."
101 :type '(choice (const :tag "Center" nil)
102 integer)
103 :group 'find-function
104 :version "20.3")
105
106 (defcustom find-function-after-hook nil
107 "Hook run after finding symbol definition.
108
109 See the functions `find-function' and `find-variable'."
110 :group 'find-function
111 :version "20.3")
112
113 ;;; Functions:
114
115 (defun find-library-suffixes ()
116 (let ((suffixes nil))
117 (dolist (suffix load-suffixes (nreverse suffixes))
118 (unless (string-match "elc" suffix) (push suffix suffixes)))))
119
120 (defun find-library-name (library)
121 "Return the full name of the elisp source of LIBRARY."
122 ;; If the library is byte-compiled, try to find a source library by
123 ;; the same name.
124 (if (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
125 (setq library (replace-match "" t t library)))
126 (or (locate-file library
127 (or find-function-source-path load-path)
128 (append (find-library-suffixes) '("")))
129 (error "Can't find library %s" library)))
130
131 (defvar find-function-C-source-directory
132 (let ((dir (expand-file-name "src" source-directory)))
133 (when (and (file-directory-p dir) (file-readable-p dir))
134 dir))
135 "Directory where the C source files of Emacs can be found.
136 If nil, do not try to find the source code of functions and variables
137 defined in C.")
138
139 (defun find-function-C-source (fun-or-var file variable-p)
140 "Find the source location where SUBR-OR-VAR is defined in FILE.
141 VARIABLE-P should be non-nil for a variable or nil for a subroutine."
142 (unless find-function-C-source-directory
143 (setq find-function-C-source-directory
144 (read-directory-name "Emacs C source dir: " nil nil t)))
145 (setq file (expand-file-name file find-function-C-source-directory))
146 (unless (file-readable-p file)
147 (error "The C source file %s is not available"
148 (file-name-nondirectory file)))
149 (unless variable-p
150 (setq fun-or-var (indirect-function fun-or-var)))
151 (with-current-buffer (find-file-noselect file)
152 (goto-char (point-min))
153 (unless (re-search-forward
154 (if variable-p
155 (concat "DEFVAR[A-Z_]*[ \t\n]*([ \t\n]*\""
156 (regexp-quote (symbol-name fun-or-var))
157 "\"")
158 (concat "DEFUN[ \t\n]*([ \t\n]*\""
159 (regexp-quote (subr-name fun-or-var))
160 "\""))
161 nil t)
162 (error "Can't find source for %s" fun-or-var))
163 (cons (current-buffer) (match-beginning 0))))
164
165 ;;;###autoload
166 (defun find-library (library)
167 "Find the elisp source of LIBRARY."
168 (interactive
169 (list
170 (completing-read "Library name: "
171 'locate-file-completion
172 (cons (or find-function-source-path load-path)
173 (find-library-suffixes)))))
174 (let ((buf (find-file-noselect (find-library-name library))))
175 (condition-case nil (switch-to-buffer buf) (error (pop-to-buffer buf)))))
176
177 ;;;###autoload
178 (defun find-function-search-for-symbol (symbol variable-p library)
179 "Search for SYMBOL.
180 If VARIABLE-P is nil, `find-function-regexp' is used, otherwise
181 `find-variable-regexp' is used. The search is done in library LIBRARY."
182 (if (null library)
183 (error "Don't know where `%s' is defined" symbol))
184 ;; Some functions are defined as part of the construct
185 ;; that defines something else.
186 (while (and (symbolp symbol) (get symbol 'definition-name))
187 (setq symbol (get symbol 'definition-name)))
188 (if (string-match "\\`src/\\(.*\\.c\\)\\'" library)
189 (find-function-C-source symbol (match-string 1 library) variable-p)
190 (if (string-match "\\.el\\(c\\)\\'" library)
191 (setq library (substring library 0 (match-beginning 1))))
192 (let* ((filename (find-library-name library)))
193 (with-current-buffer (find-file-noselect filename)
194 (let ((regexp (format (if variable-p
195 find-variable-regexp
196 find-function-regexp)
197 (regexp-quote (symbol-name symbol))))
198 (case-fold-search))
199 (with-syntax-table emacs-lisp-mode-syntax-table
200 (goto-char (point-min))
201 (if (or (re-search-forward regexp nil t)
202 (re-search-forward
203 (concat "^([^ ]+" find-function-space-re "['(]"
204 (regexp-quote (symbol-name symbol))
205 "\\>")
206 nil t))
207 (progn
208 (beginning-of-line)
209 (cons (current-buffer) (point)))
210 (error "Cannot find definition of `%s' in library `%s'"
211 symbol library))))))))
212
213 ;;;###autoload
214 (defun find-function-noselect (function)
215 "Return a pair (BUFFER . POINT) pointing to the definition of FUNCTION.
216
217 Finds the Emacs Lisp library containing the definition of FUNCTION
218 in a buffer and the point of the definition. The buffer is
219 not selected.
220
221 If the file where FUNCTION is defined is not known, then it is
222 searched for in `find-function-source-path' if non nil, otherwise
223 in `load-path'."
224 (if (not function)
225 (error "You didn't specify a function"))
226 (and (subrp (symbol-function function))
227 (error "%s is a primitive function" function))
228 (let ((def (symbol-function function))
229 aliases)
230 (while (symbolp def)
231 (or (eq def function)
232 (if aliases
233 (setq aliases (concat aliases
234 (format ", which is an alias for `%s'"
235 (symbol-name def))))
236 (setq aliases (format "`%s' an alias for `%s'"
237 function (symbol-name def)))))
238 (setq function (symbol-function function)
239 def (symbol-function function)))
240 (if aliases
241 (message aliases))
242 (let ((library
243 (cond ((eq (car-safe def) 'autoload)
244 (nth 1 def))
245 ((symbol-file function)))))
246 (find-function-search-for-symbol function nil library))))
247
248 (defalias 'function-at-point 'function-called-at-point)
249
250 (defun find-function-read (&optional variable-p)
251 "Read and return an interned symbol, defaulting to the one near point.
252
253 If the optional VARIABLE-P is nil, then a function is gotten
254 defaulting to the value of the function `function-at-point', otherwise
255 a variable is asked for, with the default coming from
256 `variable-at-point'."
257 (let ((symb (funcall (if variable-p
258 'variable-at-point
259 'function-at-point)))
260 (enable-recursive-minibuffers t)
261 val)
262 (if (equal symb 0)
263 (setq symb nil))
264 (setq val (if variable-p
265 (completing-read
266 (concat "Find variable"
267 (if symb
268 (format " (default %s)" symb))
269 ": ")
270 obarray 'boundp t nil)
271 (completing-read
272 (concat "Find function"
273 (if symb
274 (format " (default %s)" symb))
275 ": ")
276 obarray 'fboundp t nil)))
277 (list (if (equal val "")
278 symb
279 (intern val)))))
280
281 (defun find-function-do-it (symbol variable-p switch-fn)
282 "Find Emacs Lisp SYMBOL in a buffer and display it.
283 If VARIABLE-P is nil, a function definition is searched for, otherwise
284 a variable definition is searched for. The start of a definition is
285 centered according to the variable `find-function-recenter-line'.
286 See also `find-function-after-hook' It is displayed with function SWITCH-FN.
287
288 Point is saved in the buffer if it is one of the current buffers."
289 (let* ((orig-point (point))
290 (orig-buf (window-buffer))
291 (orig-buffers (buffer-list))
292 (buffer-point (save-excursion
293 (funcall (if variable-p
294 'find-variable-noselect
295 'find-function-noselect)
296 symbol)))
297 (new-buf (car buffer-point))
298 (new-point (cdr buffer-point)))
299 (when buffer-point
300 (when (memq new-buf orig-buffers)
301 (push-mark orig-point))
302 (funcall switch-fn new-buf)
303 (goto-char new-point)
304 (recenter find-function-recenter-line)
305 (run-hooks 'find-function-after-hook))))
306
307 ;;;###autoload
308 (defun find-function (function)
309 "Find the definition of the FUNCTION near point.
310
311 Finds the Emacs Lisp library containing the definition of the function
312 near point (selected by `function-at-point') in a buffer and
313 places point before the definition. Point is saved in the buffer if
314 it is one of the current buffers.
315
316 The library where FUNCTION is defined is searched for in
317 `find-function-source-path', if non nil, otherwise in `load-path'.
318 See also `find-function-recenter-line' and `find-function-after-hook'."
319 (interactive (find-function-read))
320 (find-function-do-it function nil 'switch-to-buffer))
321
322 ;;;###autoload
323 (defun find-function-other-window (function)
324 "Find, in another window, the definition of FUNCTION near point.
325
326 See `find-function' for more details."
327 (interactive (find-function-read))
328 (find-function-do-it function nil 'switch-to-buffer-other-window))
329
330 ;;;###autoload
331 (defun find-function-other-frame (function)
332 "Find, in ananother frame, the definition of FUNCTION near point.
333
334 See `find-function' for more details."
335 (interactive (find-function-read))
336 (find-function-do-it function nil 'switch-to-buffer-other-frame))
337
338 ;;;###autoload
339 (defun find-variable-noselect (variable &optional file)
340 "Return a pair `(BUFFER . POINT)' pointing to the definition of SYMBOL.
341
342 Finds the Emacs Lisp library containing the definition of SYMBOL
343 in a buffer and the point of the definition. The buffer is
344 not selected.
345
346 The library where VARIABLE is defined is searched for in FILE or
347 `find-function-source-path', if non nil, otherwise in `load-path'."
348 (if (not variable)
349 (error "You didn't specify a variable"))
350 ;; Fixme: I think `symbol-file' should be fixed instead. -- fx
351 (let ((library (or file (symbol-file (cons 'defvar variable)))))
352 (find-function-search-for-symbol variable 'variable library)))
353
354 ;;;###autoload
355 (defun find-variable (variable)
356 "Find the definition of the VARIABLE near point.
357
358 Finds the Emacs Lisp library containing the definition of the variable
359 near point (selected by `variable-at-point') in a buffer and
360 places point before the definition. Point is saved in the buffer if
361 it is one of the current buffers.
362
363 The library where VARIABLE is defined is searched for in
364 `find-function-source-path', if non nil, otherwise in `load-path'.
365 See also `find-function-recenter-line' and `find-function-after-hook'."
366 (interactive (find-function-read 'variable))
367 (find-function-do-it variable t 'switch-to-buffer))
368
369 ;;;###autoload
370 (defun find-variable-other-window (variable)
371 "Find, in another window, the definition of VARIABLE near point.
372
373 See `find-variable' for more details."
374 (interactive (find-function-read 'variable))
375 (find-function-do-it variable t 'switch-to-buffer-other-window))
376
377 ;;;###autoload
378 (defun find-variable-other-frame (variable)
379 "Find, in annother frame, the definition of VARIABLE near point.
380
381 See `find-variable' for more details."
382 (interactive (find-function-read 'variable))
383 (find-function-do-it variable t 'switch-to-buffer-other-frame))
384
385 ;;;###autoload
386 (defun find-function-on-key (key)
387 "Find the function that KEY invokes. KEY is a string.
388 Point is saved if FUNCTION is in the current buffer."
389 (interactive "kFind function on key: ")
390 (let (defn)
391 (save-excursion
392 (let* ((event (and (eventp key) (aref key 0))) ; Null event OK below.
393 (start (event-start event))
394 (modifiers (event-modifiers event))
395 (window (and (or (memq 'click modifiers) (memq 'down modifiers)
396 (memq 'drag modifiers))
397 (posn-window start))))
398 ;; For a mouse button event, go to the button it applies to
399 ;; to get the right key bindings. And go to the right place
400 ;; in case the keymap depends on where you clicked.
401 (when (windowp window)
402 (set-buffer (window-buffer window))
403 (goto-char (posn-point start)))
404 (setq defn (key-binding key))))
405 (let ((key-desc (key-description key)))
406 (if (or (null defn) (integerp defn))
407 (message "%s is unbound" key-desc)
408 (if (consp defn)
409 (message "%s runs %s" key-desc (prin1-to-string defn))
410 (find-function-other-window defn))))))
411
412 ;;;###autoload
413 (defun find-function-at-point ()
414 "Find directly the function at point in the other window."
415 (interactive)
416 (let ((symb (function-at-point)))
417 (when symb
418 (find-function-other-window symb))))
419
420 ;;;###autoload
421 (defun find-variable-at-point ()
422 "Find directly the function at point in the other window."
423 (interactive)
424 (let ((symb (variable-at-point)))
425 (when (and symb (not (equal symb 0)))
426 (find-variable-other-window symb))))
427
428 ;;;###autoload
429 (defun find-function-setup-keys ()
430 "Define some key bindings for the find-function family of functions."
431 (define-key ctl-x-map "F" 'find-function)
432 (define-key ctl-x-4-map "F" 'find-function-other-window)
433 (define-key ctl-x-5-map "F" 'find-function-other-frame)
434 (define-key ctl-x-map "K" 'find-function-on-key)
435 (define-key ctl-x-map "V" 'find-variable)
436 (define-key ctl-x-4-map "V" 'find-variable-other-window)
437 (define-key ctl-x-5-map "V" 'find-variable-other-frame))
438
439 (provide 'find-func)
440
441 ;;; arch-tag: 43ecd81c-74dc-4d9a-8f63-a61e55670d64
442 ;;; find-func.el ends here