Skip to content

Commit 01bbdd9

Browse files
authored
Move Flycheck/Flymake to lsp-diagnostics.el (#1990)
* Move flycheck integration to checker file * Rename functions * Move flymake integration * Fix flycheck configuration * Update docs and changelog * Remove uneeded check on auto-configure * check only lsp-checker-provider * Rename to lsp-diagnostics.el * Remove emacs 26 check * Move alias to lsp-mode and remove lsp-prefer-flymake * only enable mode if it has a known provider * Moved back to lsp-diagnostics using autoload
1 parent 8e0f3f9 commit 01bbdd9

File tree

5 files changed

+341
-276
lines changed

5 files changed

+341
-276
lines changed

CHANGELOG.org

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
* Changelog
22
** Release 7.0.1
3+
* Introduced ~lsp-diagnostics-mode~.
4+
* Safe renamed ~lsp-flycheck-default-level~ -> ~lsp-diagnostics-flycheck-default-level~
5+
* Safe renamed ~lsp-diagnostic-package~ -> ~lsp-diagnostics-provider~
36
* Dropped support for ~company-lsp~, the suggested provider is ~company-capf~.
47
* Moved completion features to ~lsp-completion.el~
58
* Safe renamed ~lsp-prefer-capf~ -> ~lsp-completion-provider~

docs/page/settings.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ These are `lsp-mode` specific custom settings:
1717
- `lsp-eldoc-render-all` - Display all of the info returned by `document/onHover`. If this is nil, `eldoc` will show only the symbol information.
1818
- `lsp-enable-completion-at-point` - Enable `completion-at-point` integration.
1919
- `lsp-enable-xref` - Enable xref integration.
20-
- `lsp-diagnostic-package` - Specifies which package to use for diagnostics. Choose from `:auto`, `:flycheck`, `:flymake` and `:none`. Default is `:auto` which means use `:flycheck` if present.
20+
- `lsp-diagnostics-provider` - Specifies which package to use for diagnostics. Choose from `:auto`, `:flycheck`, `:flymake` and `:none`. Default is `:auto` which means use `:flycheck` if present and fallback to `:flymake`.
2121
- `lsp-enable-indentation` - Indent regions using the file formatting functionality provided by the language server.
2222
- `lsp-enable-on-type-formatting` - Enable `textDocument/onTypeFormatting` integration.
2323
- `lsp-before-save-edits` - If non-nil, `lsp-mode` will apply edits suggested by the language server before saving a document.

lsp-diagnostics.el

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
;;; lsp-diagnostics.el --- LSP diagnostics integration -*- lexical-binding: t; -*-
2+
;;
3+
;; Copyright (C) 2020 emacs-lsp maintainers
4+
;;
5+
;; This program is free software; you can redistribute it and/or modify
6+
;; it under the terms of the GNU General Public License as published by
7+
;; the Free Software Foundation, either version 3 of the License, or
8+
;; (at your option) any later version.
9+
10+
;; This program is distributed in the hope that it will be useful,
11+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
;; GNU General Public License for more details.
14+
15+
;; You should have received a copy of the GNU General Public License
16+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
;;
18+
;;; Commentary:
19+
;;
20+
;; LSP diagnostics integration
21+
;;
22+
;;; Code:
23+
24+
(require 'lsp-mode)
25+
26+
;;;###autoload
27+
(define-obsolete-variable-alias 'lsp-diagnostic-package
28+
'lsp-diagnostics-provider "lsp-mode 7.0.1")
29+
30+
(defcustom lsp-diagnostics-provider :auto
31+
"The checker backend provider."
32+
:type
33+
'(choice
34+
(const :tag "Pick flycheck if present and fallback to flymake" :auto)
35+
(const :tag "Pick flycheck" :flycheck)
36+
(const :tag "Pick flymake" :flymake)
37+
(const :tag "Use neither flymake nor lsp" :none)
38+
(const :tag "Prefer flymake" t)
39+
(const :tag "Prefer flycheck" nil))
40+
:group 'lsp-mode
41+
:package-version '(lsp-mode . "6.3"))
42+
43+
(define-obsolete-variable-alias 'lsp-flycheck-default-level
44+
'lsp-diagnostics-flycheck-default-level "lsp-mode 7.0.1")
45+
46+
(defcustom lsp-diagnostics-flycheck-default-level 'error
47+
"Error level to use when the server does not report back a diagnostic level."
48+
:type '(choice
49+
(const error)
50+
(const warning)
51+
(const info))
52+
:group 'lsp-mode)
53+
54+
(defcustom lsp-diagnostics-attributes
55+
`((unnecessary :foreground "dim gray")
56+
(deprecated :strike-through t))
57+
"The Attributes used on the diagnostics.
58+
List containing (tag attributes) where tag is the LSP diagnostic tag and
59+
attributes is a `plist' containing face attributes which will be applied
60+
on top the flycheck face for that error level."
61+
:type '(repeat list)
62+
:group 'lsp-mode)
63+
64+
;; Flycheck integration
65+
66+
(declare-function flycheck-mode "ext:flycheck")
67+
(declare-function flycheck-define-generic-checker
68+
"ext:flycheck" (symbol docstring &rest properties))
69+
(declare-function flycheck-error-new "ext:flycheck" t t)
70+
(declare-function flycheck-error-message "ext:flycheck" (err) t)
71+
(declare-function flycheck-define-error-level "ext:flycheck" (level &rest properties))
72+
(declare-function flycheck-buffer "ext:flycheck")
73+
74+
(declare-function lsp-cpp-flycheck-clang-tidy-error-explainer "lsp-cpp")
75+
76+
(defvar flycheck-check-syntax-automatically)
77+
(defvar flycheck-checker)
78+
(defvar flycheck-checkers)
79+
80+
(defun lsp-diagnostics--flycheck-level (flycheck-level tags)
81+
"Generate flycheck level from the original FLYCHECK-LEVEL (e.
82+
g. `error', `warning') and list of LSP TAGS."
83+
(let ((name (format "lsp-flycheck-%s-%s"
84+
flycheck-level
85+
(mapconcat #'symbol-name tags "-"))))
86+
(or (intern-soft name)
87+
(let* ((face (--doto (intern (format "lsp-%s-face" name))
88+
(copy-face (-> flycheck-level
89+
(get 'flycheck-overlay-category)
90+
(get 'face))
91+
it)
92+
(mapc (lambda (tag)
93+
(apply #'set-face-attribute it nil
94+
(cl-rest (assoc tag lsp-diagnostics-attributes))))
95+
tags)))
96+
(category (--doto (intern (format "lsp-%s-category" name))
97+
(setf (get it 'face) face
98+
(get it 'priority) 100)))
99+
(new-level (intern name))
100+
(bitmap (or (get flycheck-level 'flycheck-fringe-bitmaps)
101+
(get flycheck-level 'flycheck-fringe-bitmap-double-arrow))))
102+
(flycheck-define-error-level new-level
103+
:severity (get flycheck-level 'flycheck-error-severity)
104+
:compilation-level (get flycheck-level 'flycheck-compilation-level)
105+
:overlay-category category
106+
:fringe-bitmap bitmap
107+
:fringe-face (get flycheck-level 'flycheck-fringe-face)
108+
:error-list-face face)
109+
new-level))))
110+
111+
(defun lsp-diagnostics--flycheck-calculate-level (severity tags)
112+
"Calculate flycheck level by SEVERITY and TAGS."
113+
(let ((level (pcase severity
114+
(1 'error)
115+
(2 'warning)
116+
(3 'info)
117+
(4 'info)
118+
(_ lsp-flycheck-default-level)))
119+
;; materialize only first tag.
120+
(tags (seq-map (lambda (tag)
121+
(cond
122+
((= tag lsp/diagnostic-tag-unnecessary) 'unnecessary)
123+
((= tag lsp/diagnostic-tag-deprecated) 'deprecated)))
124+
tags)))
125+
(if tags
126+
(lsp-diagnostics--flycheck-level level tags)
127+
level)))
128+
129+
(defun lsp-diagnostics--flycheck-start (checker callback)
130+
"Start an LSP syntax check with CHECKER.
131+
132+
CALLBACK is the status callback passed by Flycheck."
133+
134+
(remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t)
135+
136+
(->> (lsp--get-buffer-diagnostics)
137+
(-map (-lambda ((&Diagnostic :message :severity? :tags? :code?
138+
:range (&Range :start (&Position :line start-line
139+
:character start-character)
140+
:end (&Position :line end-line
141+
:character end-character))))
142+
(flycheck-error-new
143+
:buffer (current-buffer)
144+
:checker checker
145+
:filename buffer-file-name
146+
:message message
147+
:level (lsp-diagnostics--flycheck-calculate-level severity? tags?)
148+
:id code?
149+
:line (lsp-translate-line (1+ start-line))
150+
:column (1+ (lsp-translate-column start-character))
151+
:end-line (lsp-translate-line (1+ end-line))
152+
:end-column (1+ (lsp-translate-column end-character)))))
153+
(funcall callback 'finished)))
154+
155+
(defun lsp-diagnostics--flycheck-buffer ()
156+
"Trigger flyckeck on buffer."
157+
(remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t)
158+
(flycheck-buffer))
159+
160+
(defun lsp-diagnostics--flycheck-report ()
161+
"Report flycheck.
162+
This callback is invoked when new diagnostics are received
163+
from the language server."
164+
(when (and (or (memq 'idle-change flycheck-check-syntax-automatically)
165+
(and (memq 'save flycheck-check-syntax-automatically)
166+
(not (buffer-modified-p))))
167+
lsp--cur-workspace)
168+
;; make sure diagnostics are published even if the diagnostics
169+
;; have been received after idle-change has been triggered
170+
(->> lsp--cur-workspace
171+
(lsp--workspace-buffers)
172+
(mapc (lambda (buffer)
173+
(when (lsp-buffer-live-p buffer)
174+
(lsp-with-current-buffer buffer
175+
(add-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer nil t)
176+
(lsp--idle-reschedule (current-buffer)))))))))
177+
178+
(defun lsp-diagnostics--flycheck-enable (&rest _)
179+
"Enable flycheck integration for the current buffer."
180+
(flycheck-define-generic-checker 'lsp
181+
"A syntax checker using the Language Server Protocol (LSP)
182+
provided by lsp-mode.
183+
See https://github.com/emacs-lsp/lsp-mode."
184+
:start #'lsp-diagnostics--flycheck-start
185+
:modes '(lsp-placeholder-mode) ;; placeholder
186+
:predicate (lambda () lsp-mode)
187+
:error-explainer (lambda (e)
188+
(cond ((string-prefix-p "clang-tidy" (flycheck-error-message e))
189+
(lsp-cpp-flycheck-clang-tidy-error-explainer e))
190+
(t (flycheck-error-message e)))))
191+
(flycheck-mode 1)
192+
(setq-local flycheck-checker 'lsp)
193+
(lsp-flycheck-add-mode major-mode)
194+
(add-to-list 'flycheck-checkers 'lsp)
195+
(add-hook 'lsp-diagnostics-updated-hook #'lsp-diagnostics--flycheck-report nil t)
196+
(add-hook 'lsp-managed-mode-hook #'lsp-diagnostics--flycheck-report nil t))
197+
198+
199+
;; Flymake integration
200+
201+
(declare-function flymake-mode "ext:flymake")
202+
(declare-function flymake-make-diagnostic "ext:flymake")
203+
(declare-function flymake-diag-region "ext:flymake")
204+
205+
(defvar flymake-diagnostic-functions)
206+
(defvar flymake-mode)
207+
(defvar-local lsp-diagnostics--flymake-report-fn nil)
208+
209+
(defun lsp-diagnostics--flymake-setup ()
210+
"Setup flymake."
211+
(setq lsp-diagnostics--flymake-report-fn nil)
212+
(flymake-mode 1)
213+
(add-hook 'flymake-diagnostic-functions 'lsp-diagnostics--flymake-backend nil t)
214+
(add-hook 'lsp-diagnostics-updated-hook 'lsp-diagnostics--flymake-after-diagnostics nil t))
215+
216+
(defun lsp-diagnostics--flymake-after-diagnostics ()
217+
"Handler for `lsp-diagnostics-updated-hook'."
218+
(cond
219+
((and lsp-diagnostics--flymake-report-fn flymake-mode)
220+
(lsp-diagnostics--flymake-update-diagnostics))
221+
((not flymake-mode)
222+
(setq lsp-diagnostics--flymake-report-fn nil))))
223+
224+
(defun lsp-diagnostics--flymake-backend (report-fn &rest _args)
225+
"Flymake backend using REPORT-FN."
226+
(let ((first-run (null lsp-diagnostics--flymake-report-fn)))
227+
(setq lsp-diagnostics--flymake-report-fn report-fn)
228+
(when first-run
229+
(lsp-diagnostics--flymake-update-diagnostics))))
230+
231+
(defun lsp-diagnostics--flymake-update-diagnostics ()
232+
"Report new diagnostics to flymake."
233+
(funcall lsp-diagnostics--flymake-report-fn
234+
(-some->> (lsp-diagnostics t)
235+
(gethash (lsp--fix-path-casing buffer-file-name))
236+
(--map (-let* (((&Diagnostic :message :severity?
237+
:range (range &as &Range
238+
:start (&Position :line start-line :character)
239+
:end (&Position :line end-line))) it)
240+
((start . end) (lsp--range-to-region range)))
241+
(when (= start end)
242+
(if-let ((region (flymake-diag-region (current-buffer)
243+
(1+ start-line)
244+
character)))
245+
(setq start (car region)
246+
end (cdr region))
247+
(lsp-save-restriction-and-excursion
248+
(goto-char (point-min))
249+
(setq start (point-at-bol (1+ start-line))
250+
end (point-at-eol (1+ end-line))))))
251+
(flymake-make-diagnostic (current-buffer)
252+
start
253+
end
254+
(cl-case severity?
255+
(1 :error)
256+
(2 :warning)
257+
(t :note))
258+
message))))
259+
;; This :region keyword forces flymake to delete old diagnostics in
260+
;; case the buffer hasn't changed since the last call to the report
261+
;; function. See https://github.com/joaotavora/eglot/issues/159
262+
:region (cons (point-min) (point-max))))
263+
264+
265+
266+
;;;###autoload
267+
(defun lsp-diagnostics--enable ()
268+
"Enable LSP checker support."
269+
(when (member lsp-diagnostics-provider '(:auto :none :flycheck :flymake t nil))
270+
(lsp-diagnostics-mode 1)))
271+
272+
(defun lsp-diagnostics--disable ()
273+
"Disable LSP checker support."
274+
(lsp-diagnostics-mode -1))
275+
276+
;;;###autoload
277+
(define-minor-mode lsp-diagnostics-mode
278+
"Toggle LSP diagnostics integration."
279+
:group 'lsp-mode
280+
:global nil
281+
:lighter ""
282+
(cond
283+
(lsp-diagnostics-mode
284+
(cond
285+
((or
286+
(and (eq lsp-diagnostics-provider :auto)
287+
(functionp 'flycheck-mode))
288+
(and (eq lsp-diagnostics-provider :flycheck)
289+
(or (functionp 'flycheck-mode)
290+
(user-error "The lsp-diagnostics-provider is set to :flycheck but flycheck is not installed?")))
291+
;; legacy
292+
(null lsp-diagnostics-provider))
293+
(lsp-diagnostics--flycheck-enable))
294+
((or (eq lsp-diagnostics-provider :auto)
295+
(eq lsp-diagnostics-provider :flymake)
296+
(eq lsp-diagnostics-provider t))
297+
(require 'flymake)
298+
(lsp-diagnostics--flymake-setup))
299+
((not (eq lsp-diagnostics-provider :none))
300+
(lsp--warn "Unable to autoconfigure flycheck/flymake. The diagnostics won't be rendered.")))
301+
302+
(add-hook 'lsp-unconfigure-hook #'lsp-diagnostics--disable nil t))
303+
(t
304+
(remove-hook 'lsp-unconfigure-hook #'lsp-diagnostics--disable t))))
305+
306+
;;;###autoload
307+
(add-hook 'lsp-configure-hook (lambda ()
308+
(when lsp-auto-configure
309+
(lsp-diagnostics--enable))))
310+
311+
(provide 'lsp-diagnostics)
312+
;;; lsp-diagnostics.el ends here

0 commit comments

Comments
 (0)