├── README.md └── eldoc-eval.el /README.md: -------------------------------------------------------------------------------- 1 | Eldoc-eval 2 | ========== 3 | 4 | 5 | ## Description 6 | Enable eldoc support when minibuffer is in use. 7 | 8 | # Commentary: 9 | 10 | This package enables eldoc support when minibuffer is in use. 11 | 12 | Eldoc info is shown by default in mode-line, 13 | but you can have eldoc info somewhere else by setting 14 | `eldoc-in-minibuffer-show-fn` to another function (e.g `tooltip-show`). 15 | 16 | By default with this package `M-:` will use `pp-eval-expression` 17 | instead of `eval-expression`; you can change that by setting 18 | `eval-preferred-function` to something else. 19 | 20 | It also provides a convenient macro to enable eldoc support 21 | in your own functions using minibuffer or in your defadvices, 22 | that is `with-eldoc-in-minibuffer`, e.g: 23 | 24 | 25 | defadvice edebug-eval-expression (around with-eldoc activate) 26 | "This advice enable eldoc support." 27 | (interactive (list (with-eldoc-in-minibuffer 28 | (read-from-minibuffer 29 | "Eval: " nil read-expression-map t 30 | 'read-expression-history)))) 31 | ad-do-it) 32 | 33 | 34 | Users of own minibuffer frame will have to set 35 | `eldoc-in-minibuffer-own-frame-p` to non-nil. 36 | 37 | You can turn On/Off eldoc support in minibuffer at any time 38 | with `eldoc-in-minibuffer-mode`. 39 | 40 | # Install: 41 | 42 | Add to your init file: 43 | 44 | If installed from git, add eldoc-eval.el to `load-path` and 45 | 46 | (autoload 'eldoc-in-minibuffer-mode "eldoc-eval") 47 | (eldoc-in-minibuffer-mode 1) 48 | 49 | Otherwise only `(eldoc-in-minibuffer-mode 1)` is needed. 50 | -------------------------------------------------------------------------------- /eldoc-eval.el: -------------------------------------------------------------------------------- 1 | ;;; eldoc-eval.el --- Enable eldoc support when minibuffer is in use. -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2011-2022 Free Software Foundation, Inc. 4 | 5 | ;; Author: Thierry Volpiatto 6 | ;; Version: 0.2 7 | 8 | ;; This file is part of GNU Emacs. 9 | 10 | ;; GNU Emacs is free software: you can redistribute it and/or modify 11 | ;; it under the terms of the GNU General Public License as published by 12 | ;; the Free Software Foundation, either version 3 of the License, or 13 | ;; (at your option) any later version. 14 | 15 | ;; GNU Emacs is distributed in the hope that it will be useful, 16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | ;; GNU General Public License for more details. 19 | 20 | ;; You should have received a copy of the GNU General Public License 21 | ;; along with GNU Emacs. If not, see . 22 | 23 | ;;; Commentary: 24 | ;; 25 | ;; This package enables eldoc support when minibuffer is in use. 26 | ;; 27 | ;; Eldoc info is shown by default in mode-line, 28 | ;; but you can have eldoc info somewhere else by setting 29 | ;; `eldoc-in-minibuffer-show-fn' to another function (e.g `tooltip-show'). 30 | ;; 31 | ;; By default with this package `M-:' will use `pp-eval-expression' 32 | ;; instead of `eval-expression'; you can change that by setting 33 | ;; `eldoc-eval-preferred-function'. 34 | ;; 35 | ;; It also provides a convenient macro to enable eldoc support 36 | ;; in your own functions using minibuffer or in your defadvices, 37 | ;; that is `with-eldoc-in-minibuffer'. 38 | ;; 39 | ;; Users of own minibuffer frame will have to set 40 | ;; `eldoc-in-minibuffer-own-frame-p' to non-nil. 41 | ;; 42 | ;; You can turn On/Off eldoc support in minibuffer any time 43 | ;; with `eldoc-in-minibuffer-mode'. 44 | ;; 45 | ;;; Install: 46 | ;; Add to .emacs: 47 | ;; 48 | ;; (autoload 'eldoc-in-minibuffer-mode "eldoc-eval") 49 | ;; (eldoc-in-minibuffer-mode 1) 50 | 51 | 52 | ;;; Code: 53 | (require 'eldoc) 54 | (eval-and-compile 55 | (when (require 'elisp-mode nil t) ; emacs-25 56 | (defalias 'eldoc-current-symbol 'elisp--current-symbol) 57 | (defalias 'eldoc-fnsym-in-current-sexp 'elisp--fnsym-in-current-sexp) 58 | (defalias 'eldoc-get-fnsym-args-string 'elisp-get-fnsym-args-string) 59 | (defalias 'eldoc-get-var-docstring 'elisp-get-var-docstring))) 60 | 61 | (defvar composition-function-table) 62 | 63 | ;;; Minibuffer support. 64 | ;; Enable displaying eldoc info in something else 65 | ;; Than minibuffer when this one is in use. 66 | ;; 67 | (defgroup eldoc-eval nil 68 | "Show eldoc infos in mode line while minibuffer is in use." 69 | :group 'eldoc) 70 | 71 | (defcustom eldoc-in-minibuffer-show-fn 'eldoc-show-in-mode-line 72 | "A function to display eldoc info. 73 | Should take one arg: the string to display" 74 | :type 'function) 75 | 76 | (defcustom eldoc-show-in-mode-line-delay 12 77 | "The time we show eldoc when Emacs is idle." 78 | :type 'number) 79 | 80 | (defcustom eldoc-eval-preferred-function 'pp-eval-expression 81 | "Preferred function to use with `M-:'." 82 | :type 'function) 83 | 84 | (defcustom eldoc-in-minibuffer-own-frame-p nil 85 | "Whether minibuffer has its own frame or not." 86 | :type 'boolean) 87 | 88 | (defcustom eldoc-in-minibuffer-mode-lighter " Eldoc-eval" 89 | "String displayed in mode-line when `eldoc-in-minibuffer-mode' is enabled." 90 | :type 'string) 91 | 92 | (defcustom eldoc-mode-line-stop-rolling-on-input t 93 | "When rolling mode-line is enabled, stop rolling on input when non--nil." 94 | :type 'boolean) 95 | 96 | ;;; Compatibility with Emacs-24.4 97 | ;; New implementation of eldoc in minibuffer that come 98 | ;; with Emacs-24.4 show the eldoc info of current-buffer while 99 | ;; minibuffer is in use, disable this and inline old Emacs behavior. 100 | ;; 101 | (defconst eldoc-eval--old-message-function 102 | (and (boundp 'eldoc-message-function) eldoc-message-function)) 103 | 104 | ;; Internal. 105 | (defvar eldoc-active-minibuffers-list nil 106 | "List of active minibuffers with eldoc enabled.") 107 | (defvar eldoc-mode-line-rolling-flag nil) 108 | 109 | (defvar eldoc-in-minibuffer-mode-map 110 | (let ((map (make-sparse-keymap))) 111 | (define-key map [remap eval-expression] 'eldoc-eval-expression) 112 | map)) 113 | 114 | ;;;###autoload 115 | (define-minor-mode eldoc-in-minibuffer-mode 116 | "Show eldoc for current minibuffer input." 117 | :global t 118 | :group 'eldoc-eval 119 | (if eldoc-in-minibuffer-mode 120 | (progn 121 | (add-hook 'minibuffer-exit-hook 122 | (lambda () 123 | (setq eldoc-mode-line-rolling-flag nil))) 124 | (when (boundp 'eldoc-post-insert-mode) 125 | (setq eldoc-message-function 'message) 126 | (remove-hook 'eval-expression-minibuffer-setup-hook 127 | 'eldoc-post-insert-mode)) 128 | (define-key minibuffer-local-map (kbd "C-@") 129 | 'eldoc-mode-line-toggle-rolling) 130 | (setq eldoc-minor-mode-string eldoc-in-minibuffer-mode-lighter)) 131 | (setq eldoc-minor-mode-string " Eldoc") 132 | (when (boundp 'eldoc-post-insert-mode) 133 | (setq eldoc-message-function eldoc-eval--old-message-function) 134 | (add-hook 'eval-expression-minibuffer-setup-hook 135 | 'eldoc-post-insert-mode)) 136 | (define-key minibuffer-local-map (kbd "C-@") 'set-mark-command))) 137 | 138 | (defadvice eldoc-display-message-no-interference-p 139 | (after eldoc-eval activate) 140 | (when eldoc-in-minibuffer-mode 141 | (setq ad-return-value 142 | (and ad-return-value 143 | ;; Having this mode operate in an active minibuffer/echo area 144 | ;; causes interference with what's going on there. 145 | (not cursor-in-echo-area) 146 | (not (eq (selected-window) (minibuffer-window))))))) 147 | 148 | (defun eldoc-store-minibuffer () 149 | "Store minibuffer buffer name in `eldoc-active-minibuffers-list'. 150 | This function is called by each minibuffer started with eldoc support. 151 | See `with-eldoc-in-minibuffer'." 152 | (with-selected-window (minibuffer-window) 153 | (push (current-buffer) eldoc-active-minibuffers-list))) 154 | 155 | (defmacro with-eldoc-in-minibuffer (&rest body) 156 | "Enable eldoc support for minibuffer input that runs in BODY." 157 | (declare (indent 0) (debug t)) 158 | (let ((timer (make-symbol "eldoc-eval--timer"))) 159 | `(let ((,timer (and eldoc-in-minibuffer-mode 160 | (run-with-idle-timer 161 | eldoc-idle-delay 162 | 'repeat #'eldoc-run-in-minibuffer)))) 163 | (unwind-protect 164 | (minibuffer-with-setup-hook 165 | ;; When minibuffer is activated in body, store it. 166 | #'eldoc-store-minibuffer 167 | ,@body) 168 | (and ,timer (cancel-timer ,timer)) 169 | ;; Each time a minibuffer exits or aborts 170 | ;; its buffer is removed from stack, 171 | ;; assuming we can only exit the active minibuffer 172 | ;; on top of stack. 173 | (setq eldoc-active-minibuffers-list 174 | (cdr eldoc-active-minibuffers-list)))))) 175 | 176 | (defun eldoc-current-buffer () 177 | "Return the current buffer prior to activating the minibuffer." 178 | (with-selected-frame (last-nonminibuffer-frame) 179 | (window-buffer 180 | (cond (eldoc-in-minibuffer-own-frame-p 181 | (selected-window)) 182 | ((fboundp 'window-in-direction) 183 | (window-in-direction 184 | 'above (minibuffer-window))) 185 | (t (minibuffer-selected-window)))))) 186 | 187 | (defun eldoc-eval--get-string (str) 188 | ;; Avoid error when ligature-mode is enabled. The error comes from 189 | ;; composite.c. 190 | ;; (error "Attempt to shape unibyte text"). This happen when string 191 | ;; comes with [...] at end. 192 | (with-temp-buffer 193 | (let ((composition-function-table 194 | (default-value 'composition-function-table))) 195 | (when (stringp str) 196 | (insert str) 197 | (buffer-string))))) 198 | 199 | (defun eldoc-show-in-mode-line (input) 200 | "Display string STR in the mode-line next to minibuffer." 201 | (with-current-buffer (eldoc-current-buffer) 202 | (let* ((max (window-width (selected-window))) 203 | (str (and (stringp input) (concat " " input))) 204 | (len (length str)) 205 | (tmp-str str) 206 | (mode-line-format 207 | (or (eldoc-eval--get-string str) 208 | mode-line-format)) 209 | roll mode-line-in-non-selected-windows) 210 | (catch 'break 211 | (if (and (> len max) eldoc-mode-line-rolling-flag) 212 | (progn 213 | (while (setq roll (sit-for 0.3)) 214 | (setq tmp-str (substring tmp-str 2) 215 | mode-line-format (eldoc-eval--get-string 216 | (concat tmp-str " [<]" str))) 217 | (force-mode-line-update) 218 | (when (< (length tmp-str) 2) (setq tmp-str str))) 219 | (unless roll 220 | (when eldoc-mode-line-stop-rolling-on-input 221 | (setq eldoc-mode-line-rolling-flag nil)) 222 | (throw 'break nil))) 223 | (force-mode-line-update) 224 | (sit-for eldoc-show-in-mode-line-delay)))) 225 | (force-mode-line-update))) 226 | 227 | (defun eldoc-mode-line-toggle-rolling () 228 | (interactive) 229 | (if (and eldoc-in-minibuffer-mode 230 | (minibuffer-window-active-p (selected-window))) 231 | (setq eldoc-mode-line-rolling-flag (not eldoc-mode-line-rolling-flag)) 232 | (error "No active minibuffer found"))) 233 | 234 | (defun eldoc-run-in-minibuffer () 235 | (let ((buf (window-buffer (active-minibuffer-window)))) 236 | ;; If this minibuffer have been started with 237 | ;;`with-eldoc-in-minibuffer' give it eldoc support 238 | ;; and update mode-line, otherwise do nothing. 239 | (condition-case _err 240 | (when (member buf eldoc-active-minibuffers-list) 241 | (with-current-buffer buf 242 | (let* ((sym (save-excursion 243 | (unless (looking-back ")\\|\"" (1- (point))) 244 | (forward-char -1)) 245 | (eldoc-current-symbol))) 246 | (info-fn (eldoc-fnsym-in-current-sexp)) 247 | (doc (or (eldoc-get-var-docstring sym) 248 | (eldoc-get-fnsym-args-string 249 | (car info-fn) (cadr info-fn))))) 250 | (funcall eldoc-in-minibuffer-show-fn (or doc 1))))) 251 | (scan-error nil) 252 | (beginning-of-buffer nil)))) 253 | 254 | ;;;###autoload 255 | (defun eldoc-eval-expression () 256 | "Eval expression with eldoc support in mode-line." 257 | (interactive) 258 | (with-eldoc-in-minibuffer 259 | (call-interactively eldoc-eval-preferred-function))) 260 | 261 | 262 | (provide 'eldoc-eval) 263 | ;;; eldoc-eval.el ends here 264 | --------------------------------------------------------------------------------