A very simple but effective thing, eldoc-mode is a MinorMode which shows you, in the echo area, the argument list of the function call you are currently writing. Very handy. By NoahFriedman. Part of Emacs.
Project homepage on GNU ELPA: http://elpa.gnu.org/packages/eldoc.html
ElDoc works for EmacsLisp and certain other language modes that implement support for ElDoc. These include
ElDoc runs each element of the function arglist through a filter function stored in the eldoc-argument-case variable, which defaults to upcase. This is why the arguments are capitalized by default. [This only applies for Emacs Lisp unless a custom eldoc-documentation-function explicitly uses it.] But wouldn’t it be cool if these arguments were highlighted in font-lock-variable-name-face? Just do something like this:
(defun ted-frob-eldoc-argument-list (string) "Upcase and fontify STRING for use with `eldoc-mode'." (propertize (upcase string) 'face 'font-lock-variable-name-face)) (setq eldoc-argument-case 'ted-frob-eldoc-argument-list)
(You need Emacs 21 or later for function ‘propertize’
. For Emacs 20, use ‘put-text-property’
instead.)
One useful way to enable this minor mode is to put the following in your .emacs:
(add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode) (add-hook 'lisp-interaction-mode-hook 'turn-on-eldoc-mode) (add-hook 'ielm-mode-hook 'turn-on-eldoc-mode)
If you are using Emacs 24.4 or later, use eldoc-mode instead:
(add-hook 'emacs-lisp-mode-hook 'eldoc-mode) (add-hook 'lisp-interaction-mode-hook 'eldoc-mode) (add-hook 'ielm-mode-hook 'eldoc-mode)
Your mode need only supply 1 function to support eldoc-mode which you tell ElDoc about like this.
[You should use introspection where the language system supports it. See the Emacs 22 Python mode for an example (or Haskell mode and Erlang mode with Distel, for which Eldoc was generalized).]
(set (make-local-variable 'eldoc-documentation-function) 'tal-eldoc-function)
The function you point to need not be complex.
(defun tal-eldoc-function () "Returns a doc string appropriate for the current context, or nil." (symbol-value (intern-soft (upcase (thing-at-point 'symbol)) tal-eldoc-obarray)))
For the above, doc strings are stored in an obarray created like this.
(setq tal-eldoc-obarray (make-vector 41 0)) ... ;; Most of these set commands are actually stored in a file ;; and loaded for quick access to library function help (set (intern "STEPMOM" tal-eldoc-obarray) "PROC STEPMOM (CRTPID)") (set (intern "STOP" tal-eldoc-obarray) "PROC STOP (CRTPID, STOPBACKUP, PERROR, COMPLCODE, TERMINFO, SSID, LENGTH, TEXT)") (set (intern "STRING_UPSHIFT_" tal-eldoc-obarray) "INT PROC STRING_UPSHIFT_ (IN_STRING:LENGTH^A, OUT_STRING:LENGTH^B)") (set (intern "SUSPENDPROCESS" tal-eldoc-obarray) "PROC SUSPENDPROCESS (CRTPID)") ... ;; help for current module functions are created dynamically ;; by a fairly simple function.
You can look at TalMode to see one method of creating and maintaining such doc strings.
(defun eldoc-get-arg-index () (save-excursion (let ((fn (eldoc-fnsym-in-current-sexp)) (i 0)) (unless (memq (char-syntax (char-before)) '(32 39)) ; ? , ?' (condition-case err (backward-sexp) ;for safety (error 1))) (condition-case err (while (not (equal fn (eldoc-current-symbol))) (setq i (1+ i)) (backward-sexp)) (error 1)) (max 0 i)))) (defun eldoc-highlight-nth-arg (doc n) (cond ((null doc) "") ((<= n 0) doc) (t (let ((i 0)) (mapconcat (lambda (arg) (if (member arg '("&optional" "&rest")) arg (prog2 (if (= i n) (put-text-property 0 (length arg) 'face 'underline arg)) arg (setq i (1+ i))))) (split-string doc) " "))))) (defadvice eldoc-get-fnsym-args-string (around highlight activate) "" (setq ad-return-value (eldoc-highlight-nth-arg ad-do-it (eldoc-get-arg-index))))
I roughly implemented. – rubikitch
EmacsFromCVS now contains something like that. – nschum
Here’s an example of highlighting the param in a function help string corresponding to the param near to point.
loc := dnumout( output_buf, counter, 10, cntr_width, no_flags );
^
With point near cntr_width the help string looks like this.
All the comments make the function look bigger than it is.
(defun tal-eldoc-function () "Returns a documentation string appropriate for the current context or nil." (let ((begin (point)) (count -1) (word)) ;; if thing-at-point has a doc string, return it, we're done. (if (and (setq word (thing-at-point 'symbol)) (setq word (symbol-value (intern-soft (upcase word) tal-eldoc-obarray)))) word ;; Otherwise see if we're within (parens) and if so, count the ;; number of commas between point and the open paren. (save-excursion (when (and (condition-case () (or (backward-up-list) t) (error nil)) (eq ?\( (char-after))) (skip-syntax-backward "-") (if (setq word (thing-at-point 'symbol)) (while (progn (setq count (1+ count)) (search-forward "," begin t)))))) ;; if the word prior to the open paren has a doc string use it. (when (and word (setq word (copy-sequence (symbol-value (intern-soft (upcase word) tal-eldoc-obarray))))) ;; if the doc string has an open paren, highlight the word (if ;; any) that follows the same number of comma's counted above. (when (and (> count -1) (string-match "(" word ;; this tries to skip parens from int(32) type ;; proc/subproc declarations. (string-match ")\\s-\\(sub\\)?proc\\b" word))) (while (and (> count 0) (string-match "," word (match-end 0)) (setq count (1- count)))) (when (= count 0) (if (string-match "\\w+\\(:\\w+\\)?" word (match-end 0)) ;; found something needing to be made bold (put-text-property (match-beginning 0) (match-end 0) 'face 'bold word)))) word))))
I like to get a little documentation as well, so I add the first line of the doc string to ElDoc’s output
(defadvice eldoc-get-fnsym-args-string (after add-dacstring (sym) activate compile) "Add a doc string to ElDoc's modeline information." (let ((doc (eldoc-docstring-first-line (cdr (help-split-fundoc (documentation sym t) sym))))) (when (and doc (not (equal doc ""))) (setq ad-return-value (concat ad-return-value (if (> (+ (length ad-return-value) (length doc) 4) (frame-width)) "\n" " ") doc)))) ad-return-value)
– nschum
for Emacs version 24.4 use (elisp documentation only):
(with-eval-after-load 'elisp-mode (defadvice elisp--get-fnsym-args-string (after add-docstring activate compile) "Add a 1st line of docstring to ElDoc's function information." (when ad-return-value (let* ((doc (elisp--docstring-first-line (documentation (ad-get-arg 0) t))) (w (frame-width)) (color-doc (propertize doc 'face 'font-lock-comment-face))) (when (and doc (not (string= doc ""))) (setq ad-return-value (concat ad-return-value " " color-doc)) (when (> (length doc) w) (setq ad-return-value (substring ad-return-value 0 (1- w)))))) ad-return-value)))
– ukaszg
Good idea! I found it a bit hard to quickly scan to the docstring with so much information in the minibuffer area, so I tried to fix that by using different faces and changing the formatting a bit. I came up with this variation, which produces output like:
format (STRING &rest OBJECTS): Format a string out of a format-string
and arguments.
with the docstring fontified using font-lock-doc-face (the same one used for docstrings in emacs-lisp-mode).
Not quite finished: the code shrinking the string to fit on one line is a dumb copy-paste from eldoc-docstring-format-sym-doc. So if your minibuffer is not as wide as mine this may not work very well.
(defadvice eldoc-highlight-function-argument (around my-formatting (sym args index) compile activate preactivate) "Replace original to apply my style of formatting." ;; HACK: intercept the call to eldoc-docstring-format-sym-doc at the ;; end of the adviced function. This is obviously brittle, but the ;; alternative approach of copy/pasting the original also has ;; downsides... (flet ((eldoc-docstring-format-sym-doc (sym doc face) (let* ((function-name (propertize (symbol-name sym) 'face face)) (spec (format "%s %s" function-name doc)) (docstring (or (eldoc-docstring-first-line (documentation sym t)) "Undocumented.")) (docstring (propertize docstring 'face 'font-lock-doc-face)) ;; TODO: currently it strips from the start of spec by ;; character instead of whole arguments at a time. (fulldoc (format "%s: %s" spec docstring)) (ea-width (1- (window-width (minibuffer-window))))) (cond ((or (<= (length fulldoc) ea-width) (eq eldoc-echo-area-use-multiline-p t) (and eldoc-echo-area-use-multiline-p (> (length docstring) ea-width))) fulldoc) ((> (length docstring) ea-width) (substring docstring 0 ea-width)) ((>= (- (length fulldoc) (length spec)) ea-width) docstring) (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 spec (substring spec (- (length fulldoc) ea-width))) (format "%s: %s" spec docstring)))))) ad-do-it))
A hack to make Eldoc show the argument list in the echo area only after you type SPC. (Like in SlimeMode.)
(setq eldoc-documentation-function (lambda () (when (eql last-command-event 32) (let (eldoc-documentation-function) (eldoc-print-current-symbol-info)))))
The default eldoc output for functions is a little lacking for new programmers. I wrote this to provide context help on the function or variable under the cursor. You can toggle it on or off - change key binding to suit…
Update : 25 Feb 2020 - Now using the (derived) code from https://emacs.stackexchange.com/a/22139 by suggestion of rileyrg
Update : 21 April 2021 - Implemented a standalone minor mode separate from eldoc for popup docstring at point : https://github.com/rileyrg/el-docstring-sap rileyg
(define-minor-mode my-contextual-help-mode "Show help for the elisp symbol at point in the current *Help* buffer. Advises `eldoc-print-current-symbol-info'." :lighter " C-h" :global t (require 'help-mode) ;; for `help-xref-interned' (when (eq this-command 'my-contextual-help-mode) (message "Contextual help is %s" (if my-contextual-help-mode "on" "off"))) (and my-contextual-help-mode (eldoc-mode 1) (if (fboundp 'eldoc-current-symbol) (eldoc-current-symbol) (elisp--current-symbol)) (my-contextual-help :force))) (defadvice eldoc-print-current-symbol-info (before my-contextual-help activate) "Triggers contextual elisp *Help*. Enabled by `my-contextual-help-mode'." (and my-contextual-help-mode (derived-mode-p 'emacs-lisp-mode) (my-contextual-help))) (defvar-local my-contextual-help-last-symbol nil ;; Using a buffer-local variable for this means that we can't ;; trigger changes to the help buffer simply by switching windows, ;; which seems generally preferable to the alternative. "The last symbol processed by `my-contextual-help' in this buffer.") (defun my-contextual-help (&optional force) "Describe function, variable, or face at point, if *Help* buffer is visible." (let ((help-visible-p (get-buffer-window (help-buffer)))) (when (or help-visible-p force) (let ((sym (if (fboundp 'eldoc-current-symbol) (eldoc-current-symbol) (elisp--current-symbol)))) ;; We ignore keyword symbols, as their help is redundant. ;; If something else changes the help buffer contents, ensure we ;; don't immediately revert back to the current symbol's help. (and (not (keywordp sym)) (or (not (eq sym my-contextual-help-last-symbol)) (and force (not help-visible-p))) (setq my-contextual-help-last-symbol sym) sym (save-selected-window (help-xref-interned sym))))))) (defun my-contextual-help-toggle () "Intelligently enable or disable `my-contextual-help-mode'." (interactive) (if (get-buffer-window (help-buffer)) (my-contextual-help-mode 'toggle) (my-contextual-help-mode 1))) (my-contextual-help-mode 1) (global-set-key (kbd "C-c h") #'my-contextual-help-toggle)
Fetches function documentation from the net on demand:
(require 'xml) (setq my-php-function-doc-hash (make-hash-table :test 'equal)) (defun my-php-fetch-function-doc (function) (let ((doc (gethash function my-php-function-doc-hash 'nope))) (when (eq doc 'nope) (setq doc nil) (let ((buf (url-retrieve-synchronously (concat "http://php.net/manual-lookup.php?pattern=" function)))) (with-current-buffer buf (goto-char (point-min)) (let (desc) (when (re-search-forward "<div class=\"methodsynopsis dc-description\">\\(\\(.\\|\n\\)*?\\)</div>" nil t) (setq desc (replace-regexp-in-string " +" " " (replace-regexp-in-string "\n" "" (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1))))) (when (re-search-forward "<p class=\"para rdfs-comment\">\\(\\(.\\|\n\\)*?\\)</p>" nil t) (setq desc (concat desc "\n\n" (replace-regexp-in-string " +" " " (replace-regexp-in-string "\n" "" (replace-regexp-in-string "<.*?>" "" (match-string-no-properties 1)))))))) (if desc (setq doc (xml-substitute-special desc))))) (kill-buffer buf)) (puthash function doc my-php-function-doc-hash)) doc)) (defun my-php-eldoc-function () (let ((symbol (thing-at-point 'symbol))) (if (and symbol (not (eq (elt symbol 0) ?$))) (my-php-fetch-function-doc symbol))))
https://github.com/zenozeng/php-eldoc
https://github.com/zenozeng/css-eldoc
Eldoc support for Python using Rope extension. Allow highlighting of current position in argument list, integration with flyspell(properly show errors in minibuffer). Advice for message function can be avoided I it is possible to switch off Pymacs traces in minibuffer(this advice just don’t execute real message function in case of calling rope, because some identifiers in source code cause Rope traces).
(defun rope-eldoc-function () (interactive) (let* ((win-conf (current-window-configuration)) (resize-mini-windows nil) (disable-python-trace t) class fun args result-type (flymake-message (python-flymake-show-help)) (initial-point (point)) (paren-range (let (tmp) (ignore-errors (setq tmp (vimpulse-paren-range 0 ?\( nil t)) (if (and tmp (>= (point) (car tmp)) (<= (point) (cadr tmp))) tmp nil)))) (result (save-excursion ;; check if we on the border of args list - lparen or rparen (if paren-range (goto-char (car paren-range))) (call-interactively 'rope-show-doc) (set-buffer "*rope-pydoc*") (goto-char (point-min)) (if (or (equal (point-max) 1) (not (re-search-forward "\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)(.*):" (point-at-eol) t)) (and (current-message) (string-match-p "BadIdentifierError" (current-message)))) nil (let (result) ;; check if this is class definition (if (looking-at "class \\([a-zA-Z_]+[a-zA-Z0-9_]*\\)(.*):") (progn (goto-char (point-at-eol)) (re-search-forward (buffer-substring (match-beginning 1) (match-end 1))))) (goto-char (point-at-bol)) (setq result (buffer-substring (point) (point-at-eol))) ;; check if exist better description of function (goto-char (point-at-eol)) (string-match "\\([a-zA-Z_]+[a-zA-Z0-9_]*\\)(.*)" result) ;get function name (if (re-search-forward (concat (match-string 1 result) "(.*)") nil t) (progn (goto-char (point-at-bol)) (setq result (buffer-substring (point) (point-at-eol))))) ;; return result result )))) (arg-position (save-excursion (if paren-range (count-matches "," (car paren-range) (point)))))) ;; save window configuration (set-window-configuration win-conf) ;; process main result (if result (progn (setq result-type (nth 1 (split-string result "->"))) (setq result (nth 0 (split-string result "->"))) (setq result (split-string result "(")) (setq fun (nth 1 (split-string (nth 0 result) "\\."))) (setq class (nth 0 (split-string (nth 0 result) "\\."))) ;; process args - highlight current function argument (setq args (nth 0 (split-string (nth 1 result) ")"))) ;; highlight current argument (if args (progn (setq args (split-string args ",")) (setq args (let ((num -1)) (mapconcat (lambda(x)(progn (setq num (+ 1 num)) (if (equal num arg-position) (propertize x 'face 'eldoc-highlight-function-argument) x))) args ","))))) ;; create string for type signature (setq result (concat (propertize "Signature: " 'face 'flymake-message-face) (if fun (concat (propertize (org-trim class) 'face 'font-lock-type-face) "." (propertize (org-trim fun) 'face 'font-lock-function-name-face)) (propertize (org-trim class) 'face 'font-lock-function-name-face)) " (" args ")" (if result-type (concat " -> " (org-trim result-type))) )))) ;; create final result (if (and (null flymake-message) (null result)) nil (concat flymake-message (if (and result flymake-message) "\n") result)))) (defvar disable-python-trace nil) (defadvice message(around message-disable-python-trace activate) (if disable-python-trace t ad-do-it)) (defface flymake-message-face '((((class color) (background light)) (:foreground "#b2dfff")) (((class color) (background dark)) (:foreground "#b2dfff"))) "Flymake message face") (defun python-flymake-show-help () (when (get-char-property (point) 'flymake-overlay) (let ((help (get-char-property (point) 'help-echo))) (if help (format (concat (propertize "Error: " 'face 'flymake-message-face) "%s") help)))))
For using this function you should add following code to python-mode-hook:
(set (make-local-variable 'eldoc-documentation-function) 'rope-eldoc-function)
See the answer on the question Help Buffer on Hover Possible?: https://emacs.stackexchange.com/a/22139/12999