fix(:tools lsp): make eglot diags use flycheck with :checkers syntax
Research on how Flycheck work, and a pending fix from Eglot, allowed to get a cleaner representation of how this "hack" works and make it more resilient Co-Authored-By: Steve Purcell <steve@sanityinc.com>
This commit is contained in:
parent
539ab8d1e9
commit
813dc6e664
2 changed files with 26 additions and 20 deletions
|
@ -11,6 +11,8 @@
|
||||||
;; NOTE We disable eglot-auto-display-help-buffer because :select t in
|
;; NOTE We disable eglot-auto-display-help-buffer because :select t in
|
||||||
;; its popup rule causes eglot to steal focus too often.
|
;; its popup rule causes eglot to steal focus too often.
|
||||||
eglot-auto-display-help-buffer nil)
|
eglot-auto-display-help-buffer nil)
|
||||||
|
(when (featurep! :checkers syntax)
|
||||||
|
(setq eglot-stay-out-of '(flymake)))
|
||||||
|
|
||||||
:config
|
:config
|
||||||
(set-popup-rule! "^\\*eglot-help" :size 0.15 :quit t :select t)
|
(set-popup-rule! "^\\*eglot-help" :size 0.15 :quit t :select t)
|
||||||
|
|
|
@ -1,22 +1,27 @@
|
||||||
;;; flycheck-eglot --- Hacky eglot support in flycheck -*- lexical-binding: t; -*-
|
;;; flycheck-eglot --- Hacky eglot support in flycheck -*- lexical-binding: t; -*-
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
;; This file sets up flycheck so that, when eglot receives a publishDiagnostics method
|
;; This file sets up flycheck so that, when eglot receives a publishDiagnostics method
|
||||||
;; from the server, then eglot calls a report function that creates diagnostics for
|
;; from the server, flycheck updates the reports.
|
||||||
;; flycheck.
|
|
||||||
;;
|
;;
|
||||||
;; It works by creating an eglot-specific callback function, and using this as
|
;; Thanks to:
|
||||||
;; the REPORT-FN argument of `eglot-flymake-backend', which internally registers
|
;; - joaotavora for adding a handle to plug flycheck, and
|
||||||
;; that lambda as the function to use whenever there is a publishDiagnostics method.
|
;; - purcell for finding out the initial stub and the current implementation
|
||||||
;; Calling `+lsp--flycheck-eglot-init' "too late" is not a problem, since if there
|
;;
|
||||||
;; are any unreported/missed diagnostics, eglot ensures that the
|
;; It works by creating a bridge function which can be used as the argument of
|
||||||
;; REPORT-FN function is called immediately.
|
;; `eglot-flymake-backend', which both consumes diagnostics and queue a call to
|
||||||
|
;; 'flycheck-buffer'
|
||||||
;;
|
;;
|
||||||
;; Note: as long as joaotavora/eglot#596 isn't fixed/dealt with, this checker cannot
|
|
||||||
;; work. Please check the issue on github for more context
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
(defvar-local +lsp--flycheck-eglot--current-errors nil)
|
||||||
|
|
||||||
(defun +lsp--flycheck-eglot-init (checker callback)
|
(defun +lsp--flycheck-eglot-init (checker callback)
|
||||||
"CHECKER is the checker (eglot).
|
"CHECKER is the checker (eglot).
|
||||||
CALLBACK is the function that we need to call when we are done, on all the errors."
|
CALLBACK is the function that we need to call when we are done, on all the errors."
|
||||||
|
(eglot-flymake-backend #'+lsp--flycheck-eglot--on-diagnostics)
|
||||||
|
(funcall callback 'finished +lsp--flycheck-eglot--current-errors))
|
||||||
|
|
||||||
|
(defun +lsp--flycheck-eglot--on-diagnostics (diags &rest _)
|
||||||
(cl-labels
|
(cl-labels
|
||||||
((flymake-diag->flycheck-err
|
((flymake-diag->flycheck-err
|
||||||
(diag)
|
(diag)
|
||||||
|
@ -30,17 +35,13 @@ CALLBACK is the function that we need to call when we are done, on all the error
|
||||||
(_ (error "Unknown diagnostic type, %S" diag)))
|
(_ (error "Unknown diagnostic type, %S" diag)))
|
||||||
(flymake--diag-text diag)
|
(flymake--diag-text diag)
|
||||||
:end-pos (flymake--diag-end diag)
|
:end-pos (flymake--diag-end diag)
|
||||||
:checker checker
|
:checker 'eglot
|
||||||
:buffer (current-buffer)
|
:buffer (current-buffer)
|
||||||
:filename (buffer-file-name)))))
|
:filename (buffer-file-name)))))
|
||||||
;; NOTE: Setting up eglot to automatically create flycheck errors for the buffer.
|
(setq +lsp--flycheck-eglot--current-errors
|
||||||
;; Internally, this sets the lambda as the callback to be used by eglot
|
(mapcar #'flymake-diag->flycheck-err diags))
|
||||||
;; when it receives a publishDiagnostics method from the server
|
;; Call Flycheck to update the diagnostics annotations
|
||||||
(eglot-flymake-backend
|
(flycheck-buffer-deferred)))
|
||||||
(lambda (flymake-diags &rest _)
|
|
||||||
(funcall callback
|
|
||||||
'finished
|
|
||||||
(mapcar #'flymake-diag->flycheck-err flymake-diags))))))
|
|
||||||
|
|
||||||
(defun +lsp--flycheck-eglot-available-p ()
|
(defun +lsp--flycheck-eglot-available-p ()
|
||||||
(bound-and-true-p eglot--managed-mode))
|
(bound-and-true-p eglot--managed-mode))
|
||||||
|
@ -56,11 +57,14 @@ CALLBACK is the function that we need to call when we are done, on all the error
|
||||||
(add-hook! 'eglot-managed-mode-hook
|
(add-hook! 'eglot-managed-mode-hook
|
||||||
(defun +lsp-eglot-prefer-flycheck-h ()
|
(defun +lsp-eglot-prefer-flycheck-h ()
|
||||||
(when eglot--managed-mode
|
(when eglot--managed-mode
|
||||||
|
(flymake-mode -1)
|
||||||
(when-let ((current-checker (flycheck-get-checker-for-buffer)))
|
(when-let ((current-checker (flycheck-get-checker-for-buffer)))
|
||||||
(unless (equal current-checker 'eglot)
|
(unless (equal current-checker 'eglot)
|
||||||
(flycheck-add-next-checker 'eglot current-checker)))
|
(flycheck-add-next-checker 'eglot current-checker)))
|
||||||
(flycheck-add-mode 'eglot major-mode)
|
(flycheck-add-mode 'eglot major-mode)
|
||||||
(flycheck-mode 1)
|
(flycheck-mode 1)
|
||||||
(flymake-mode -1))))
|
;; Call flycheck on initilization to make sure to display initial
|
||||||
|
;; errors
|
||||||
|
(flycheck-buffer-deferred))))
|
||||||
|
|
||||||
;;; flycheck-eglot.el ends here
|
;;; flycheck-eglot.el ends here
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue