branch: elpa/flycheck
commit 0ba62aea8f5189c67325c299943b3e14e7e3091f
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Document errors at point through Eldoc by default
    
    The old default pushed error messages to the echo area on a timer via
    display-message-or-buffer, popping the *Flycheck error messages* buffer
    for long messages.  Going through Eldoc composes with other Eldoc
    sources like Eglot and honors Eldoc display customizations
    (eldoc-echo-area-use-multiline-p, eldoc-box and friends).
    
    The Eldoc backend only activates when flycheck-display-errors-function
    has its new default value, so custom display functions and extensions
    like flycheck-posframe keep working unchanged.
---
 CHANGES.rst                      |  6 ++++
 doc/user/error-interaction.rst   | 22 ++++++++----
 flycheck.el                      | 74 ++++++++++++++++++++++++++++++++++----
 test/specs/test-customization.el |  4 +--
 test/specs/test-error-display.el | 78 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 169 insertions(+), 15 deletions(-)

diff --git a/CHANGES.rst b/CHANGES.rst
index 78e99f202f..340770e74c 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -12,6 +12,12 @@
   the existing minimum-level filter (``f``); ``F`` resets all filters.
 - The File and ID columns of the error list adapt their width to the
   errors on display instead of truncating at fixed widths.
+- Errors at point are now documented through Eldoc by default, composing
+  with other Eldoc sources (e.g. Eglot) and honoring Eldoc display
+  customizations.  The previous behavior remains available by setting
+  ``flycheck-display-errors-function`` to
+  ``flycheck-display-error-messages``; custom display functions and
+  extensions like flycheck-posframe keep working unchanged.
 - [#1787]: Add the ``:handle-suspicious`` property to command checkers.
   It lets a checker translate a "suspicious state" (a non-zero exit
   status with no parsable errors, e.g. a missing dependency) into
diff --git a/doc/user/error-interaction.rst b/doc/user/error-interaction.rst
index 28944513c3..191e45b0d8 100644
--- a/doc/user/error-interaction.rst
+++ b/doc/user/error-interaction.rst
@@ -108,9 +108,10 @@ customising:
    `flycheck-posframe`, to prevent `flycheck-posframe` from repeatedly 
displaying
    errors at point.
 
-By default Flycheck shows the error messages in the minibuffer or in a separate
-buffer if the minibuffer is too small to hold the whole error message but this
-behaviour is entirely customisable:
+By default Flycheck documents the errors at point through Eldoc, alongside
+other Eldoc sources such as Eglot.  This honors your Eldoc customizations,
+e.g. `eldoc-echo-area-use-multiline-p` or alternative Eldoc frontends like
+`eldoc-box`.  The behaviour is entirely customisable:
 
 .. defcustom:: flycheck-display-errors-function
 
@@ -119,15 +120,22 @@ behaviour is entirely customisable:
    The function is given the list of Flycheck errors to display as sole 
argument
    and shall display these errors to the user in some way.
 
-Flycheck provides two built-in functions for this option:
+Flycheck provides three built-in functions for this option:
+
+.. defun:: flycheck-display-errors-via-eldoc errors
+
+   The default.  Errors at point are shown through Eldoc.  Messages too
+   long for the echo area (see `eldoc-echo-area-use-multiline-p`) can be
+   read in full with :kbd:`M-x eldoc-doc-buffer`.
 
 .. defun:: flycheck-display-error-messages errors
            flycheck-display-error-messages-unless-error-list errors
 
    Show error messages and IDs in the echo area or in a separate buffer if the
-   echo area is too small (using `display-message-or-buffer` which see).  The
-   latter only displays errors when the :ref:`error list <flycheck-error-list>`
-   is not visible.  To enable it add the following to your :term:`init file`:
+   echo area is too small (using `display-message-or-buffer` which see).  This
+   was the default before Flycheck 37.  The latter only displays errors when
+   the :ref:`error list <flycheck-error-list>` is not visible.  To enable it
+   add the following to your :term:`init file`:
 
    .. code-block:: elisp
 
diff --git a/flycheck.el b/flycheck.el
index 6dfd9e4f55..45c434e4ef 100644
--- a/flycheck.el
+++ b/flycheck.el
@@ -431,21 +431,30 @@ Use floating point numbers to express fractions of 
seconds."
   :package-version '(flycheck . "0.15")
   :safe #'numberp)
 
-(defcustom flycheck-display-errors-function #'flycheck-display-error-messages
+(defcustom flycheck-display-errors-function #'flycheck-display-errors-via-eldoc
   "Function to display error messages.
 
 If set to a function, call the function with the list of errors
 to display as single argument.  Each error is an instance of the
 `flycheck-error' struct.
 
+With the default value `flycheck-display-errors-via-eldoc',
+errors at point are documented through Eldoc.  This composes with
+other Eldoc sources (e.g. Eglot) and honors Eldoc display
+customizations such as `eldoc-echo-area-use-multiline-p' or
+alternative Eldoc frontends.  Use \\[eldoc-doc-buffer] to read
+messages that don't fit into the echo area in full.
+
 If set to nil, do not display errors at all."
   :group 'flycheck
-  :type '(choice (const :tag "Display error messages"
+  :type '(choice (const :tag "Display errors via Eldoc"
+                        flycheck-display-errors-via-eldoc)
+                 (const :tag "Display error messages"
                         flycheck-display-error-messages)
                  (const :tag "Display error messages only if no error list"
                         flycheck-display-error-messages-unless-error-list)
                  (function :tag "Error display function"))
-  :package-version '(flycheck . "0.13")
+  :package-version '(flycheck . "37")
   :risky t)
 
 (defcustom flycheck-clear-displayed-errors-function 
#'flycheck-clear-displayed-error-messages
@@ -3144,6 +3153,15 @@ ARG is ‘toggle’; disable the mode otherwise."
    (flycheck-mode
     (flycheck-clear)
     (flycheck--sync-margin)
+    (add-hook 'eldoc-documentation-functions #'flycheck-eldoc-function nil t)
+    ;; `global-eldoc-mode' may have skipped this buffer because no
+    ;; documentation source was registered when it made its decision;
+    ;; give it another chance now that Flycheck provides one.  Buffers
+    ;; where the user disabled Eldoc entirely are left alone; there the
+    ;; display timer picks up the slack.
+    (when (and (bound-and-true-p global-eldoc-mode)
+               (not (bound-and-true-p eldoc-mode)))
+      (turn-on-eldoc-mode))
 
     (pcase-dolist (`(,hook . ,fn) (reverse flycheck-hooks-alist))
       (add-hook hook fn nil 'local))
@@ -3165,6 +3183,8 @@ ARG is ‘toggle’; disable the mode otherwise."
     (unless (eq flycheck-old-next-error-function :unset)
       (setq next-error-function flycheck-old-next-error-function))
 
+    (remove-hook 'eldoc-documentation-functions #'flycheck-eldoc-function t)
+
     (pcase-dolist (`(,hook . ,fn) flycheck-hooks-alist)
       (remove-hook hook fn 'local))
 
@@ -5920,6 +5940,43 @@ avoid slowing down editing when the error list is 
hidden."
 
 
 ;;; Displaying errors in the current buffer
+(defun flycheck--display-errors-via-eldoc-p ()
+  "Whether errors at point are displayed through Eldoc."
+  (eq flycheck-display-errors-function #'flycheck-display-errors-via-eldoc))
+
+(defun flycheck-display-errors-via-eldoc (_errors)
+  "Trigger Eldoc to document the errors at point.
+
+Eldoc computes its documentation from all of its registered
+sources, including `flycheck-eldoc-function', so refreshing it
+shows the Flycheck errors at point alongside e.g. Eglot's
+documentation.  This works from any display entry point --
+interactive commands, error navigation, automatic display after a
+check -- whether or not variable `eldoc-mode' is enabled."
+  (eldoc-print-current-symbol-info))
+
+(defun flycheck-eldoc-function (callback &rest _ignored)
+  "Document the Flycheck errors at point by calling CALLBACK.
+
+Intended for `eldoc-documentation-functions', where command
+`flycheck-mode' registers it.  Only active when
+`flycheck-display-errors-function' has its default value
+`flycheck-display-errors-via-eldoc', so that user customizations
+and third-party display packages keep working unchanged."
+  (when (and flycheck-mode (flycheck--display-errors-via-eldoc-p))
+    (when-let* ((errors (flycheck-overlay-errors-at (point))))
+      (funcall callback
+               (mapconcat
+                (lambda (err)
+                  (let ((level (flycheck-error-level err)))
+                    (concat
+                     (propertize (symbol-name level)
+                                 'face (flycheck-error-level-error-list-face
+                                        level))
+                     ": "
+                     (flycheck-error-format-message-and-id err))))
+                errors "\n")))))
+
 (defun flycheck-display-errors (errors)
   "Display ERRORS using `flycheck-display-errors-function'."
   (when flycheck-display-errors-function
@@ -5957,9 +6014,14 @@ If there are no errors, clears the error messages at 
point."
 (defun flycheck-display-error-at-point-soon ()
   "Display error messages at point, with a delay."
   (flycheck-cancel-error-display-error-at-point-timer)
-  (setq flycheck-display-error-at-point-timer
-        (run-at-time flycheck-display-errors-delay nil
-                     'flycheck-display-error-at-point)))
+  ;; When errors are displayed through Eldoc and `eldoc-mode' is active,
+  ;; its own post-command refresh covers this path; without `eldoc-mode'
+  ;; fall back to Flycheck's timer, which triggers the refresh itself
+  (unless (and (flycheck--display-errors-via-eldoc-p)
+               (bound-and-true-p eldoc-mode))
+    (setq flycheck-display-error-at-point-timer
+          (run-at-time flycheck-display-errors-delay nil
+                       'flycheck-display-error-at-point))))
 
 
 (defun flycheck-handle-focus-change ()
diff --git a/test/specs/test-customization.el b/test/specs/test-customization.el
index 9fea51bd1e..571c3eb085 100644
--- a/test/specs/test-customization.el
+++ b/test/specs/test-customization.el
@@ -78,9 +78,9 @@
 
   (describe "flycheck-display-errors-function"
 
-    (it "defaults to display-error-messages"
+    (it "defaults to display via eldoc"
       (expect flycheck-display-errors-function
-              :to-be #'flycheck-display-error-messages)))
+              :to-be #'flycheck-display-errors-via-eldoc)))
 
   (describe "flycheck-indication-mode"
 
diff --git a/test/specs/test-error-display.el b/test/specs/test-error-display.el
index d7aaaf6704..70f95e4177 100644
--- a/test/specs/test-error-display.el
+++ b/test/specs/test-error-display.el
@@ -27,6 +27,84 @@
         (flycheck-display-errors (list err))
         (expect displayed-errors :to-equal (list err)))))
 
+  (describe "flycheck-eldoc-function"
+
+    (it "documents the errors at point"
+      (flycheck-buttercup-with-temp-buffer
+        (insert "hello world")
+        (flycheck-mode)
+        (goto-char 3)
+        (flycheck-add-overlay
+         (flycheck-error-new-at 1 1 'warning "Watch out" :end-column 6))
+        (let (doc)
+          (flycheck-eldoc-function (lambda (string &rest _) (setq doc string)))
+          (expect doc :to-match "warning: Watch out"))))
+
+    (it "registers with eldoc in flycheck-mode"
+      (flycheck-buttercup-with-temp-buffer
+        (flycheck-mode 1)
+        (expect (member #'flycheck-eldoc-function
+                        eldoc-documentation-functions)
+                :to-be-truthy)
+        (flycheck-mode -1)
+        (expect (member #'flycheck-eldoc-function
+                        eldoc-documentation-functions)
+                :not :to-be-truthy)))
+
+    (it "stays inert when the display function is customized"
+      (flycheck-buttercup-with-temp-buffer
+        (insert "hello")
+        (flycheck-mode)
+        (flycheck-add-overlay (flycheck-error-new-at 1 1 'error "Boom"))
+        (goto-char 1)
+        (let ((flycheck-display-errors-function
+               #'flycheck-display-error-messages)
+              (called nil))
+          (flycheck-eldoc-function (lambda (&rest _) (setq called t)))
+          (expect called :not :to-be-truthy))))
+
+    (it "triggers an eldoc refresh from any display entry point"
+      (flycheck-buttercup-with-temp-buffer
+        (insert "hello")
+        (flycheck-mode)
+        (flycheck-add-overlay (flycheck-error-new-at 1 1 'error "Boom"))
+        (goto-char 1)
+        (let ((refreshed 0))
+          (cl-letf (((symbol-function 'eldoc-print-current-symbol-info)
+                     (lambda (&rest _) (cl-incf refreshed))))
+            ;; The default display function refreshes Eldoc, so the
+            ;; interactive command, error navigation and automatic
+            ;; display keep working
+            (flycheck-display-error-at-point)
+            (expect refreshed :to-equal 1)))))
+
+    (it "enables eldoc-mode when global-eldoc-mode allows it"
+      (flycheck-buttercup-with-temp-buffer
+        (let ((global-eldoc-mode t))
+          (flycheck-mode 1)
+          (expect (bound-and-true-p eldoc-mode) :to-be-truthy)
+          (flycheck-mode -1))))
+
+    (it "keeps the display timer off only while eldoc-mode is active"
+      (flycheck-buttercup-with-temp-buffer
+        ;; `eldoc-mode' refuses to activate without a documentation
+        ;; source, so register Flycheck's first
+        (flycheck-mode 1)
+        (eldoc-mode 1)
+        (flycheck-display-error-at-point-soon)
+        (expect flycheck-display-error-at-point-timer :not :to-be-truthy)
+        (eldoc-mode -1)
+        ;; Without eldoc-mode the timer must pick up the slack
+        (flycheck-display-error-at-point-soon)
+        (expect flycheck-display-error-at-point-timer :to-be-truthy)
+        (flycheck-cancel-error-display-error-at-point-timer)
+        ;; The old default schedules the timer regardless
+        (let ((flycheck-display-errors-function
+               #'flycheck-display-error-messages))
+          (flycheck-display-error-at-point-soon)
+          (expect flycheck-display-error-at-point-timer :to-be-truthy)
+          (flycheck-cancel-error-display-error-at-point-timer)))))
+
   (describe "flycheck-display-error-messages"
 
     (it "displays error messages"

Reply via email to