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