branch: elpa/eldoc-mouse
commit 60ded7eadb03dcfc916aa2f886d1e87c003585ba
Merge: d0193a64680 17bc2aea4cb
Author: huangfeiyu <[email protected]>
Commit: GitHub <[email protected]>

    Merge pull request #16 from huangfeiyu/dev
    
    Dev
---
 README.md      |  52 +++++++++++---
 eldoc-mouse.el | 222 ++++++++++++++++++++++++++-------------------------------
 2 files changed, 147 insertions(+), 127 deletions(-)

diff --git a/README.md b/README.md
index a7c38611137..094255734eb 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,20 @@
 
 # eldoc-mouse
 
-`eldoc-mouse` is an Emacs package that enhances the `eldoc` functionality by 
displaying documentation in a popup at the mouse point using 
[posframe](https://github.com/tumashu/posframe) when the mouse hovers over a 
symbol in an `eglot` managed buffer. It integrates with `posframe` to provide 
popping up documentation and features a debounced hover mechanism to prevent 
excessive requests to the LSP server.
+`eldoc-mouse` is an Emacs package that enhances the `eldoc` functionality by 
displaying documentation in a popup at the mouse point using 
[posframe](https://github.com/tumashu/posframe) when the mouse hovers over a 
symbol. It integrates with `posframe` to provide popping up documentation.
 [Eldoc-mouse Demo at Youtube](https://youtu.be/XFAc4WyiJjI)
 <video 
src="https://github.com/user-attachments/assets/5622cfd2-de0c-46e8-9276-d67615671932";
 controls></video>
 
 ## Features
-- Displays documentation in a popup when hovering over symbols in `eglot` 
managed buffers.
+- Displays documentation in a popup when hovering over symbols.
 - Integrates with `posframe` for popup documentation.
 - Support moving mouse to the popup by move mouse on it and click.
 - Automatically hide the popup when mouse moved away from the symbol, also 
supoort pressing `C-g` to hide the popup.
-- Avoids spamming the LSP server by debouncing hover events.
-- Works in eglot managed buffers to show documentation for the symbol under 
the mouse point.
-- Removed the unnecessary signatures from the document to make doucment more 
clear.
-- Still keep highlighting the symbol under the cursor.
+- The following enhancements are made for eglot managed buffers.
+   - Removed the unnecessary signatures from the document to make doucment 
more clear.
+   - Still keep highlighting the symbol under the cursor.
 - An interactive command to pop up document at cursor 
`eldoc-mouse-pop-doc-at-cursor`, this is helpful when you don't bother to move 
mouse, but want to check document. I bind it to key sequence `F1 F1`, press 
Ctrl-G or moving cursor away from the current symbol to close the popup.
+- More modes can be easily supported by extends `eldoc-mouse`.
 - So far, `eldoc-mouse` works only for GUI Emacs.
 
 ## Installation
@@ -31,12 +31,48 @@ You can install `eldoc-mouse` with the following command.
 Add the following in your Emacs configuration:
 ```
 ;; The following two lines are both optional, but you would like to add at 
least one of them to your Emacs configuration.
-(use-package eldoc-mouse :hook (eglot-managed-mode)) ;; enable mouse hover for 
eglot managed buffers.
+(use-package eldoc-mouse :hook (eglot-managed-mode emacs-lisp-mode)) ;; enable 
mouse hover for eglot managed buffers, and emacs lisp buffers.
 (global-set-key (kbd "<f1> <f1>") 'eldoc-mouse-pop-doc-at-cursor) ;; replace 
<f1> <f1> to a key you like. Displaying document on a popup when you press a 
key.
 ```
+### Supported modes
+* eglot-managed-mode
+* emacs-list-mode
 ## Customization
 
 You can customize the behavior of eldoc-mouse by adjusting the variables. For 
instance, you can adjust the delay time between mouse hover and displaying the 
documentation by changing the eldoc-mouse-mouse-timer settings.
+
+## Extend (Add new mode)
+`eldoc-mouse` can be easily extended to support more major/minor modes, finish 
the following 3 steps to extend it to support a new mode.
+
+1. write an impementation of `eldoc-documentation-functions`. see 
https://www.gnu.org/software/emacs/manual/html_node/emacs/Programming-Language-Doc.html#index-eldoc_002ddocumentation_002dfunctions.
 Here's an example implementation for `emacs-lisp-mode`
+   ```elisp
+    (defun eldoc-mouse--elisp-eldoc-documentation-function (_cb)
+      "The `eldoc-documentation-functions' implementation for elisp."
+      (if (eq major-mode 'emacs-lisp-mode)
+          (let ((sym (symbol-at-point)))
+            (cond
+             ;; If the symbol is a function
+             ((and sym (fboundp sym))
+              (documentation sym))
+             ;; If the symbol is a variable
+             ((and sym (boundp sym))
+              (let ((doc (documentation-property sym 'variable-documentation)))
+                (if doc
+                    doc
+                  nil)))
+             ;; If no symbol or not a function/variable
+             (t nil)))
+        nil)) ;; if the expected mode is not available, nil should be returned.
+   ```
+2. add the function name to the `eldoc-mouse` variable 
`eldoc-mouse--eldoc-documentation-functions`. for example:
+   ```elisp
+   (defvar eldoc-mouse--eldoc-documentation-functions
+      '(eldoc-mouse--eglot-eldoc-documentation-function
+        eldoc-mouse--elisp-eldoc-documentation-function)
+      "The `eldoc-documentation-functions' for `eldoc-mouse-mode'.")
+   ```
+3. submit a pull request. I'd love to merge it.
+
 ## Requirements
 
     Emacs 30.1 or higher
@@ -52,7 +88,7 @@ Contributing
 Feel free to open issues and pull requests for improvements. If you encounter 
any bugs or have feature requests, please create an issue on the GitHub Issues 
page.
 ## TODO 
 * make moving mouse to the posframe easier, currently, it requires moving 
mouse quickly and with a click.
-* make showing document for mouse hover more generic, not only for eglot 
managed buffers,  but also for buffers that it makes sense to show something on 
a posframe for mouse hover. (truly lives up to its name)
+* *(done)* make showing document for mouse hover more generic, not only for 
eglot managed buffers,  but also for buffers that it makes sense to show 
something on a posframe for mouse hover. (truly lives up to its name)
 * *(done)* an interactive command to popup document on a posframe for the 
symbol of the cursor.
 
 ## Acknowledgments
diff --git a/eldoc-mouse.el b/eldoc-mouse.el
index 44a2f11b521..970511a4777 100644
--- a/eldoc-mouse.el
+++ b/eldoc-mouse.el
@@ -28,10 +28,9 @@
 ;;; Commentary:
 
 ;; This package enhances eldoc' by displaying documentation in a child frame
-;; when the mouse hovers over a symbol in eglot'-managed buffers.  It 
integrates
-;; with posframe' for popup documentation and provides a debounced mouse hover
-;; mechanism to avoid spamming the LSP server.  Enable it in prog-mode' buffers
-;; to show documentation for the symbol under the mouse cursor.
+;; when the mouse hovers over a symbol.  It integrates with posframe' for 
popup 
+;; documentation. Enable it in buffers that you want to show documentation 
using
+;; eldoc for the symbol under the mouse cursor.
 
 ;; To use, ensure posframe is installed, then add:
 ;; (require 'eldoc-mouse)
@@ -87,13 +86,20 @@ no limit, the popup may affect writing."
 (defvar-local eldoc-mouse-last-symbol-bounds nil
   "Bounds of the last symbol processed for eldoc.")
 
-(defvar-local eldoc-mouse-unsupress-posframe nil
-  "Temporarily un-suppress the posframe.
-By default, posframe will not used by eldoc.")
-
 (defvar-local eldoc-mouse--original-display-functions nil
   "Store the original `eldoc-display-functions'.")
 
+(defvar-local eldoc-mouse--doc-identifier "*^eldoc-mouse*^"
+  "The identifier used for distinguish the doc triggered by eldoc-mouse.")
+
+(defvar eldoc-mouse--eldoc-documentation-functions
+  '(eldoc-mouse--eglot-eldoc-documentation-function
+    eldoc-mouse--elisp-eldoc-documentation-function)
+  "The `eldoc-documentation-functions' for `eldoc-mouse-mode'.")
+
+(defvar-local eldoc-mouse--original-documentation-functions nil
+  "The original eldoc-documentation-fuctions.")
+
 ;;;###autoload
 (define-minor-mode eldoc-mouse-mode
   "Toggle the `eldoc-mouse-mode'."
@@ -109,56 +115,38 @@ By default, posframe will not used by eldoc.")
   (interactive)
   (eldoc-mouse--hide-posframe)
   (when-let* ((symbol-bounds (bounds-of-thing-at-point 'symbol)))
-    (cond
-     (eldoc-mouse-mode
-      (add-hook
-       'eldoc-documentation-functions #'eldoc-mouse-hover-eldoc-function
-       nil t)
-      (setq-local eldoc-mouse-last-symbol-bounds symbol-bounds)
-      (setq-local eldoc-mouse-unsupress-posframe t)
-
-      ;; Make sure eldoc always send the request to get doc.
-      (setq eldoc--last-request-state nil)
-
-      (eldoc-print-current-symbol-info)
-      (remove-hook
-       'eldoc-documentation-functions #'eldoc-mouse-hover-eldoc-function
-       t))
-     (t
-      (when (eglot-managed-p)
-        (remove-hook
-         'eldoc-documentation-functions #'eglot-signature-eldoc-function
-         t))
-      
-      (setq-local eldoc-mouse-last-symbol-bounds symbol-bounds)
+    (setq-local eldoc-mouse--original-documentation-functions 
eldoc-documentation-functions)
+    (setq-local eldoc-documentation-functions nil)
+    (dolist (fun-sym eldoc-mouse--eldoc-documentation-functions)
+      (advice-add fun-sym :around #'eldoc-mouse--hover-edloc-function-advise)
+      (add-hook 'eldoc-documentation-functions (symbol-function fun-sym) nil 
t))
+    (setq-local eldoc-mouse-last-symbol-bounds symbol-bounds)
+    (when (not eldoc-mouse-mode)
       (unless eldoc-mouse--original-display-functions
-        (setq-local eldoc-mouse--original-display-functions
-                    eldoc-display-functions))
+        (setq-local eldoc-mouse--original-display-functions 
eldoc-display-functions))
       (setq-local eldoc-display-functions
                   (append
-                   eldoc-display-functions '(eldoc-mouse-display-in-posframe)))
-      (setq-local eldoc-mouse-unsupress-posframe t)
-
-      ;; Make sure eldoc always send the request to get doc.
-      (setq eldoc--last-request-state nil)
+                   eldoc-display-functions 
'(eldoc-mouse-display-in-posframe))))
+    (setq eldoc--last-request-state nil)
+    (eldoc-print-current-symbol-info)
+    (dolist (fun-sym eldoc-mouse--eldoc-documentation-functions)
+      (advice-remove fun-sym #'eldoc-mouse--hover-edloc-function-advise))
+    (setq-local eldoc-documentation-functions 
eldoc-mouse--original-documentation-functions)))
 
-      (eldoc-print-current-symbol-info)
-      (when (eglot-managed-p)
-        (add-hook 'eldoc-documentation-functions 
#'eglot-signature-eldoc-function
-                  nil
-                  t))))))
 (defun eldoc-mouse-enable ()
-  "Enable eldoc-mouse in all `eglot-managed-p' buffers."
+  "Enable eldoc-mouse in buffers."
+  ;; Enable mouse tracking.
+  (setq track-mouse t)
+  (setq-local eldoc-mouse--original-display-functions eldoc-display-functions)
+  (setq-local eldoc-display-functions
+              (append
+               eldoc-display-functions '(eldoc-mouse-display-in-posframe)))
+  (local-set-key [mouse-movement] #'eldoc-mouse-doc-on-mouse)
+
+  ;; Optimization for eglot managed buffers.
   (when (eglot-managed-p)
-    ;; Enable mouse tracking.
-    (setq track-mouse t)
-    (setq-local eldoc-mouse--original-display-functions 
eldoc-display-functions)
-    (setq-local eldoc-display-functions
-                (append
-                 eldoc-display-functions '(eldoc-mouse-display-in-posframe)))
     ;; Avoid unnecessary document of signatures that clutters the document.
-    (remove-hook 'eldoc-documentation-functions 
#'eglot-signature-eldoc-function
-                 t)
+    (remove-hook 'eldoc-documentation-functions 
#'eglot-signature-eldoc-function t)
     ;; Avoid show document for the cursor.
     (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t)
     ;; Enable highlight symbol under the cursor.
@@ -169,34 +157,30 @@ By default, posframe will not used by eldoc.")
     ;; See details:
     ;; 
https://cgit.git.savannah.gnu.org/cgit/emacs.git/commit/?id=60166a419f601b413db86ddce186cc387e8ec269
     (when (fboundp 'eglot--highlight-piggyback)
-      (add-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback
-                nil
-                t))
-    (local-set-key [mouse-movement] #'eldoc-mouse-doc-on-mouse)))
+      (add-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback 
nil t))))
 
 (defun eldoc-mouse-disable ()
-  "Disable eldoc-mouse in all `eglot-managed-p' buffers."
+  "Disable eldoc-mouse in buffers."
   (when eldoc-mouse--original-display-functions
     (setq-local eldoc-display-functions
                 eldoc-mouse--original-display-functions))
-  (when (fboundp 'eglot--highlight-piggyback)
-    (remove-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback 
t))
-
-  (unless (memq #'eglot-signature-eldoc-function eldoc-documentation-functions)
-    (add-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function
-              nil
-              t))
-  (unless (memq #'eglot-hover-eldoc-function eldoc-documentation-functions)
-    (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function
-              nil
-              t))
+
+  ;; Optimization for eglot managed buffers.
+  (when (eglot-managed-p)
+    (when (fboundp 'eglot--highlight-piggyback)
+      (remove-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback 
t))
+
+    (unless (memq #'eglot-signature-eldoc-function 
eldoc-documentation-functions)
+      (add-hook 'eldoc-documentation-functions 
#'eglot-signature-eldoc-function nil t))
+    (unless (memq #'eglot-hover-eldoc-function eldoc-documentation-functions)
+      (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function 
nil t)))
 
   (when eldoc-mouse-mouse-timer
     (cancel-timer eldoc-mouse-mouse-timer)
     (setq eldoc-mouse-mouse-timer nil))
   (eldoc-mouse--hide-posframe)
   (local-unset-key [mouse-movement])
-  (when (y-or-n-p "Disable mouse-tracking (may impact other modes)?")
+  (when (y-or-n-p "eldoc-mouse-mode has been turned off.  Also disable 
mouse-tracking (may impact other modes)?")
     (setq track-mouse nil)))
 
 (defun eldoc-mouse--post-command-hook ()
@@ -225,15 +209,14 @@ POS is the buffer position under the mouse cursor."
                  (< pos (car eldoc-mouse-last-symbol-bounds))
                  (> pos (cdr eldoc-mouse-last-symbol-bounds))))
     (eldoc-mouse--hide-posframe)
-    (when (fboundp 'eglot--highlight-piggyback)
-      (remove-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback
-                   t))
     (when eldoc-mouse-mouse-overlay
       (delete-overlay eldoc-mouse-mouse-overlay))
     (save-excursion
-      (add-hook
-       'eldoc-documentation-functions #'eldoc-mouse-hover-eldoc-function
-       nil t)
+      (setq-local eldoc-mouse--original-documentation-functions 
eldoc-documentation-functions)
+      (setq-local eldoc-documentation-functions nil)
+      (dolist (fun-sym eldoc-mouse--eldoc-documentation-functions)
+        (advice-add fun-sym :around #'eldoc-mouse--hover-edloc-function-advise)
+        (add-hook 'eldoc-documentation-functions (symbol-function fun-sym) nil 
t))
       (goto-char pos)
       (setq-local eldoc-mouse-last-symbol-bounds
                   (bounds-of-thing-at-point 'symbol))
@@ -243,20 +226,15 @@ POS is the buffer position under the mouse cursor."
       (when (and eldoc-mouse-last-symbol-bounds
                  (not (eolp))
                  (not (nth 4 (syntax-ppss))))
-        (setq-local eldoc-mouse-unsupress-posframe t)
         (eldoc-print-current-symbol-info)
         (setq-local eldoc-mouse-mouse-overlay
                     (make-overlay
                      (car eldoc-mouse-last-symbol-bounds)
                      (cdr eldoc-mouse-last-symbol-bounds)))
-        (overlay-put eldoc-mouse-mouse-overlay 'face 'highlight))
-      (remove-hook
-       'eldoc-documentation-functions #'eldoc-mouse-hover-eldoc-function
-       t)
-      (when (fboundp 'eglot--highlight-piggyback)
-        (add-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback
-                  nil
-                  t)))))
+        (overlay-put eldoc-mouse-mouse-overlay 'face 'secondary-selection))
+      (dolist (fun-sym eldoc-mouse--eldoc-documentation-functions)
+        (advice-remove fun-sym #'eldoc-mouse--hover-edloc-function-advise))
+      (setq-local eldoc-documentation-functions 
eldoc-mouse--original-documentation-functions))))
 
 (defun eldoc-mouse--hide-posframe ()
   "Hide the posframe."
@@ -268,48 +246,56 @@ POS is the buffer position under the mouse cursor."
 (defun eldoc-mouse-doc-on-mouse (event)
   "Show eldoc documentation when mouse hovers over EVENT."
   (interactive "e")
-  (let ((pos (posn-point (event-start event))))
-    (when (and pos (number-or-marker-p pos) (eglot-managed-p))
+  (when eldoc-mouse-mode
+    (let ((pos (posn-point (event-start event))))
+    (when (and pos (number-or-marker-p pos))
       ;; Debounce to avoid spamming eglot.
       (when eldoc-mouse-mouse-timer
         (cancel-timer eldoc-mouse-mouse-timer))
       (setq eldoc-mouse-mouse-timer
             (run-with-idle-timer
              eldoc-mouse-idle-time nil #'eldoc-mouse-show-doc-at
-             pos)))))
+             pos))))))
 
-(defun eldoc-mouse-hover-eldoc-function (cb)
+(defun eldoc-mouse--eglot-eldoc-documentation-function (cb)
   "Modify the `eglot-hover-eldoc-function'.
 So it won't call `eglot--highlight-piggyback` with `CB`."
-  (if (fboundp 'eglot--highlight-piggyback)
-      (cl-letf (((symbol-function 'eglot--highlight-piggyback)
-                 (lambda (&rest _args) (message ""))))
+  (if (eglot-managed-p)
+      (if (fboundp 'eglot--highlight-piggyback)
+          (cl-letf (((symbol-function 'eglot--highlight-piggyback)
+                     (lambda (&rest _args) (message ""))))
+            (eglot-hover-eldoc-function cb))
         (eglot-hover-eldoc-function cb))
-    (eglot-hover-eldoc-function cb)))
-
-(defun eldoc-mouse-handle-eglot-hooks ()
-  "Handle the eldoc eglot hooks.
-Remove all eglot hooks and keep highlighting on cursor,
-add eldoc-mouse's `eldoc-display-functions'."
-  (setq-local eldoc-display-functions
-              (append
-               eldoc-display-functions '(eldoc-mouse-display-in-posframe)))
-  ;; Avoid unnecessary document of signatures that clutters the document.
-  (remove-hook 'eldoc-documentation-functions #'eglot-signature-eldoc-function
-               t)
-  ;; Avoid show document for the cursor.
-  (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t)
-  ;; Enable highlight symbol under the cursor.
-  ;; In the future the following line is no longer necessary,
-  ;; as emacs use a specific function eglot-highlight-eldoc-function
-  ;; for highlighting.
-  ;; And here, we want to keep the highlight at cursor.
-  ;; See details:
-  ;; 
https://cgit.git.savannah.gnu.org/cgit/emacs.git/commit/?id=60166a419f601b413db86ddce186cc387e8ec269
-  (when (fboundp 'eglot--highlight-piggyback)
-    (add-hook 'eldoc-documentation-functions #'eglot--highlight-piggyback
-              nil
-              t)))
+    nil))
+
+(defun eldoc-mouse--elisp-eldoc-documentation-function (_cb)
+  "The `eldoc-documentation-functions' implementation for elisp."
+  (if (eq major-mode 'emacs-lisp-mode)
+      (let ((sym (symbol-at-point)))
+        (cond
+         ;; If the symbol is a function
+         ((and sym (fboundp sym))
+          (documentation sym))
+         ;; If the symbol is a variable
+         ((and sym (boundp sym))
+          (let ((doc (documentation-property sym 'variable-documentation)))
+            (if doc
+                doc
+              nil)))
+         ;; If no symbol or not a function/variable
+         (t nil)))
+    nil))
+
+(defun eldoc-mouse--hover-edloc-function-advise (orig-fn fn)
+  "Wrap FN argument of ORIG-FN so that it append indentifier'."
+  (let ((result (funcall orig-fn
+           (lambda (s &rest r)
+             (funcall fn (if (and s (not (string-empty-p (string-trim s))))
+                             (concat s eldoc-mouse--doc-identifier)
+                           s) r)))))
+    (if (stringp result)
+        (concat result eldoc-mouse--doc-identifier)
+      result)))
 
 (defun eldoc-mouse-is-mouse-hovering-posframe? (posframe-name)
   "Check if the mouse is hovering over the given posframe `POSFRAME-NAME'."
@@ -323,10 +309,8 @@ add eldoc-mouse's `eldoc-display-functions'."
 
 (defun eldoc-mouse-display-in-posframe (docs _interactive)
   "Display `DOCS` STRING in a posframe at the current mouse position."
-  (when (and docs eldoc-mouse-unsupress-posframe)
-    (setq-local eldoc-mouse-unsupress-posframe nil)
+  (when (and docs (string-match-p (regexp-quote eldoc-mouse--doc-identifier) 
(car (car docs))))
     ;; Output the document for *eldoc* buffer.
-    ;; (eldoc--format-doc-buffer docs)
     (let* ((eldoc-buffer
             (get-buffer
              (car
@@ -340,7 +324,7 @@ add eldoc-mouse's `eldoc-display-functions'."
                  (buffer-string)))
               (border-color (face-foreground 'default)))
           (when text
-            (eldoc-mouse--pop-doc text border-color))))
+            (eldoc-mouse--pop-doc (replace-regexp-in-string (regexp-quote 
eldoc-mouse--doc-identifier) "" text) border-color))))
       ;; non-nil => suppress other display functions.
       t)))
 

Reply via email to