]> code.delx.au - gnu-emacs/blob - lisp/emacs-lisp/find-func.el
(find-function-regexp): Allow a ) or ( to end a function name.
[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 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 (defcustom find-function-regexp
58 ;; Match things like (defun foo ...), (defmacro foo ...),
59 ;; (define-skeleton foo ...), (define-generic-mode 'foo ...),
60 ;; (define-derived-mode foo ...), (easy-mmode-define-minor-mode foo)
61 "^\\s-*(\\(def\\(ine-skeleton\\|ine-generic-mode\\|ine-derived-mode\\|\
62 \[^cgv\W]\\w+\\*?\\)\\|easy-mmode-define-minor-mode\\)\\s-+'?\
63 %s\\(\\s-\\|$\\|\(\\|\)\\)"
64 "The regexp used by `find-function' to search for a function definition.
65 Note it must contain a `%s' at the place where `format'
66 should insert the function name. The default value avoids `defconst',
67 `defgroup', `defvar'.
68
69 Please send improvements and fixes to the maintainer."
70 :type 'regexp
71 :group 'find-function
72 :version "20.3")
73
74 (defcustom find-variable-regexp
75 "^\\s-*(def[^uma\W]\\w+\\*?\\s-+%s\\(\\s-\\|$\\)"
76 "The regexp used by `find-variable' to search for a variable definition.
77 It should match right up to the variable name. The default value
78 avoids `defun', `defmacro', `defalias', `defadvice'.
79
80 Please send improvements and fixes to the maintainer."
81 :type 'regexp
82 :group 'find-function
83 :version "20.3")
84
85 (defcustom find-function-source-path nil
86 "The default list of directories where `find-function' searches.
87
88 If this variable is nil then `find-function' searches `load-path' by
89 default."
90 :type '(repeat directory)
91 :group 'find-function)
92
93 (defcustom find-function-recenter-line 1
94 "The window line-number from which to start displaying a symbol definition.
95 A value of nil implies center the beginning of the definition.
96 See the function `center-to-window-line' for more information, and
97 `find-function' and `find-variable'."
98 :group 'find-function
99 :version "20.3")
100
101 (defcustom find-function-after-hook nil
102 "Hook run after finding symbol definition.
103
104 See the functions `find-function' and `find-variable'."
105 :group 'find-function
106 :version "20.3")
107
108 ;;; Functions:
109
110 (defun find-function-search-for-symbol (symbol variable-p library)
111 "Search for SYMBOL.
112 If VARIABLE-P is nil, `find-function-regexp' is used, otherwise
113 `find-variable-regexp' is used. The search is done in library LIBRARY."
114 (if (null library)
115 (error "Don't know where `%s' is defined" symbol))
116 (save-match-data
117 (if (string-match "\\.el\\(c\\)\\'" library)
118 (setq library (substring library 0 (match-beginning 1))))
119 (let* ((path find-function-source-path)
120 (compression (or (rassq 'jka-compr-handler file-name-handler-alist)
121 (member 'crypt-find-file-hook find-file-hooks)))
122 (filename (progn
123 ;; use `file-name-sans-extension' here? (if it gets fixed)
124 (if (string-match "\\(\\.el\\)\\'" library)
125 (setq library (substring library 0
126 (match-beginning 1))))
127 (or (locate-library (concat library ".el") t path)
128 (locate-library library t path)
129 (if compression
130 (or (locate-library (concat library ".el.gz")
131 t path)
132 (locate-library (concat library ".gz")
133 t path)))))))
134 (if (not filename)
135 (error "The library `%s' is not in the path" library))
136 (with-current-buffer (find-file-noselect filename)
137 (let ((regexp (format (if variable-p
138 find-variable-regexp
139 find-function-regexp)
140 (regexp-quote (symbol-name symbol))))
141 (syn-table (syntax-table)))
142 (unwind-protect
143 (progn
144 (set-syntax-table emacs-lisp-mode-syntax-table)
145 (goto-char (point-min))
146 (if (re-search-forward regexp nil t)
147 (progn
148 (beginning-of-line)
149 (cons (current-buffer) (point)))
150 (error "Cannot find definition of `%s' in library `%s'"
151 symbol library)))
152 (set-syntax-table syn-table)))))))
153
154 ;;;###autoload
155 (defun find-function-noselect (function)
156 "Return a pair (BUFFER . POINT) pointing to the definition of FUNCTION.
157
158 Finds the Emacs Lisp library containing the definition of FUNCTION
159 in a buffer and the point of the definition. The buffer is
160 not selected.
161
162 If the file where FUNCTION is defined is not known, then it is
163 searched for in `find-function-source-path' if non nil, otherwise
164 in `load-path'."
165 (if (not function)
166 (error "You didn't specify a function"))
167 (and (subrp (symbol-function function))
168 (error "%s is a primitive function" function))
169 (let ((def (symbol-function function))
170 aliases)
171 (while (symbolp def)
172 (or (eq def function)
173 (if aliases
174 (setq aliases (concat aliases
175 (format ", which is an alias for `%s'"
176 (symbol-name def))))
177 (setq aliases (format "`%s' an alias for `%s'"
178 function (symbol-name def)))))
179 (setq function (symbol-function function)
180 def (symbol-function function)))
181 (if aliases
182 (message aliases))
183 (let ((library
184 (cond ((eq (car-safe def) 'autoload)
185 (nth 1 def))
186 ((symbol-file function)))))
187 (find-function-search-for-symbol function nil library))))
188
189 (defun function-at-point ()
190 (or (condition-case ()
191 (let ((stab (syntax-table)))
192 (unwind-protect
193 (save-excursion
194 (set-syntax-table emacs-lisp-mode-syntax-table)
195 (or (not (zerop (skip-syntax-backward "_w")))
196 (eq (char-syntax (char-after (point))) ?w)
197 (eq (char-syntax (char-after (point))) ?_)
198 (forward-sexp -1))
199 (skip-chars-forward "`'")
200 (let ((obj (read (current-buffer))))
201 (and (symbolp obj) (fboundp obj) obj)))
202 (set-syntax-table stab)))
203 (error nil))
204 (condition-case ()
205 (save-excursion
206 (save-restriction
207 (narrow-to-region (max (point-min) (- (point) 1000)) (point-max))
208 (backward-up-list 1)
209 (forward-char 1)
210 (let (obj)
211 (setq obj (read (current-buffer)))
212 (and (symbolp obj) (fboundp obj) obj))))
213 (error nil))))
214
215 (defun find-function-read (&optional variable-p)
216 "Read and return an interned symbol, defaulting to the one near point.
217
218 If the optional VARIABLE-P is nil, then a function is gotten
219 defaulting to the value of the function `function-at-point', otherwise
220 a variable is asked for, with the default coming from
221 `variable-at-point'."
222 (let ((symb (funcall (if variable-p
223 'variable-at-point
224 'function-at-point)))
225 (enable-recursive-minibuffers t)
226 val)
227 (if (equal symb 0)
228 (setq symb nil))
229 (setq val (if variable-p
230 (completing-read
231 (concat "Find variable"
232 (if symb
233 (format " (default %s)" symb))
234 ": ")
235 obarray 'boundp t nil)
236 (completing-read
237 (concat "Find function"
238 (if symb
239 (format " (default %s)" symb))
240 ": ")
241 obarray 'fboundp t nil)))
242 (list (if (equal val "")
243 symb
244 (intern val)))))
245
246 (defun find-function-do-it (symbol variable-p switch-fn)
247 "Find Emacs Lisp SYMBOL in a buffer and display it.
248 If VARIABLE-P is nil, a function definition is searched for, otherwise
249 a variable definition is searched for. The start of a definition is
250 centered according to the variable `find-function-recenter-line'.
251 See also `find-function-after-hook' It is displayed with function SWITCH-FN.
252
253 Point is saved in the buffer if it is one of the current buffers."
254 (let* ((orig-point (point))
255 (orig-buf (window-buffer))
256 (orig-buffers (buffer-list))
257 (buffer-point (save-excursion
258 (funcall (if variable-p
259 'find-variable-noselect
260 'find-function-noselect)
261 symbol)))
262 (new-buf (car buffer-point))
263 (new-point (cdr buffer-point)))
264 (when buffer-point
265 (when (memq new-buf orig-buffers)
266 (push-mark orig-point))
267 (funcall switch-fn new-buf)
268 (goto-char new-point)
269 (recenter find-function-recenter-line)
270 (run-hooks find-function-after-hook))))
271
272 ;;;###autoload
273 (defun find-function (function)
274 "Find the definition of the FUNCTION near point.
275
276 Finds the Emacs Lisp library containing the definition of the function
277 near point (selected by `function-at-point') in a buffer and
278 places point before the definition. Point is saved in the buffer if
279 it is one of the current buffers.
280
281 The library where FUNCTION is defined is searched for in
282 `find-function-source-path', if non nil, otherwise in `load-path'.
283 See also `find-function-recenter-line' and `find-function-after-hook'."
284 (interactive (find-function-read))
285 (find-function-do-it function nil 'switch-to-buffer))
286
287 ;;;###autoload
288 (defun find-function-other-window (function)
289 "Find, in another window, the definition of FUNCTION near point.
290
291 See `find-function' for more details."
292 (interactive (find-function-read))
293 (find-function-do-it function nil 'switch-to-buffer-other-window))
294
295 ;;;###autoload
296 (defun find-function-other-frame (function)
297 "Find, in ananother frame, the definition of FUNCTION near point.
298
299 See `find-function' for more details."
300 (interactive (find-function-read))
301 (find-function-do-it function nil 'switch-to-buffer-other-frame))
302
303 ;;;###autoload
304 (defun find-variable-noselect (variable)
305 "Return a pair `(buffer . point)' pointing to the definition of SYMBOL.
306
307 Finds the Emacs Lisp library containing the definition of SYMBOL
308 in a buffer and the point of the definition. The buffer is
309 not selected.
310
311 The library where VARIABLE is defined is searched for in
312 `find-function-source-path', if non nil, otherwise in `load-path'."
313 (if (not variable)
314 (error "You didn't specify a variable"))
315 (let ((library (symbol-file variable)))
316 (find-function-search-for-symbol variable 'variable library)))
317
318 ;;;###autoload
319 (defun find-variable (variable)
320 "Find the definition of the VARIABLE near point.
321
322 Finds the Emacs Lisp library containing the definition of the variable
323 near point (selected by `variable-at-point') in a buffer and
324 places point before the definition. Point is saved in the buffer if
325 it is one of the current buffers.
326
327 The library where VARIABLE is defined is searched for in
328 `find-function-source-path', if non nil, otherwise in `load-path'.
329 See also `find-function-recenter-line' and `find-function-after-hook'."
330 (interactive (find-function-read 'variable))
331 (find-function-do-it variable t 'switch-to-buffer))
332
333 ;;;###autoload
334 (defun find-variable-other-window (variable)
335 "Find, in another window, the definition of VARIABLE near point.
336
337 See `find-variable' for more details."
338 (interactive (find-function-read 'variable))
339 (find-function-do-it variable t 'switch-to-buffer-other-window))
340
341 ;;;###autoload
342 (defun find-variable-other-frame (variable)
343 "Find, in annother frame, the definition of VARIABLE near point.
344
345 See `find-variable' for more details."
346 (interactive (find-function-read 'variable))
347 (find-function-do-it variable t 'switch-to-buffer-other-frame))
348
349 ;;;###autoload
350 (defun find-function-on-key (key)
351 "Find the function that KEY invokes. KEY is a string.
352 Point is saved if FUNCTION is in the current buffer."
353 (interactive "kFind function on key: ")
354 (save-excursion
355 (let* ((event (and (eventp key) (aref key 0))) ; Null event OK below.
356 (start (event-start event))
357 (modifiers (event-modifiers event))
358 (window (and (or (memq 'click modifiers) (memq 'down modifiers)
359 (memq 'drag modifiers))
360 (posn-window start))))
361 ;; For a mouse button event, go to the button it applies to
362 ;; to get the right key bindings. And go to the right place
363 ;; in case the keymap depends on where you clicked.
364 (when (windowp window)
365 (set-buffer (window-buffer window))
366 (goto-char (posn-point start)))
367 (let ((defn (key-binding key))
368 (key-desc (key-description key)))
369 (if (or (null defn) (integerp defn))
370 (message "%s is unbound" key-desc)
371 (if (consp defn)
372 (message "%s runs %s" key-desc (prin1-to-string defn))
373 (find-function-other-window defn)))))))
374
375 ;;;###autoload
376 (defun find-function-at-point ()
377 "Find directly the function at point in the other window."
378 (interactive)
379 (let ((symb (function-at-point)))
380 (when symb
381 (find-function-other-window symb))))
382
383 ;;;###autoload
384 (defun find-variable-at-point ()
385 "Find directly the function at point in the other window."
386 (interactive)
387 (let ((symb (variable-at-point)))
388 (when (and symb (not (equal symb 0)))
389 (find-variable-other-window symb))))
390
391 ;;;###autoload
392 (defun find-function-setup-keys ()
393 "Define some key bindings for the find-function family of functions."
394 (define-key ctl-x-map "F" 'find-function)
395 (define-key ctl-x-4-map "F" 'find-function-other-window)
396 (define-key ctl-x-5-map "F" 'find-function-other-frame)
397 (define-key ctl-x-map "K" 'find-function-on-key)
398 (define-key ctl-x-map "V" 'find-variable)
399 (define-key ctl-x-4-map "V" 'find-variable-other-window)
400 (define-key ctl-x-5-map "V" 'find-variable-other-frame))
401
402 (provide 'find-func)
403
404 ;;; find-func.el ends here