]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/eldoc.el
* emacs-lisp/package.el (package-dir-info): New function.
[gnu-emacs] / lisp / emacs-lisp / eldoc.el
index 0b8304af29f857ac306b27d64a7641c0ebd688a5..0c74f3fedc01c63f7a89109daf745ba79296d3cb 100644 (file)
@@ -1,6 +1,6 @@
-;;; 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
@@ -47,9 +47,6 @@
 
 ;;; 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
@@ -82,13 +79,13 @@ This has two preferred values: `upcase' or `downcase'.
 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.
@@ -105,8 +102,8 @@ If value is nil, messages are always truncated to fit in a single line of
 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\
@@ -116,8 +113,8 @@ has no effect, unless the function handles it explicitly."
 (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.
@@ -129,7 +126,8 @@ choose to increase the number of buckets, you must do so before loading
 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,
@@ -140,12 +138,14 @@ This variable contains an obarray of symbols; do not manipulate it
 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)
 
@@ -185,15 +185,33 @@ it displays the argument list of the function called in the
 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")
@@ -201,11 +219,16 @@ expression point is on."
 \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))
@@ -300,7 +323,7 @@ Otherwise work like `message'."
 
 \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.
@@ -313,8 +336,7 @@ the variables `eldoc-argument-case' and `eldoc-echo-area-use-multiline-p',
 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,
@@ -327,239 +349,6 @@ Emacs Lisp mode) that support ElDoc.")
                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
@@ -575,7 +364,7 @@ ARGLIST is either a string, or a list of strings or symbols."
 
 (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)
@@ -585,7 +374,7 @@ ARGLIST is either a string, or a list of strings or symbols."
 
 (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