@@ -147,35 +147,106 @@ g. `error', `warning') and list of LSP TAGS."
147
147
(lsp-diagnostics--flycheck-level level tags)
148
148
level)))
149
149
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
+
150
191
(defun lsp-diagnostics--flycheck-start (checker callback )
151
192
" Start an LSP syntax check with CHECKER.
152
193
153
194
CALLBACK is the status callback passed by Flycheck."
154
195
155
196
(remove-hook 'lsp-on-idle-hook #'lsp-diagnostics--flycheck-buffer t )
156
197
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)))
179
250
180
251
(defun lsp-diagnostics--flycheck-buffer ()
181
252
" Trigger flyckeck on buffer."
@@ -260,6 +331,7 @@ See https://github.com/emacs-lsp/lsp-mode."
260
331
(declare-function flymake-diag-region " ext:flymake" )
261
332
262
333
(defvar flymake-diagnostic-functions )
334
+ (defvar flymake-list-only-diagnostics )
263
335
(defvar flymake-mode )
264
336
(defvar-local lsp-diagnostics--flymake-report-fn nil )
265
337
@@ -285,38 +357,105 @@ See https://github.com/emacs-lsp/lsp-mode."
285
357
(when first-run
286
358
(lsp-diagnostics--flymake-update-diagnostics))))
287
359
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
+
288
406
(defun lsp-diagnostics--flymake-update-diagnostics ()
289
407
" 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 )))))
320
459
321
460
322
461
0 commit comments