@@ -147,35 +147,106 @@ g. `error', `warning') and list of LSP TAGS."
147147 (lsp-diagnostics--flycheck-level level tags)
148148 level)))
149149
150+ (lsp-defun lsp-diagnostics--flycheck-range-to-region
151+ ((range &as &Range
152+ :start (start &as &Position
153+ :line start-line
154+ :character start-column)
155+ :end (end &as &Position
156+ :line end-line
157+ :character end-column))
158+ external)
159+ " Determine diagnostic region from RANGE."
160+ (if external
161+ (let ((start-line (1+ start-line))
162+ (start-column (1+ start-column))
163+ (end-line (1+ end-line))
164+ (end-column (1+ end-column)))
165+ (if (lsp--position-equal start end)
166+ (if (= start-column 1 )
167+ ; ; Highlight entire line
168+ (cons (cons start-line nil )
169+ (cons start-line nil ))
170+ ; ; Approximate using `flycheck-highlighting-mode'
171+ (cons (cons start-line start-column)
172+ (cons start-line nil )))
173+ (cons (cons start-line start-column)
174+ (cons end-line end-column))))
175+ ; ; Diagnostic in current buffer
176+ (let ((start-line (lsp-translate-line (1+ start-line)))
177+ (start-column (1+ (lsp-translate-column start-column)))
178+ (end-line (lsp-translate-line (1+ end-line)))
179+ (end-column (1+ (lsp-translate-column end-column))))
180+ (if (lsp--position-equal start end)
181+ (if (= start-column 1 )
182+ ; ; Highlight entire line
183+ (cons (cons start-line nil )
184+ (cons start-line nil ))
185+ ; ; Approximate using `flycheck-highlighting-mode'
186+ (cons (cons start-line start-column)
187+ (cons start-line nil )))
188+ (cons (cons start-line start-column)
189+ (cons end-line end-column))))))
190+
150191(defun lsp-diagnostics--flycheck-start (checker callback )
151192 " Start an LSP syntax check with CHECKER.
152193
153194CALLBACK is the status callback passed by Flycheck."
154195
155196 (remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t )
156197
157- (->> (lsp--get-buffer-diagnostics)
158- (-map (-lambda ((&Diagnostic :message :severity? :tags? :code? :source?
159- :range (&Range :start (start &as &Position
160- :line start-line
161- :character start-character)
162- :end (end &as &Position
163- :line end-line
164- :character end-character))))
165- (flycheck-error-new
166- :buffer (current-buffer )
167- :checker checker
168- :filename buffer-file-name
169- :message message
170- :level (lsp-diagnostics--flycheck-calculate-level severity? tags?)
171- :id code?
172- :group source?
173- :line (lsp-translate-line (1+ start-line))
174- :column (1+ (lsp-translate-column start-character))
175- :end-line (lsp-translate-line (1+ end-line))
176- :end-column (unless (lsp--position-equal start end)
177- (1+ (lsp-translate-column end-character))))))
178- (funcall callback 'finished )))
198+ (let ((diagnostics nil )
199+ (path (lsp--fix-path-casing buffer-file-name)))
200+ (seq-doseq (diagnostic (lsp--get-buffer-diagnostics))
201+ (-let* (((&Diagnostic :message :severity? :tags? :code?
202+ :source? :related-information?
203+ :range ) diagnostic)
204+ (level (lsp-diagnostics--flycheck-calculate-level severity? tags?) )
205+ (checker (if (stringp source?) (intern source?) checker))
206+ (group (gensym )))
207+ (-let ((((start-line . start-column) .
208+ (end-line . end-column))
209+ (lsp-diagnostics--flycheck-range-to-region range nil )))
210+ (push
211+ (flycheck-error-new
212+ :buffer (current-buffer )
213+ :checker checker
214+ :filename buffer-file-name
215+ :message message
216+ :level level
217+ :id code?
218+ :group group
219+ :line start-line
220+ :column start-column
221+ :end-line end-line
222+ :end-column end-column)
223+ diagnostics))
224+ (seq-doseq (related-info related-information?)
225+ (-let* (((&DiagnosticRelatedInformation
226+ :message :location (&Location :range :uri )) related-info)
227+ (related-file (lsp--fix-path-casing (lsp--uri-to-path uri)))
228+ (external (not (equal path related-file)))
229+ (((start-line . start-column) .
230+ (end-line . end-column))
231+ (lsp-diagnostics--flycheck-range-to-region range external)))
232+ (push
233+ (flycheck-error-new
234+ :buffer (current-buffer )
235+ :checker checker
236+ :filename related-file
237+ :message message
238+ :level level
239+ :id code?
240+ :group group
241+ :line start-line
242+ :column start-column
243+ :end-line end-line
244+ :end-column end-column)
245+ diagnostics)))))
246+
247+ ; ; Refresh diagnostics
248+ (setq diagnostics (nreverse diagnostics))
249+ (funcall callback 'finished diagnostics)))
179250
180251(defun lsp-diagnostics--flycheck-buffer ()
181252 " Trigger flyckeck on buffer."
@@ -260,6 +331,7 @@ See https://github.com/emacs-lsp/lsp-mode."
260331(declare-function flymake-diag-region " ext:flymake" )
261332
262333(defvar flymake-diagnostic-functions )
334+ (defvar flymake-list-only-diagnostics )
263335(defvar flymake-mode )
264336(defvar-local lsp-diagnostics--flymake-report-fn nil )
265337
@@ -285,38 +357,105 @@ See https://github.com/emacs-lsp/lsp-mode."
285357 (when first-run
286358 (lsp-diagnostics--flymake-update-diagnostics))))
287359
360+ (defun lsp-diagnostics--flymake-calculate-level (severity? )
361+ " Determine SEVERITY mapping, defaulting to error."
362+
363+ (when (stringp severity?)
364+ (setq severity? (string-to-number severity?) ))
365+
366+ (pcase severity?
367+ ((pred null ) :error )
368+ ((pred (= lsp/diagnostic-severity-error)) :error )
369+ ((pred (= lsp/diagnostic-severity-warning)) :warning )
370+ ((pred (= lsp/diagnostic-severity-information)) :note )
371+ ((pred (= lsp/diagnostic-severity-hint)) :note )
372+ (_ :error )))
373+
374+ (lsp-defun lsp-diagnostics--flymake-range-to-region
375+ ((range &as &Range
376+ :start (start &as &Position
377+ :line start-line
378+ :character start-column)
379+ :end (end &as &Position
380+ :line end-line
381+ :character end-column))
382+ external)
383+ " Determine diagnostic region from RANGE."
384+ (let ((start-line (1+ start-line))
385+ (end-line (1+ end-line)))
386+ (if external
387+ (cons (cons start-line start-column)
388+ (cons end-line end-column))
389+ (if (lsp--position-equal start end)
390+ (if-let ((region (flymake-diag-region (current-buffer )
391+ start-line
392+ start-column)))
393+ (cons (car region ) (cdr region ))
394+ (lsp-save-restriction-and-excursion
395+ (goto-char (point-min ))
396+ (cons (line-beginning-position start-line)
397+ (line-end-position end-line))))
398+ (lsp--range-to-region range)))))
399+
400+ (defun lsp-diagnostics--flymake-message (message code? source? )
401+ " Construct diagnostic message with MESSAGE, CODE and SOURCE."
402+ (let* ((code (and code? (format " [%s ] " code?) ))
403+ (source (and source? (format " (%s ) " source?) )))
404+ (concat message code source )))
405+
288406(defun lsp-diagnostics--flymake-update-diagnostics ()
289407 " Report new diagnostics to flymake."
290- (funcall lsp-diagnostics--flymake-report-fn
291- (-some->> (lsp-diagnostics t )
292- (gethash (lsp--fix-path-casing buffer-file-name))
293- (--map (-let* (((&Diagnostic :message :severity?
294- :range (range &as &Range
295- :start (&Position :line start-line :character )
296- :end (&Position :line end-line))) it)
297- ((start . end) (lsp--range-to-region range)))
298- (when (= start end)
299- (if-let ((region (flymake-diag-region (current-buffer )
300- (1+ start-line)
301- character)))
302- (setq start (car region )
303- end (cdr region ))
304- (lsp-save-restriction-and-excursion
305- (goto-char (point-min ))
306- (setq start (line-beginning-position (1+ start-line))
307- end (line-end-position (1+ end-line))))))
308- (flymake-make-diagnostic (current-buffer )
309- start
310- end
311- (cl-case severity?
312- (1 :error )
313- (2 :warning )
314- (t :note ))
315- message))))
316- ; ; This :region keyword forces flymake to delete old diagnostics in
317- ; ; case the buffer hasn't changed since the last call to the report
318- ; ; function. See https://github.com/joaotavora/eglot/issues/159
319- :region (cons (point-min ) (point-max ))))
408+ (let ((foreign-diagnostics (ht-create))
409+ (domestic-diagnostics nil )
410+ (path (lsp--fix-path-casing buffer-file-name)))
411+
412+ ; ; Remove any "foreign" diagnostics which may have existed prior
413+ ; ; to this buffer having been loaded.
414+ (setq flymake-list-only-diagnostics
415+ (assoc-delete-all path flymake-list-only-diagnostics))
416+
417+ (seq-doseq (diagnostic (lsp--get-buffer-diagnostics))
418+ (-let* (((&Diagnostic :message :severity? :code?
419+ :source? :related-information?
420+ :range ) diagnostic)
421+ (level (lsp-diagnostics--flymake-calculate-level severity?) )
422+ (message (lsp-diagnostics--flymake-message message code? source?) ))
423+ (-let (((start . end)
424+ (lsp-diagnostics--flymake-range-to-region range nil )))
425+ (push
426+ (flymake-make-diagnostic (current-buffer ) start end level message)
427+ domestic-diagnostics))
428+ (seq-doseq (related-info related-information?)
429+ (-let* (((&DiagnosticRelatedInformation
430+ :message :location (&Location :range :uri )) related-info)
431+ (related-file (lsp--fix-path-casing (lsp--uri-to-path uri)))
432+ (external (not (equal path related-file)))
433+ ((start . end)
434+ (lsp-diagnostics--flymake-range-to-region range external))
435+ (message (lsp-diagnostics--flymake-message message code? source?) ))
436+ (if external
437+ (push
438+ (flymake-make-diagnostic related-file start end level message)
439+ (gethash related-file foreign-diagnostics))
440+ (push
441+ (flymake-make-diagnostic (current-buffer ) start end level message)
442+ domestic-diagnostics))))))
443+
444+ ; ; Refresh foreign diagnostics
445+ (maphash
446+ (lambda (foreign-file diagnostics )
447+ (setq flymake-list-only-diagnostics
448+ (assoc-delete-all foreign-file flymake-list-only-diagnostics))
449+ (push (cons foreign-file diagnostics) flymake-list-only-diagnostics))
450+ foreign-diagnostics)
451+
452+ ; ; Refresh domestic diagnostics
453+ (funcall lsp-diagnostics--flymake-report-fn
454+ domestic-diagnostics
455+ ; ; This :region keyword forces flymake to delete old diagnostics in
456+ ; ; case the buffer hasn't changed since the last call to the report
457+ ; ; function. See https://github.com/joaotavora/eglot/issues/159
458+ :region (cons (point-min ) (point-max )))))
320459
321460
322461
0 commit comments