Skip to content

Commit d70c8bb

Browse files
authored
Merge pull request #3415 from bhcleek/diagnostics/hover
tool: display LSP diagnostics in hover balloon
2 parents f4dcf11 + 7c905b4 commit d70c8bb

File tree

1 file changed

+103
-22
lines changed

1 file changed

+103
-22
lines changed

autoload/go/lsp.vim

+103-22
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,8 @@ function! s:newlsp() abort
277277
if l:level < l:diag.severity
278278
continue
279279
endif
280-
let [l:error, l:matchpos] = s:errorFromDiagnostic(l:diag, l:bufname, l:fname)
281-
let l:diagnostics = add(l:diagnostics, l:error)
280+
let [l:diagnostic, l:matchpos] = s:processDiagnostic(l:diag, l:bufname, l:fname)
281+
let l:diagnostics = add(l:diagnostics, l:diagnostic)
282282

283283
if empty(l:matchpos)
284284
continue
@@ -973,35 +973,68 @@ function! go#lsp#Hover(fname, line, col, handler) abort
973973
let l:lsp = s:lspfactory.get()
974974
let l:msg = go#lsp#message#Hover(a:fname, a:line, a:col)
975975
let l:state = s:newHandlerState('')
976-
let l:state.handleResult = funcref('s:hoverHandler', [function(a:handler, [], l:state)], l:state)
977-
let l:state.error = funcref('s:noop')
976+
let l:diagnosticMsg = ''
977+
978+
if has_key(l:lsp.diagnostics, a:fname)
979+
for l:diagnostic in l:lsp.diagnostics[a:fname]
980+
if !s:within(l:diagnostic.range, a:line, a:col)
981+
continue
982+
endif
983+
984+
let l:diagnosticMsg = l:diagnostic.message
985+
break
986+
endfor
987+
endif
988+
let l:state.handleResult = funcref('s:hoverHandler', [function(a:handler, [], l:state), l:diagnosticMsg], l:state)
989+
let l:state.error = funcref('s:hoverError', [function(a:handler, [], l:state), l:diagnosticMsg], l:state)
978990
return l:lsp.sendMessage(l:msg, l:state)
979991
endfunction
980992

981-
function! s:hoverHandler(next, msg) abort dict
993+
function! s:hoverHandler(next, diagnostic, msg) abort dict
982994
if a:msg is v:null || !has_key(a:msg, 'contents')
995+
if len(a:diagnostic) > 0
996+
call call(a:next, [a:diagnostic])
997+
endif
983998
return
984999
endif
9851000

9861001
try
9871002
let l:value = json_decode(a:msg.contents.value)
9881003

1004+
let l:msg = []
1005+
if len(a:diagnostic) > 0
1006+
let l:msg = split(a:diagnostic, "\n")
1007+
let l:msg = add(l:msg, '')
1008+
endif
9891009
let l:signature = split(l:value.signature, "\n")
990-
let l:msg = l:signature
1010+
let l:msg = extend(l:msg, l:signature)
9911011
if go#config#DocBalloon()
9921012
" use synopsis instead of fullDocumentation to keep the hover window
9931013
" small.
9941014
let l:doc = l:value.synopsis
9951015
if len(l:doc) isnot 0
996-
let l:msg = l:signature + ['', l:doc]
1016+
let l:msg = extend(l:msg, ['', l:doc])
9971017
endif
9981018
endif
1019+
9991020
call call(a:next, [l:msg])
10001021
catch
10011022
" TODO(bc): log the message and/or show an error message.
10021023
endtry
10031024
endfunction
10041025

1026+
function! s:hoverError(next, diagnostic, msg) abort dict
1027+
try
1028+
if len(a:diagnostic) > 0
1029+
let l:msg = split(a:diagnostic, "\n")
1030+
call call(a:next, [l:msg])
1031+
endif
1032+
catch
1033+
endtry
1034+
1035+
return
1036+
endfunction
1037+
10051038
function! go#lsp#Doc() abort
10061039
let l:fname = expand('%:p')
10071040
let [l:line, l:col] = go#lsp#lsp#Position()
@@ -1379,7 +1412,7 @@ function! go#lsp#Diagnostics(...) abort
13791412

13801413
for l:arg in a:000
13811414
if l:arg == l:pkg || l:arg == 'all'
1382-
let l:diagnostics = extend(l:diagnostics, l:val)
1415+
let l:diagnostics = extend(l:diagnostics, map(l:val, 'v:val["error"]'))
13831416
endif
13841417
endfor
13851418
endfor
@@ -1399,7 +1432,7 @@ function! go#lsp#AnalyzeFile(fname) abort
13991432

14001433
let l:version = get(l:lsp.fileVersions, a:fname, 0)
14011434
if l:version == getbufvar(a:fname, 'changedtick')
1402-
return l:lastdiagnostics
1435+
return map(l:lastdiagnostics, 'v:val["error"]')
14031436
endif
14041437

