diff options
| author | Noah Friedman | 1995-11-12 21:04:08 +0000 |
|---|---|---|
| committer | Noah Friedman | 1995-11-12 21:04:08 +0000 |
| commit | 1b09702ac83110f91a6ecb3360b412ff70ce6611 (patch) | |
| tree | 63de89ec8f8f308c93fe2dd3b4ea3694ec976ce4 | |
| parent | 08bc143c8b79a19157253962dff688c55fb993a4 (diff) | |
| download | emacs-1b09702ac83110f91a6ecb3360b412ff70ce6611.tar.gz emacs-1b09702ac83110f91a6ecb3360b412ff70ce6611.zip | |
Initial revision
| -rw-r--r-- | lisp/emacs-lisp/eldoc.el | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el new file mode 100644 index 00000000000..d5cddaa1626 --- /dev/null +++ b/lisp/emacs-lisp/eldoc.el | |||
| @@ -0,0 +1,423 @@ | |||
| 1 | ;;; eldoc.el --- show function arglist or variable docstring in echo area | ||
| 2 | |||
| 3 | ;; Copyright (C) 1995 Noah S. Friedman | ||
| 4 | |||
| 5 | ;; Author: Noah Friedman <friedman@prep.ai.mit.edu> | ||
| 6 | ;; Maintainer: friedman@prep.ai.mit.edu | ||
| 7 | ;; Keywords: extensions | ||
| 8 | ;; Status: Works in Emacs 19 and XEmacs. | ||
| 9 | ;; Created: 1995-10-06 | ||
| 10 | |||
| 11 | ;; LCD Archive Entry: | ||
| 12 | ;; eldoc|Noah Friedman|friedman@prep.ai.mit.edu| | ||
| 13 | ;; show function arglist or variable docstring in echo area| | ||
| 14 | ;; $Date$|$Revision$|~/misc/eldoc.el.gz| | ||
| 15 | |||
| 16 | ;; $Id$ | ||
| 17 | |||
| 18 | ;; This program is free software; you can redistribute it and/or modify | ||
| 19 | ;; it under the terms of the GNU General Public License as published by | ||
| 20 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 21 | ;; any later version. | ||
| 22 | ;; | ||
| 23 | ;; This program is distributed in the hope that it will be useful, | ||
| 24 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 25 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 26 | ;; GNU General Public License for more details. | ||
| 27 | ;; | ||
| 28 | ;; You should have received a copy of the GNU General Public License | ||
| 29 | ;; along with this program; if not, you can either send email to this | ||
| 30 | ;; program's maintainer or write to: The Free Software Foundation, | ||
| 31 | ;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA. | ||
| 32 | |||
| 33 | ;;; Commentary: | ||
| 34 | |||
| 35 | ;; This program was inspired by the behavior of the Lisp Machine "mouse | ||
| 36 | ;; documentation window"; as you type a function's symbol name as part of a | ||
| 37 | ;; sexp, it will print the argument list for that function. However, this | ||
| 38 | ;; program's behavior is different in a couple of significant ways. For | ||
| 39 | ;; one, you need not actually type the function name; you need only move | ||
| 40 | ;; point around in a sexp that calls it. However, if point is over a | ||
| 41 | ;; documented variable, it will print the one-line documentation for that | ||
| 42 | ;; variable instead, to remind you of that variable's purpose. | ||
| 43 | |||
| 44 | ;; One useful way to enable this minor mode is to put the following in your | ||
| 45 | ;; .emacs: | ||
| 46 | ;; | ||
| 47 | ;; (autoload 'turn-on-eldoc-mode "eldoc" nil t) | ||
| 48 | ;; (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) | ||
| 49 | ;; (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode) | ||
| 50 | |||
| 51 | ;;; Code: | ||
| 52 | |||
| 53 | ;;;###autoload | ||
| 54 | (defvar eldoc-mode nil | ||
| 55 | "*If non-nil, show the defined parameters for the elisp function near point. | ||
| 56 | |||
| 57 | For the emacs lisp function at the beginning of the sexp which point is | ||
| 58 | within, show the defined parameters for the function in the echo area. | ||
| 59 | This information is extracted directly from the function or macro if it is | ||
| 60 | in pure lisp. | ||
| 61 | |||
| 62 | If the emacs function is a subr, the parameters are obtained from the | ||
| 63 | documentation string if possible. | ||
| 64 | |||
| 65 | If point is over a documented variable, print that variable's docstring | ||
| 66 | instead; see function `eldoc-print-var-docstring'. | ||
| 67 | |||
| 68 | This variable is buffer-local.") | ||
| 69 | (make-variable-buffer-local 'eldoc-mode) | ||
| 70 | |||
| 71 | (defvar eldoc-idle-delay 0.50 | ||
| 72 | "*Number of seconds of idle time to wait before printing. | ||
| 73 | If user input arrives before this interval of time has elapsed after the | ||
| 74 | last input, no documentation will be printed. | ||
| 75 | |||
| 76 | If this variable is set to 0, no idle time is required.") | ||
| 77 | |||
| 78 | (defvar eldoc-argument-case 'upcase | ||
| 79 | "Case to display argument names of functions, as a symbol. | ||
| 80 | This has two preferred values: `upcase' or `downcase'. | ||
| 81 | Actually, any name of a function which takes a string as an argument and | ||
| 82 | returns another string is acceptable.") | ||
| 83 | |||
| 84 | (defvar eldoc-mode-message-commands nil | ||
| 85 | "*Obarray of command names where it is appropriate to print in the echo area. | ||
| 86 | |||
| 87 | This is not done for all commands since some print their own | ||
| 88 | messages in the echo area, and these functions would instantly overwrite | ||
| 89 | them. But self-insert-command as well as most motion commands are good | ||
| 90 | candidates. | ||
| 91 | |||
| 92 | It is probably best to manipulate this data structure with the commands | ||
| 93 | `eldoc-add-command' and `eldoc-remove-command'.") | ||
| 94 | |||
| 95 | (cond ((null eldoc-mode-message-commands) | ||
| 96 | ;; If you increase the number of buckets, keep it a prime number. | ||
| 97 | (setq eldoc-mode-message-commands (make-vector 31 0)) | ||
| 98 | (let ((list '("self-insert-command" | ||
| 99 | "next-" "previous-" | ||
| 100 | "forward-" "backward-" | ||
| 101 | "beginning-of-" "end-of-" | ||
| 102 | "goto-" | ||
| 103 | "recenter" | ||
| 104 | "scroll-")) | ||
| 105 | (syms nil)) | ||
| 106 | (while list | ||
| 107 | (setq syms (all-completions (car list) obarray 'fboundp)) | ||
| 108 | (setq list (cdr list)) | ||
| 109 | (while syms | ||
| 110 | (set (intern (car syms) eldoc-mode-message-commands) t) | ||
| 111 | (setq syms (cdr syms))))))) | ||
| 112 | |||
| 113 | ;; Bookkeeping; the car contains the last symbol read from the buffer. | ||
| 114 | ;; The cdr contains the string last displayed in the echo area, so it can | ||
| 115 | ;; be printed again if necessary without reconsing. | ||
| 116 | (defvar eldoc-last-data '(nil . nil)) | ||
| 117 | |||
| 118 | ;; Put this minor mode on the minor-mode-alist. | ||
| 119 | (or (assq 'eldoc-mode minor-mode-alist) | ||
| 120 | (setq-default minor-mode-alist | ||
| 121 | (append minor-mode-alist '((eldoc-mode " ElDoc"))))) | ||
| 122 | |||
| 123 | |||
| 124 | ;;;###autoload | ||
| 125 | (defun eldoc-mode (&optional prefix) | ||
| 126 | "*If non-nil, then enable eldoc-mode (see variable docstring)." | ||
| 127 | (interactive "P") | ||
| 128 | |||
| 129 | ;; Make sure it's on the post-command-idle-hook if defined, otherwise put | ||
| 130 | ;; it on post-command-hook. The former first appeared in Emacs 19.30. | ||
| 131 | (add-hook (if (boundp 'post-command-idle-hook) | ||
| 132 | 'post-command-idle-hook | ||
| 133 | 'post-command-hook) | ||
| 134 | 'eldoc-mode-print-current-symbol-info) | ||
| 135 | |||
| 136 | (setq eldoc-mode | ||
| 137 | (>= (prefix-numeric-value prefix) 0)) | ||
| 138 | (and (interactive-p) | ||
| 139 | (if eldoc-mode | ||
| 140 | (message "eldoc-mode is enabled") | ||
| 141 | (message "eldoc-mode is disabled"))) | ||
| 142 | eldoc-mode) | ||
| 143 | |||
| 144 | ;;;###autoload | ||
| 145 | (defun turn-on-eldoc-mode () | ||
| 146 | "Unequivocally turn on eldoc-mode (see variable documentation)." | ||
| 147 | (interactive) | ||
| 148 | (eldoc-mode 1)) | ||
| 149 | |||
| 150 | (defun eldoc-add-command (cmd) | ||
| 151 | "Add COMMAND to the list of commands which causes function arg display. | ||
| 152 | If called interactively, completion matches any bound function. | ||
| 153 | |||
| 154 | When point is in a sexp, the function args are not reprinted in the echo | ||
| 155 | area after every possible interactive command because some of them print | ||
| 156 | their own messages in the echo area; the eldoc functions would instantly | ||
| 157 | overwrite them unless it is more restrained." | ||
| 158 | (interactive "aAdd function to eldoc message commands list: ") | ||
| 159 | (and (fboundp cmd) | ||
| 160 | (set (intern (symbol-name cmd) eldoc-mode-message-commands) t))) | ||
| 161 | |||
| 162 | (defun eldoc-remove-command (cmd) | ||
| 163 | "Remove COMMAND from the list of commands which causes function arg display. | ||
| 164 | If called interactively, completion matches only those functions currently | ||
| 165 | in the list. | ||
| 166 | |||
| 167 | When point is in a sexp, the function args are not reprinted in the echo | ||
| 168 | area after every possible interactive command because some of them print | ||
| 169 | their own messages in the echo area; the eldoc functions would instantly | ||
| 170 | overwrite them unless it is more restrained." | ||
| 171 | (interactive (list (completing-read | ||
| 172 | "Remove function from eldoc message commands list: " | ||
| 173 | eldoc-mode-message-commands 'boundp t))) | ||
| 174 | (and (symbolp cmd) | ||
| 175 | (setq cmd (symbol-name cmd))) | ||
| 176 | (if (fboundp 'unintern) | ||
| 177 | (unintern cmd eldoc-mode-message-commands) | ||
| 178 | (let ((s (intern-soft cmd eldoc-mode-message-commands))) | ||
| 179 | (and s | ||
| 180 | (makunbound s))))) | ||
| 181 | |||
| 182 | (defun eldoc-mode-print-current-symbol-info () | ||
| 183 | (and eldoc-mode | ||
| 184 | ;; Having this mode operate in the minibuffer makes it impossible to | ||
| 185 | ;; see what you're doing. | ||
| 186 | (not (eq (selected-window) (minibuffer-window))) | ||
| 187 | (sit-for eldoc-idle-delay) | ||
| 188 | (intern-soft (symbol-name this-command) eldoc-mode-message-commands) | ||
| 189 | (let ((current-symbol (eldoc-current-symbol)) | ||
| 190 | (current-fnsym (eldoc-fnsym-in-current-sexp))) | ||
| 191 | (cond ((eq current-symbol current-fnsym) | ||
| 192 | (eldoc-print-fnsym-args current-fnsym)) | ||
| 193 | (t | ||
| 194 | (or (eldoc-print-var-docstring current-symbol) | ||
| 195 | (eldoc-print-fnsym-args current-fnsym))))))) | ||
| 196 | |||
| 197 | |||
| 198 | (defun eldoc-print-var-docstring (&optional sym) | ||
| 199 | "Print the brief (one-line) documentation string for the variable at point. | ||
| 200 | If called with no argument, print the first line of the variable | ||
| 201 | documentation string for the symbol at point in the echo area. | ||
| 202 | If called with a symbol, print the line for that symbol. | ||
| 203 | |||
| 204 | If the entire line cannot fit in the echo area, the variable name may be | ||
| 205 | truncated or eliminated entirely from the output to make room. | ||
| 206 | Any leading `*' in the docstring (which indicates the variable is a user | ||
| 207 | option) is not printed." | ||
| 208 | (interactive) | ||
| 209 | (let* ((s (or sym (eldoc-current-symbol))) | ||
| 210 | (name (symbol-name s)) | ||
| 211 | (doc (and s (documentation-property s 'variable-documentation t)))) | ||
| 212 | (and doc | ||
| 213 | (save-match-data | ||
| 214 | (and (string-match "\n" doc) | ||
| 215 | (setq doc (substring doc 0 (match-beginning 0)))) | ||
| 216 | (and (string-match "^\\*" doc) | ||
| 217 | (setq doc (substring doc 1))) | ||
| 218 | (let* ((doclen (+ (length name) (length ": ") (length doc))) | ||
| 219 | ;; Subtract 1 from window width since emacs seems not to | ||
| 220 | ;; write any chars to the last column, at least for some | ||
| 221 | ;; terminal types. | ||
| 222 | (strip (- doclen (1- (window-width (minibuffer-window)))))) | ||
| 223 | (cond ((> strip 0) | ||
| 224 | (let* ((len (length name))) | ||
| 225 | (cond ((>= strip len) | ||
| 226 | (message "%s" doc)) | ||
| 227 | (t | ||
| 228 | (setq name (substring name 0 (- len strip))) | ||
| 229 | (message "%s: %s" name doc))))) | ||
| 230 | (t | ||
| 231 | (message "%s: %s" s doc)))) | ||
| 232 | t)))) | ||
| 233 | |||
| 234 | |||
| 235 | ;;;###autoload | ||
| 236 | (defun eldoc-print-fnsym-args (&optional symbol) | ||
| 237 | "*Show the defined parameters for the function near point. | ||
| 238 | For the function at the beginning of the sexp which point is within, show | ||
| 239 | the defined parameters for the function in the echo area. | ||
| 240 | This information is extracted directly from the function or macro if it is | ||
| 241 | in pure lisp. | ||
| 242 | If the emacs function is a subr, the parameters are obtained from the | ||
| 243 | documentation string if possible." | ||
| 244 | (interactive) | ||
| 245 | (let ((sym (or symbol (eldoc-fnsym-in-current-sexp))) | ||
| 246 | (printit t) | ||
| 247 | (args nil)) | ||
| 248 | (cond ((not (and (symbolp sym) | ||
| 249 | (fboundp sym)))) | ||
| 250 | ((eq sym (car eldoc-last-data)) | ||
| 251 | (setq printit nil) | ||
| 252 | (setq args (cdr eldoc-last-data))) | ||
| 253 | ((subrp (eldoc-symbol-function sym)) | ||
| 254 | (setq args (eldoc-function-argstring-from-docstring sym)) | ||
| 255 | (setcdr eldoc-last-data args)) | ||
| 256 | (t | ||
| 257 | (setq args (eldoc-function-argstring sym)) | ||
| 258 | (setcdr eldoc-last-data args))) | ||
| 259 | (and args | ||
| 260 | printit | ||
| 261 | ;; In emacs 19.29 and later, all messages are recorded in a log. | ||
| 262 | ;; Do not put eldoc messages in the log since they are Legion. | ||
| 263 | (let ((message-log-max nil)) | ||
| 264 | (message "%s: %s" sym args))))) | ||
| 265 | |||
| 266 | (defun eldoc-fnsym-in-current-sexp () | ||
| 267 | (let* ((p (point)) | ||
| 268 | (sym (progn | ||
| 269 | (while (and (eldoc-forward-sexp-safe -1) | ||
| 270 | (> (point) (point-min)))) | ||
| 271 | (cond ((or (= (point) (point-min)) | ||
| 272 | (memq (or (char-after (point)) 0) | ||
| 273 | '(?\( ?\")) | ||
| 274 | ;; If we hit a quotation mark before a paren, we | ||
| 275 | ;; are inside a specific string, not a list of | ||
| 276 | ;; symbols. | ||
| 277 | (eq (or (char-after (1- (point))) 0) ?\")) | ||
| 278 | nil) | ||
| 279 | (t (condition-case nil | ||
| 280 | (read (current-buffer)) | ||
| 281 | (error nil))))))) | ||
| 282 | (goto-char p) | ||
| 283 | (and (symbolp sym) | ||
| 284 | sym))) | ||
| 285 | |||
| 286 | (defun eldoc-function-argstring (fn) | ||
| 287 | (let* ((def (eldoc-symbol-function fn)) | ||
| 288 | (arglist (cond ((null def) nil) | ||
| 289 | ((compiled-function-p def) | ||
| 290 | (if (fboundp 'compiled-function-arglist) | ||
| 291 | (funcall 'compiled-function-arglist def) | ||
| 292 | (car (append def nil)))) | ||
| 293 | ((eq (car-safe def) 'lambda) | ||
| 294 | (nth 1 def)) | ||
| 295 | (t t)))) | ||
| 296 | (eldoc-function-argstring-format arglist))) | ||
| 297 | |||
| 298 | |||
| 299 | (defun eldoc-function-argstring-from-docstring (fn) | ||
| 300 | (let ((docstring (documentation fn 'raw)) | ||
| 301 | (doc nil) | ||
| 302 | (doclist nil) | ||
| 303 | (end nil)) | ||
| 304 | (save-match-data | ||
| 305 | (cond | ||
| 306 | ;; Try first searching for args starting with symbol name. | ||
| 307 | ;; This is to avoid matching parenthetical remarks in e.g. sit-for. | ||
| 308 | ((string-match (format "^(%s[^\n)]*)$" fn) docstring) | ||
| 309 | ;; end does not include trailing ")" sequence. | ||
| 310 | (setq end (- (match-end 0) 1)) | ||
| 311 | (if (string-match " +" docstring (match-beginning 0)) | ||
| 312 | (setq doc (substring docstring (match-end 0) end)) | ||
| 313 | (setq doc ""))) | ||
| 314 | |||
| 315 | ;; Try again not requiring this symbol name in the docstring. | ||
| 316 | ;; This will be the case when looking up aliases. | ||
| 317 | ((string-match (format "^([^\n)]+)$" fn) docstring) | ||
| 318 | ;; end does not include trailing ")" sequence. | ||
| 319 | (setq end (- (match-end 0) 1)) | ||
| 320 | (if (string-match " +" docstring (match-beginning 0)) | ||
| 321 | (setq doc (substring docstring (match-end 0) end)) | ||
| 322 | (setq doc ""))) | ||
| 323 | |||
| 324 | ;; Emacs subr docstring style: | ||
| 325 | ;; (fn arg1 arg2 ...): description... | ||
| 326 | ((string-match "^([^\n)]+):" docstring) | ||
| 327 | ;; end does not include trailing "):" sequence. | ||
| 328 | (setq end (- (match-end 0) 2)) | ||
| 329 | (if (string-match " +" docstring (match-beginning 0)) | ||
| 330 | (setq doc (substring docstring (match-end 0) end)) | ||
| 331 | (setq doc ""))) | ||
| 332 | |||
| 333 | ;; XEmacs subr docstring style: | ||
| 334 | ;; "arguments: (arg1 arg2 ...) | ||
| 335 | ((string-match "^arguments: (\\([^\n)]+\\))" docstring) | ||
| 336 | ;; Also, skip leading paren, but the first word is actually an | ||
| 337 | ;; argument, not the function name. | ||
| 338 | (setq doc (substring docstring | ||
| 339 | (match-beginning 1) | ||
| 340 | (match-end 1)))) | ||
| 341 | |||
| 342 | ;; Some subrs have examples of usage, but they are indented. | ||
| 343 | ;; Actually, `setq-default' may be the only one. | ||
| 344 | ((string-match (format "^[ \t]+\\((%s[^\n)]*)\\)$" fn) docstring) | ||
| 345 | ;; end does not include trailing ")" sequence. | ||
| 346 | (setq end (- (match-end 1) 1)) | ||
| 347 | (if (string-match " +" docstring (match-beginning 1)) | ||
| 348 | (setq doc (substring docstring (match-end 0) end)) | ||
| 349 | (setq doc "")))) | ||
| 350 | |||
| 351 | (cond ((not (stringp doc)) | ||
| 352 | nil) | ||
| 353 | ((string-match "&" doc) | ||
| 354 | (let ((p 0) | ||
| 355 | (l (length doc))) | ||
| 356 | (while (< p l) | ||
| 357 | (cond ((string-match "[ \t\n]+" doc p) | ||
| 358 | (setq doclist | ||
| 359 | (cons (substring doc p (match-beginning 0)) | ||
| 360 | doclist)) | ||
| 361 | (setq p (match-end 0))) | ||
| 362 | (t | ||
| 363 | (setq doclist (cons (substring doc p) doclist)) | ||
| 364 | (setq p l)))) | ||
| 365 | (eldoc-function-argstring-format (nreverse doclist)))) | ||
| 366 | (t | ||
| 367 | (concat "(" (funcall eldoc-argument-case doc) ")")))))) | ||
| 368 | |||
| 369 | (defun eldoc-function-argstring-format (arglist) | ||
| 370 | (cond ((not (listp arglist)) | ||
| 371 | (setq arglist nil)) | ||
| 372 | ((symbolp (car arglist)) | ||
| 373 | (setq arglist | ||
| 374 | (mapcar (function (lambda (s) | ||
| 375 | (if (memq s '(&optional &rest)) | ||
| 376 | (symbol-name s) | ||
| 377 | (funcall eldoc-argument-case | ||
| 378 | (symbol-name s))))) | ||
| 379 | arglist))) | ||
| 380 | ((stringp (car arglist)) | ||
| 381 | (setq arglist | ||
| 382 | (mapcar (function (lambda (s) | ||
| 383 | (if (member s '("&optional" "&rest")) | ||
| 384 | s | ||
| 385 | (funcall eldoc-argument-case s)))) | ||
| 386 | arglist)))) | ||
| 387 | (concat "(" (mapconcat 'identity arglist " ") ")")) | ||
| 388 | |||
| 389 | |||
| 390 | ;; forward-sexp calls scan-sexps, which returns an error if it hits the | ||
| 391 | ;; beginning or end of the sexp. This returns nil instead. | ||
| 392 | (defun eldoc-forward-sexp-safe (&optional count) | ||
| 393 | "Move forward across one balanced expression (sexp). | ||
| 394 | With argument, do it that many times. Negative arg -COUNT means | ||
| 395 | move backward across COUNT balanced expressions. | ||
| 396 | Return distance in buffer moved, or nil." | ||
| 397 | (or count (setq count 1)) | ||
| 398 | (condition-case err | ||
| 399 | (- (- (point) (progn | ||
| 400 | (let ((parse-sexp-ignore-comments t)) | ||
| 401 | (forward-sexp count)) | ||
| 402 | (point)))) | ||
| 403 | (error nil))) | ||
| 404 | |||
| 405 | ;; Do indirect function resolution if possible. | ||
| 406 | (defun eldoc-symbol-function (fsym) | ||
| 407 | (let ((defn (and (fboundp fsym) | ||
| 408 | (symbol-function fsym)))) | ||
| 409 | (and (symbolp defn) | ||
| 410 | (condition-case err | ||
| 411 | (setq defn (indirect-function fsym)) | ||
| 412 | (error (setq defn nil)))) | ||
| 413 | defn)) | ||
| 414 | |||
| 415 | (defun eldoc-current-symbol () | ||
| 416 | (let ((c (char-after (point)))) | ||
| 417 | (and c | ||
| 418 | (memq (char-syntax c) '(?w ?_)) | ||
| 419 | (intern-soft (current-word))))) | ||
| 420 | |||
| 421 | (provide 'eldoc) | ||
| 422 | |||
| 423 | ;;; eldoc.el ends here | ||