]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/find-func.el
6e491409233d1c07bc27b70db7b6df26f9bb5510
[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 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 ;;;###autoload
116 (defun find-function-search-for-symbol (symbol variable-p library)
117 "Search for SYMBOL.
118 If VARIABLE-P is nil, `find-function-regexp' is used, otherwise
119 `find-variable-regexp' is used. The search is done in library LIBRARY."
120 (if (null library)
121 (error "Don't know where `%s' is defined" symbol))
122 ;; Some functions are defined as part of the construct
123 ;; that defines something else.
124 (while (get symbol 'definition-name)
125 (setq symbol (get symbol 'definition-name)))
126 (save-match-data
127 (if (string-match "\\.el\\(c\\)\\'" library)
128 (setq library (substring library 0 (match-beginning 1))))
129 (let* ((path find-function-source-path)
130 (compression (or (rassq 'jka-compr-handler file-name-handler-alist)
131 (member 'crypt-find-file-hook find-file-hook)))
132 (filename (progn
133 ;; use `file-name-sans-extension' here? (if it gets fixed)
134 (if (string-match "\\(\\.el\\)\\'" library)
135 (setq library (substring library 0
136 (match-beginning 1))))
137 (or (locate-library (concat library ".el") t path)
138 (locate-library library t path)
139 (if compression
140 (or (locate-library (concat library ".el.gz")
141 t path)
142 (locate-library (concat library ".gz")
143 t path)))))))
144 (if (not filename)
145 (error "The library `%s' is not in the path" library))
146 (with-current-buffer (find-file-noselect filename)
147 (let ((regexp (format (if variable-p
148 find-variable-regexp
149 find-function-regexp)
150 (regexp-quote (symbol-name symbol))))
151 (case-fold-search))
152 (with-syntax-table emacs-lisp-mode-syntax-table
153 (goto-char (point-min))
154 (if (or (re-search-forward regexp nil t)
155 (re-search-forward
156 (concat "^([^ ]+" find-function-space-re "['(]"
157 (regexp-quote (symbol-name symbol))
158 "\\>")
159 nil t))
160 (progn
161 (beginning-of-line)
162 (cons (current-buffer) (point)))
163 (error "Cannot find definition of `%s' in library `%s'"
164 symbol library))))))))
165
166 ;;;###autoload
167 (defun find-function-noselect (function)
168 "Return a pair (BUFFER . POINT) pointing to the definition of FUNCTION.
169
170 Finds the Emacs Lisp library containing the definition of FUNCTION
171 in a buffer and the point of the definition. The buffer is
172 not selected.
173
174 If the file where FUNCTION is defined is not known, then it is
175 searched for in `find-function-source-path' if non nil, otherwise
176 in `load-path'."
177 (if (not function)
178 (error "You didn't specify a function"))
179 (and (subrp (symbol-function function))
180 (error "%s is a primitive function" function))
181 (let ((def (symbol-function function))
182 aliases)
183 (while (symbolp def)
184 (or (eq def function)
185 (if aliases
186 (setq aliases (concat aliases
187 (format ", which is an alias for `%s'"
188 (symbol-name def))))
189 (setq aliases (format "`%s' an alias for `%s'"
190 function (symbol-name def)))))
191 (setq function (symbol-function function)
192 def (symbol-function function)))
193 (if aliases
194 (message aliases))
195 (let ((library
196 (cond ((eq (car-safe def) 'autoload)
197 (nth 1 def))
198 ((symbol-file function)))))
199 (find-function-search-for-symbol function nil library))))
200
201 (defalias 'function-at-point 'function-called-at-point)
202
203 (defun find-function-read (&optional variable-p)
204 "Read and return an interned symbol, defaulting to the one near point.
205
206 If the optional VARIABLE-P is nil, then a function is gotten
207 defaulting to the value of the function `function-at-point', otherwise
208 a variable is asked for, with the default coming from
209 `variable-at-point'."
210 (let ((symb (funcall (if variable-p
211 'variable-at-point
212 'function-at-point)))
213 (enable-recursive-minibuffers t)
214 val)
215 (if (equal symb 0)
216 (setq symb nil))
217 (setq val (if variable-p
218 (completing-read
219 (concat "Find variable"
220 (if symb
221 (format " (default %s)" symb))
222 ": ")
223 obarray 'boundp t nil)
224 (completing-read
225 (concat "Find function"
226 (if symb
227 (format " (default %s)" symb))
228 ": ")
229 obarray 'fboundp t nil)))
230 (list (if (equal val "")
231 symb
232 (intern val)))))
233
234 (defun find-function-do-it (symbol variable-p switch-fn)
235 "Find Emacs Lisp SYMBOL in a buffer and display it.
236 If VARIABLE-P is nil, a function definition is searched for, otherwise
237 a variable definition is searched for. The start of a definition is
238 centered according to the variable `find-function-recenter-line'.
239 See also `find-function-after-hook' It is displayed with function SWITCH-FN.
240
241 Point is saved in the buffer if it is one of the current buffers."
242 (let* ((orig-point (point))
243 (orig-buf (window-buffer))
244 (orig-buffers (buffer-list))
245 (buffer-point (save-excursion
246 (funcall (if variable-p
247 'find-variable-noselect
248 'find-function-noselect)
249 symbol)))
250 (new-buf (car buffer-point))
251 (new-point (cdr buffer-point)))
252 (when buffer-point
253 (when (memq new-buf orig-buffers)
254 (push-mark orig-point))
255 (funcall switch-fn new-buf)
256 (goto-char new-point)
257 (recenter find-function-recenter-line)
258 (run-hooks 'find-function-after-hook))))
259
260 ;;;###autoload
261 (defun find-function (function)
262 "Find the definition of the FUNCTION near point.
263
264 Finds the Emacs Lisp library containing the definition of the function
265 near point (selected by `function-at-point') in a buffer and
266 places point before the definition. Point is saved in the buffer if
267 it is one of the current buffers.
268
269 The library where FUNCTION is defined is searched for in
270 `find-function-source-path', if non nil, otherwise in `load-path'.
271 See also `find-function-recenter-line' and `find-function-after-hook'."
272 (interactive (find-function-read))
273 (find-function-do-it function nil 'switch-to-buffer))
274
275 ;;;###autoload
276 (defun find-function-other-window (function)
277 "Find, in another window, the definition of FUNCTION near point.
278
279 See `find-function' for more details."
280 (interactive (find-function-read))
281 (find-function-do-it function nil 'switch-to-buffer-other-window))
282
283 ;;;###autoload
284 (defun find-function-other-frame (function)
285 "Find, in ananother frame, the definition of FUNCTION near point.
286
287 See `find-function' for more details."
288 (interactive (find-function-read))
289 (find-function-do-it function nil 'switch-to-buffer-other-frame))
290
291 ;;;###autoload
292 (defun find-variable-noselect (variable &optional file)
293 "Return a pair `(BUFFER . POINT)' pointing to the definition of SYMBOL.
294
295 Finds the Emacs Lisp library containing the definition of SYMBOL
296 in a buffer and the point of the definition. The buffer is
297 not selected.
298
299 The library where VARIABLE is defined is searched for in FILE or
300 `find-function-source-path', if non nil, otherwise in `load-path'."
301 (if (not variable)
302 (error "You didn't specify a variable"))
303 (let ((library (or file (symbol-file variable))))
304 (find-function-search-for-symbol variable 'variable library)))
305
306 ;;;###autoload
307 (defun find-variable (variable)
308 "Find the definition of the VARIABLE near point.
309
310 Finds the Emacs Lisp library containing the definition of the variable
311 near point (selected by `variable-at-point') in a buffer and
312 places point before the definition. Point is saved in the buffer if
313 it is one of the current buffers.
314
315 The library where VARIABLE is defined is searched for in
316 `find-function-source-path', if non nil, otherwise in `load-path'.
317 See also `find-function-recenter-line' and `find-function-after-hook'."
318 (interactive (find-function-read 'variable))
319 (find-function-do-it variable t 'switch-to-buffer))
320
321 ;;;###autoload
322 (defun find-variable-other-window (variable)
323 "Find, in another window, the definition of VARIABLE near point.
324
325 See `find-variable' for more details."
326 (interactive (find-function-read 'variable))
327 (find-function-do-it variable t 'switch-to-buffer-other-window))
328
329 ;;;###autoload
330 (defun find-variable-other-frame (variable)
331 "Find, in annother frame, the definition of VARIABLE near point.
332
333 See `find-variable' for more details."
334 (interactive (find-function-read 'variable))
335 (find-function-do-it variable t 'switch-to-buffer-other-frame))
336
337 ;;;###autoload
338 (defun find-function-on-key (key)
339 "Find the function that KEY invokes. KEY is a string.
340 Point is saved if FUNCTION is in the current buffer."
341 (interactive "kFind function on key: ")
342 (save-excursion
343 (let* ((event (and (eventp key) (aref key 0))) ; Null event OK below.
344 (start (event-start event))
345 (modifiers (event-modifiers event))
346 (window (and (or (memq 'click modifiers) (memq 'down modifiers)
347 (memq 'drag modifiers))
348 (posn-window start))))
349 ;; For a mouse button event, go to the button it applies to
350 ;; to get the right key bindings. And go to the right place
351 ;; in case the keymap depends on where you clicked.
352 (when (windowp window)
353 (set-buffer (window-buffer window))
354 (goto-char (posn-point start)))
355 (let ((defn (key-binding key))
356 (key-desc (key-description key)))
357 (if (or (null defn) (integerp defn))
358 (message "%s is unbound" key-desc)
359 (if (consp defn)
360 (message "%s runs %s" key-desc (prin1-to-string defn))
361 (find-function-other-window defn)))))))
362
363 ;;;###autoload
364 (defun find-function-at-point ()
365 "Find directly the function at point in the other window."
366 (interactive)
367 (let ((symb (function-at-point)))
368 (when symb
369 (find-function-other-window symb))))
370
371 ;;;###autoload
372 (defun find-variable-at-point ()
373 "Find directly the function at point in the other window."
374 (interactive)
375 (let ((symb (variable-at-point)))
376 (when (and symb (not (equal symb 0)))
377 (find-variable-other-window symb))))
378
379 ;;;###autoload
380 (defun find-function-setup-keys ()
381 "Define some key bindings for the find-function family of functions."
382 (define-key ctl-x-map "F" 'find-function)
383 (define-key ctl-x-4-map "F" 'find-function-other-window)
384 (define-key ctl-x-5-map "F" 'find-function-other-frame)
385 (define-key ctl-x-map "K" 'find-function-on-key)
386 (define-key ctl-x-map "V" 'find-variable)
387 (define-key ctl-x-4-map "V" 'find-variable-other-window)
388 (define-key ctl-x-5-map "V" 'find-variable-other-frame))
389
390 (provide 'find-func)
391
392 ;;; find-func.el ends here