14051438
call go#lsp#DidChange(a:fname)
@@ -1410,47 +1443,75 @@ function! go#lsp#AnalyzeFile(fname) abort
14101443
endfunction
14111444

14121445
function! s:setDiagnostics(...) abort
1413-
return a:000
1446+
return map(a:000, 'v:val["error"]')
14141447
endfunction
14151448

1416-
" s:processDiagnostic converts a diagnostic into an error string. It returns
1417-
" the errors string and the match position described in the diagnostic. The
1418-
" match position will be an empty list when bufname is not a valid name for
1419-
" the current buffer.
1420-
function! s:errorFromDiagnostic(diagnostic, bufname, fname) abort
1449+
" s:processDiagnostic converts a diagnostic from LSP into useful values for
1450+
" Vim. It returns the a value with the original message, the diagnostic range
1451+
" as expressed by LSP, an error string, and the Vim match position described
1452+
" in the diagnostic. The match position will be an empty list when bufname is
1453+
" not a valid name for the current buffer.
1454+
function! s:processDiagnostic(diagnostic, bufname, fname) abort
14211455
let l:range = a:diagnostic.range
14221456

1457+
let l:diagnostic = {
1458+
\ "message": a:diagnostic.message,
1459+
\ "range": {
1460+
\ "start": {
1461+
\ "line": l:range.start.line,
1462+
\ "character": l:range.start.character,
1463+
\ },
1464+
\ "end": {
1465+
\ "line": l:range.end.line,
1466+
\ "character": l:range.end.character,
1467+
\ },
1468+
\ },
1469+
\ }
1470+
14231471
let l:line = l:range.start.line + 1
1472+
let l:endline = l:range.end.line + 1
1473+
14241474
let l:buflines = getbufline(a:bufname, l:line)
14251475
let l:col = ''
14261476
if len(l:buflines) > 0
14271477
let l:col = go#lsp#lsp#PositionOf(l:buflines[0], l:range.start.character)
14281478
endif
1429-
let l:error = printf('%s:%s:%s:%s: %s', a:fname, l:line, l:col, go#lsp#lsp#SeverityToErrorType(a:diagnostic.severity), a:diagnostic.message)
1479+
1480+
let l:severity = go#lsp#lsp#SeverityToErrorType(a:diagnostic.severity)
1481+
let l:diagnostic.error = printf('%s:%s:%s:%s: %s', a:fname, l:line, l:col, l:severity, l:diagnostic.message)
14301482

14311483
if !(a:diagnostic.severity == 1 || a:diagnostic.severity == 2)
1432-
return [l:error, []]
1484+
return [l:diagnostic, []]
14331485
endif
14341486

14351487
" return when the diagnostic is not for the current buffer.
14361488
if bufnr(a:bufname) != bufnr('')
1437-
return [l:error, []]
1489+
return [l:diagnostic, []]
14381490
end
14391491

1440-
let l:endline = l:range.end.line + 1
14411492
" don't bother trying to highlight errors or warnings that span
14421493
" the whole file (e.g when there's missing package documentation).
14431494
if l:line == 1 && (l:endline) == line('$')
1444-
return [l:error, []]
1495+
return [l:diagnostic, []]
1496+
endif
1497+
1498+
if len(l:buflines) == 0
1499+
return [l:diagnostic, []]
1500+
endif
1501+
1502+
let l:buflines = getbufline(a:bufname, l:endline)
1503+
if len(l:buflines) > 0
1504+
let l:endcol = go#lsp#lsp#PositionOf(l:buflines[0], l:range.end.character)
1505+
else
1506+
return [l:diagnostic, []]
14451507
endif
1446-
let l:endcol = go#lsp#lsp#PositionOf(getline(l:endline), l:range.end.character)
14471508

14481509
" the length of the match is the number of bytes between the start of
14491510
" the match and the end of the match.
14501511
let l:matchLength = line2byte(l:endline) + l:endcol - (line2byte(l:line) + l:col)
14511512
let l:pos = [l:line, l:col, l:matchLength]
14521513

1453-
return [l:error, l:pos]
1514+
return [l:diagnostic, l:pos]
14541515
endfunction
14551516

14561517
function! s:highlightMatches(errorMatches, warningMatches) abort
@@ -1947,6 +2008,26 @@ function! s:lineinfile(fname, line) abort
19472008
endtry
19482009
endfunction
19492010

2011+
function! s:within(range, line, character) abort
2012+
if a:line < a:range.start.line
2013+
return 0
2014+
endif
2015+
2016+
if a:line > a:range.end.line
2017+
return 0
2018+
endif
2019+
2020+
if a:line == a:range.start.line && a:character < a:range.start.character
2021+
return 0
2022+
endif
2023+
2024+
if a:line == a:range.end.line && a:character > a:range.end.character
2025+
return 0
2026+
endif
2027+
2028+
return 1
2029+
endfunction
2030+
19502031
" restore Vi compatibility settings
19512032
let &cpo = s:cpo_save
19522033
unlet s:cpo_save

0 commit comments

Comments
 (0)