-;;; eldoc.el --- show function arglist or variable docstring in echo area -*- lexical-binding: t; -*-
+;;; eldoc.el --- Show function arglist or variable docstring in echo area -*- lexical-binding:t; -*-
-;; Copyright (C) 1996-2014 Free Software Foundation, Inc.
+;; Copyright (C) 1996-2015 Free Software Foundation, Inc.
;; Author: Noah Friedman <friedman@splode.com>
;; Maintainer: friedman@splode.com
;;; Code:
-(require 'help-fns) ;For fundoc-usage handling functions.
-(require 'cl-lib)
-
(defgroup eldoc nil
"Show function arglist or variable docstring in echo area."
:group 'lisp
Actually, any name of a function which takes a string as an argument and
returns another string is acceptable.
-Note that if `eldoc-documentation-function' is non-nil, this variable
-has no effect, unless the function handles it explicitly."
+Note that this variable has no effect, unless
+`eldoc-documentation-function' handles it explicitly."
:type '(radio (function-item upcase)
(function-item downcase)
function)
:group 'eldoc)
-(make-obsolete-variable 'eldoc-argument-case nil "24.5")
+(make-obsolete-variable 'eldoc-argument-case nil "25.1")
(defcustom eldoc-echo-area-use-multiline-p 'truncate-sym-name-if-fit
"Allow long ElDoc messages to resize echo area display.
display in the echo area. Function or variable symbol name may be
truncated to make more of the arglist or documentation string visible.
-Note that if `eldoc-documentation-function' is non-nil, this variable
-has no effect, unless the function handles it explicitly."
+Note that this variable has no effect, unless
+`eldoc-documentation-function' handles it explicitly."
:type '(radio (const :tag "Always" t)
(const :tag "Never" nil)
(const :tag "Yes, but truncate symbol names if it will\
(defface eldoc-highlight-function-argument
'((t (:inherit bold)))
"Face used for the argument at point in a function's argument list.
-Note that if `eldoc-documentation-function' is non-nil, this face
-has no effect, unless the function handles it explicitly."
+Note that this face has no effect unless the `eldoc-documentation-function'
+handles it explicitly."
:group 'eldoc)
;;; No user options below here.
this file since the obarray is initialized at load time.
Remember to keep it a prime number to improve hash performance.")
-(defconst eldoc-message-commands
+(defvar eldoc-message-commands
+ ;; Don't define as `defconst' since it would then go to (read-only) purespace.
(make-vector eldoc-message-commands-table-size 0)
"Commands after which it is appropriate to print in the echo area.
ElDoc does not try to print function arglists, etc., after just any command,
directly. Instead, use `eldoc-add-command' and `eldoc-remove-command'.")
;; Not a constant.
-(defconst eldoc-last-data (make-vector 3 nil)
+(defvar eldoc-last-data (make-vector 3 nil)
+ ;; Don't define as `defconst' since it would then go to (read-only) purespace.
"Bookkeeping; elements are as follows:
0 - contains the last symbol read from the buffer.
1 - contains the string last displayed in the echo area for variables,
or argument string for functions.
- 2 - 'function if function args, 'variable if variable documentation.")
+ 2 - `function' if function args, `variable' if variable documentation.")
+(make-obsolete-variable 'eldoc-last-data "use your own instead" "25.1")
(defvar eldoc-last-message nil)
expression point is on."
:group 'eldoc :lighter eldoc-minor-mode-string
(setq eldoc-last-message nil)
- (if eldoc-mode
- (progn
- (when eldoc-print-after-edit
- (setq-local eldoc-message-commands (eldoc-edit-message-commands)))
- (add-hook 'post-command-hook 'eldoc-schedule-timer nil t)
- (add-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area nil t))
+ (cond
+ ((memq eldoc-documentation-function '(nil ignore))
+ (message "There is no ElDoc support in this buffer")
+ (setq eldoc-mode nil))
+ (eldoc-mode
+ (when eldoc-print-after-edit
+ (setq-local eldoc-message-commands (eldoc-edit-message-commands)))
+ (add-hook 'post-command-hook 'eldoc-schedule-timer nil t)
+ (add-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area nil t))
+ (t
(kill-local-variable 'eldoc-message-commands)
(remove-hook 'post-command-hook 'eldoc-schedule-timer t)
- (remove-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area t)))
+ (remove-hook 'pre-command-hook 'eldoc-pre-command-refresh-echo-area t))))
+
+;;;###autoload
+(define-minor-mode global-eldoc-mode
+ "Enable `eldoc-mode' in all buffers where it's applicable."
+ :group 'eldoc :global t
+ :initialize 'custom-initialize-delay
+ :init-value t
+ (setq eldoc-last-message nil)
+ (if global-eldoc-mode
+ (progn
+ (add-hook 'post-command-hook #'eldoc-schedule-timer)
+ (add-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area))
+ (remove-hook 'post-command-hook #'eldoc-schedule-timer)
+ (remove-hook 'pre-command-hook #'eldoc-pre-command-refresh-echo-area)))
;;;###autoload
(define-obsolete-function-alias 'turn-on-eldoc-mode 'eldoc-mode "24.4")
\f
(defun eldoc-schedule-timer ()
(or (and eldoc-timer
- (memq eldoc-timer timer-idle-list))
+ (memq eldoc-timer timer-idle-list)) ;FIXME: Why?
(setq eldoc-timer
(run-with-idle-timer
eldoc-idle-delay t
- (lambda () (and eldoc-mode (eldoc-print-current-symbol-info))))))
+ (lambda ()
+ (when (or eldoc-mode
+ (and global-eldoc-mode
+ (not (memq eldoc-documentation-function
+ '(nil ignore)))))
+ (eldoc-print-current-symbol-info))))))
;; If user has changed the idle delay, update the timer.
(cond ((not (= eldoc-idle-delay eldoc-current-idle-delay))
\f
;;;###autoload
-(defvar eldoc-documentation-function #'eldoc-documentation-function-default
+(defvar eldoc-documentation-function #'ignore
"Function to call to return doc string.
The function of no args should return a one-line string for displaying
doc about a function etc. appropriate to the context around point.
and the face `eldoc-highlight-function-argument', if they are to have any
effect.
-This variable is expected to be made buffer-local by modes (other than
-Emacs Lisp mode) that support ElDoc.")
+This variable is expected to be set buffer-locally by modes that support ElDoc.")
(defun eldoc-print-current-symbol-info ()
;; This is run from post-command-hook or some idle timer thing,
nil))
(eldoc-message (funcall eldoc-documentation-function)))))
-(defun eldoc-documentation-function-default ()
- "Default value for `eldoc-documentation-function' (which see)."
- (let ((current-symbol (eldoc-current-symbol))
- (current-fnsym (eldoc-fnsym-in-current-sexp)))
- (cond ((null current-fnsym)
- nil)
- ((eq current-symbol (car current-fnsym))
- (or (apply #'eldoc-get-fnsym-args-string current-fnsym)
- (eldoc-get-var-docstring current-symbol)))
- (t
- (or (eldoc-get-var-docstring current-symbol)
- (apply #'eldoc-get-fnsym-args-string current-fnsym))))))
-
-(defun eldoc-get-fnsym-args-string (sym &optional index)
- "Return a string containing the parameter list of the function SYM.
-If SYM is a subr and no arglist is obtainable from the docstring
-or elsewhere, return a 1-line docstring."
- (let (args doc advertised)
- (cond ((not (and sym (symbolp sym) (fboundp sym))))
- ((and (eq sym (aref eldoc-last-data 0))
- (eq 'function (aref eldoc-last-data 2)))
- (setq doc (aref eldoc-last-data 1)))
- ((listp (setq advertised (gethash (indirect-function sym)
- advertised-signature-table t)))
- (setq args advertised))
- ((setq doc (help-split-fundoc (documentation sym t) sym))
- (setq args (car doc)))
- (t
- (setq args (help-function-arglist sym))))
- (if args
- ;; Stringify, and store before highlighting, downcasing, etc.
- ;; FIXME should truncate before storing.
- (eldoc-last-data-store sym (setq args (eldoc-function-argstring args))
- 'function)
- (setq args doc)) ; use stored value
- ;; Change case, highlight, truncate.
- (if args
- (eldoc-highlight-function-argument sym args index))))
-
-(defun eldoc-highlight-function-argument (sym args index)
- "Highlight argument INDEX in ARGS list for function SYM.
-In the absence of INDEX, just call `eldoc-docstring-format-sym-doc'."
- (let ((start nil)
- (end 0)
- (argument-face 'eldoc-highlight-function-argument))
- ;; Find the current argument in the argument string. We need to
- ;; handle `&rest' and informal `...' properly.
- ;;
- ;; FIXME: What to do with optional arguments, like in
- ;; (defun NAME ARGLIST [DOCSTRING] BODY...) case?
- ;; The problem is there is no robust way to determine if
- ;; the current argument is indeed a docstring.
-
- ;; When `&key' is used finding position based on `index'
- ;; would be wrong, so find the arg at point and determine
- ;; position in ARGS based on this current arg.
- (when (string-match "&key" args)
- (let* (case-fold-search
- (cur-w (current-word))
- (limit (save-excursion
- (when (re-search-backward (symbol-name sym) nil t)
- (match-end 0))))
- (cur-a (if (string-match ":\\([^ ()]*\\)" cur-w)
- (substring cur-w 1)
- (save-excursion
- (when (re-search-backward ":\\([^ ()\n]*\\)" limit t)
- (match-string 1))))))
- ;; If `cur-a' is nil probably cursor is on a positional arg
- ;; before `&key', in this case, exit this block and determine
- ;; position with `index'.
- (when (and cur-a
- (string-match (concat "\\_<" (upcase cur-a) "\\_>") args))
- (setq index nil ; Skip next block based on positional args.
- start (match-beginning 0)
- end (match-end 0)))))
- ;; Handle now positional arguments.
- (while (and index (>= index 1))
- (if (string-match "[^ ()]+" args end)
- (progn
- (setq start (match-beginning 0)
- end (match-end 0))
- (let ((argument (match-string 0 args)))
- (cond ((string= argument "&rest")
- ;; All the rest arguments are the same.
- (setq index 1))
- ((string= argument "&optional")) ; Skip.
- ((string= argument "&allow-other-keys")) ; Skip.
- ;; Back to index 0 in ARG1 ARG2 ARG2 ARG3 etc...
- ;; like in `setq'.
- ((or (string-match-p "\\.\\.\\.$" argument)
- (and (string-match-p "\\.\\.\\.)?$" args)
- (> index 1) (cl-oddp index)))
- (setq index 0))
- (t
- (setq index (1- index))))))
- (setq end (length args)
- start (1- end)
- argument-face 'font-lock-warning-face
- index 0)))
- (let ((doc args))
- (when start
- (setq doc (copy-sequence args))
- (add-text-properties start end (list 'face argument-face) doc))
- (setq doc (eldoc-docstring-format-sym-doc
- sym doc (if (functionp sym) 'font-lock-function-name-face
- 'font-lock-keyword-face)))
- doc)))
-
-;; Return a string containing a brief (one-line) documentation string for
-;; the variable.
-(defun eldoc-get-var-docstring (sym)
- (when sym
- (cond ((and (eq sym (aref eldoc-last-data 0))
- (eq 'variable (aref eldoc-last-data 2)))
- (aref eldoc-last-data 1))
- (t
- (let ((doc (documentation-property sym 'variable-documentation t)))
- (cond (doc
- (setq doc (eldoc-docstring-format-sym-doc
- sym (eldoc-docstring-first-line doc)
- 'font-lock-variable-name-face))
- (eldoc-last-data-store sym doc 'variable)))
- doc)))))
-
-(defun eldoc-last-data-store (symbol doc type)
- (aset eldoc-last-data 0 symbol)
- (aset eldoc-last-data 1 doc)
- (aset eldoc-last-data 2 type))
-
-;; Note that any leading `*' in the docstring (which indicates the variable
-;; is a user option) is removed.
-(defun eldoc-docstring-first-line (doc)
- (and (stringp doc)
- (substitute-command-keys
- (save-match-data
- ;; Don't use "^" in the regexp below since it may match
- ;; anywhere in the doc-string.
- (let ((start (if (string-match "\\`\\*" doc) (match-end 0) 0)))
- (cond ((string-match "\n" doc)
- (substring doc start (match-beginning 0)))
- ((zerop start) doc)
- (t (substring doc start))))))))
-
-;; If the entire line cannot fit in the echo area, the symbol name may be
-;; truncated or eliminated entirely from the output to make room for the
-;; description.
-(defun eldoc-docstring-format-sym-doc (sym doc face)
- (save-match-data
- (let* ((name (symbol-name sym))
- (ea-multi eldoc-echo-area-use-multiline-p)
- ;; Subtract 1 from window width since emacs will not write
- ;; any chars to the last column, or in later versions, will
- ;; cause a wraparound and resize of the echo area.
- (ea-width (1- (window-width (minibuffer-window))))
- (strip (- (+ (length name) (length ": ") (length doc)) ea-width)))
- (cond ((or (<= strip 0)
- (eq ea-multi t)
- (and ea-multi (> (length doc) ea-width)))
- (format "%s: %s" (propertize name 'face face) doc))
- ((> (length doc) ea-width)
- (substring (format "%s" doc) 0 ea-width))
- ((>= strip (length name))
- (format "%s" doc))
- (t
- ;; Show the end of the partial symbol name, rather
- ;; than the beginning, since the former is more likely
- ;; to be unique given package namespace conventions.
- (setq name (substring name strip))
- (format "%s: %s" (propertize name 'face face) doc))))))
-
-\f
-;; Return a list of current function name and argument index.
-(defun eldoc-fnsym-in-current-sexp ()
- (save-excursion
- (let ((argument-index (1- (eldoc-beginning-of-sexp))))
- ;; If we are at the beginning of function name, this will be -1.
- (when (< argument-index 0)
- (setq argument-index 0))
- ;; Don't do anything if current word is inside a string.
- (if (= (or (char-after (1- (point))) 0) ?\")
- nil
- (list (eldoc-current-symbol) argument-index)))))
-
-;; Move to the beginning of current sexp. Return the number of nested
-;; sexp the point was over or after.
-(defun eldoc-beginning-of-sexp ()
- (let ((parse-sexp-ignore-comments t)
- (num-skipped-sexps 0))
- (condition-case _
- (progn
- ;; First account for the case the point is directly over a
- ;; beginning of a nested sexp.
- (condition-case _
- (let ((p (point)))
- (forward-sexp -1)
- (forward-sexp 1)
- (when (< (point) p)
- (setq num-skipped-sexps 1)))
- (error))
- (while
- (let ((p (point)))
- (forward-sexp -1)
- (when (< (point) p)
- (setq num-skipped-sexps (1+ num-skipped-sexps))))))
- (error))
- num-skipped-sexps))
-
-;; returns nil unless current word is an interned symbol.
-(defun eldoc-current-symbol ()
- (let ((c (char-after (point))))
- (and c
- (memq (char-syntax c) '(?w ?_))
- (intern-soft (current-word)))))
-
-;; Do indirect function resolution if possible.
-(defun eldoc-symbol-function (fsym)
- (let ((defn (symbol-function fsym)))
- (and (symbolp defn)
- (condition-case _
- (setq defn (indirect-function fsym))
- (error (setq defn nil))))
- defn))
-
-(defun eldoc-function-argstring (arglist)
- "Return ARGLIST as a string enclosed by ().
-ARGLIST is either a string, or a list of strings or symbols."
- (let ((str (cond ((stringp arglist) arglist)
- ((not (listp arglist)) nil)
- (t (format "%S" (help-make-usage 'toto arglist))))))
- (if (and str (string-match "\\`([^ ]+ ?" str))
- (replace-match "(" t t str)
- str)))
-
\f
;; When point is in a sexp, the function args are not reprinted in the echo
;; area after every possible interactive command because some of them print
(defun eldoc-add-command-completions (&rest names)
(dolist (name names)
- (apply 'eldoc-add-command (all-completions name obarray 'commandp))))
+ (apply #'eldoc-add-command (all-completions name obarray 'commandp))))
(defun eldoc-remove-command (&rest cmds)
(dolist (name cmds)
(defun eldoc-remove-command-completions (&rest names)
(dolist (name names)
- (apply 'eldoc-remove-command
+ (apply #'eldoc-remove-command
(all-completions name eldoc-message-commands))))
\f