Skip to content

Commit cfb2e19

Browse files
committed
Add support for range diagnostics under cursor
1 parent 04428c9 commit cfb2e19

File tree

3 files changed

+112
-4
lines changed

3 files changed

+112
-4
lines changed

autoload/lsp/internal/diagnostics/under_cursor.vim

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,29 @@ function! lsp#internal#diagnostics#under_cursor#get_diagnostic(...) abort
2929
let l:line = line('.')
3030
let l:col = col('.')
3131

32+
return lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, l:line, l:col)
33+
endfunction
34+
35+
" Returns a diagnostic object, or empty dictionary if no diagnostics are
36+
" available.
37+
function! lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(diagnostics, line, col) abort
3238
let l:closest_diagnostic = {}
3339
let l:closest_distance = -1
40+
let l:closest_end_col = -1
3441

35-
for l:diagnostic in l:diagnostics
42+
for l:diagnostic in a:diagnostics
3643
let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['start'])
44+
let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['end'])
3745

38-
if l:line == l:start_line
39-
let l:distance = abs(l:start_col - l:col)
46+
if (a:line > l:start_line || (a:line == l:start_line && a:col >= l:start_col)) &&
47+
\ (a:line < l:end_line || (a:line == l:end_line && a:col < l:end_col))
48+
let l:distance = abs(l:start_col - a:col)
4049
if l:closest_distance < 0 || l:distance < l:closest_distance
50+
let l:closest_end_col = l:end_col
4151
let l:closest_diagnostic = l:diagnostic
4252
let l:closest_distance = l:distance
4353
endif
4454
endif
4555
endfor
46-
4756
return l:closest_diagnostic
4857
endfunction
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
Describe lsp#internal#diagnostics#under_cursor
2+
" refer to lsp#test#projectdir('go') . '/documentdiagnostics.go'
3+
4+
It should not trigger diagnostics when cursor is outside diagnostic range
5+
let l:diagnostics = [
6+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
7+
\ ]
8+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 1, 1), {})
9+
End
10+
11+
It should trigger diagnostic when cursor is exactly at start position
12+
let l:diagnostics = [
13+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
14+
\ ]
15+
let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
16+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 13), l:expected)
17+
End
18+
19+
It should not trigger diagnostic when cursor is at line before start position
20+
let l:diagnostics = [
21+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
22+
\ ]
23+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 6, 13), {})
24+
End
25+
26+
It should not trigger diagnostic when cursor is one character before start position
27+
let l:diagnostics = [
28+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
29+
\ ]
30+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 12), {})
31+
End
32+
33+
It should trigger diagnostic when cursor is at start column on an intermediate line within range
34+
let l:diagnostics = [
35+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
36+
\ ]
37+
let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
38+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 8, 1), l:expected)
39+
End
40+
41+
It should trigger diagnostic when cursor is at end column on an intermediate line within range
42+
let l:diagnostics = [
43+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
44+
\ ]
45+
let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
46+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 8, 15), l:expected)
47+
End
48+
49+
It should trigger diagnostic when cursor is exactly at end position
50+
let l:diagnostics = [
51+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
52+
\ ]
53+
let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
54+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 9, 5), l:expected)
55+
End
56+
57+
It should not trigger diagnostic when cursor is at line after end position
58+
let l:diagnostics = [
59+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
60+
\ ]
61+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 10, 5), {})
62+
End
63+
64+
It should not return diagnostic when cursor is one character after end position
65+
let l:diagnostics = [
66+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
67+
\ ]
68+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 9, 6), {})
69+
End
70+
71+
It should return the closest diagnostic when multiple diagnostics exist across different ranges
72+
let l:diagnostics = [
73+
\ { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 7, 'line': 10}} },
74+
\ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} }
75+
\ ]
76+
let l:expected = { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 7, 'line': 10}} }
77+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 3), l:expected)
78+
End
79+
80+
It should return the most specific diagnostic when multiple diagnostics overlap at cursor position
81+
let l:diagnostics = [
82+
\ { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 15, 'line': 4}} },
83+
\ { 'range': {'start': {'character': 12, 'line': 4}, 'end': {'character': 14, 'line': 4}} }
84+
\ ]
85+
let l:expected = { 'range': {'start': {'character': 12, 'line': 4}, 'end': {'character': 14, 'line': 4}} }
86+
Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 5, 13), l:expected)
87+
End
88+
End
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func documentdiagnostics() {
6+
msg := "msg"
7+
fmt.Printf(msg +
8+
msg +
9+
msg,
10+
)
11+
}

0 commit comments

Comments
 (0)