@@ -1004,21 +1004,22 @@ function! s:hoverHandler(next, diagnostic, msg) abort dict
10041004 endif
10051005
10061006 try
1007- let l: value = json_decode (a: msg .contents.value)
1008-
10091007 let l: msg = []
10101008 if len (a: diagnostic ) > 0
1011- let l: msg = split (a: diagnostic , " \n " )
1009+ let l: msg = split (a: diagnostic , ' \n ' )
10121010 let l: msg = add (l: msg , ' ' )
10131011 endif
1014- let l: signature = split (l: value .signature, " \n " )
1015- let l: msg = extend (l: msg , l: signature )
1012+
1013+ let l: value = s: structuredHoverResult (a: msg .contents)
1014+
1015+ let l: msg = extend (l: msg , l: value .signature)
10161016 if go#config#DocBalloon ()
10171017 " use synopsis instead of fullDocumentation to keep the hover window
10181018 " small.
10191019 let l: doc = l: value .synopsis
10201020 if len (l: doc ) isnot 0
1021- let l: msg = extend (l: msg , [' ' , l: doc ])
1021+ let l: msg = add (l: msg , ' ' )
1022+ let l: msg = extend (l: msg , l: doc )
10221023 endif
10231024 endif
10241025
@@ -1065,51 +1066,122 @@ function! s:docFromHoverResult(msg) abort dict
10651066 return [' Undocumented' , 0 ]
10661067 endif
10671068
1068- let l: value = json_decode (a: msg .contents.value )
1069- let l: doc = l: value .fullDocumentation
1069+ let l: value = s: structuredHoverResult (a: msg .contents)
1070+ let l: doc = join ( l: value .fullDocumentation, " \n " )
10701071 if len (l: doc ) is 0
10711072 let l: doc = ' Undocumented'
10721073 endif
1073- let l: content = printf (" %s\n\n %s" , l: value .signature, l: doc )
1074+ let l: content = printf (" %s\n\n %s" , join ( l: value .signature, " \n " ) , l: doc )
10741075 return [l: content , 0 ]
10751076endfunction
10761077
1078+ function ! s: structuredHoverResult (contents) abort
1079+ let l: value = {
1080+ \ ' fullDocumentation' : [],
1081+ \ ' synopsis' : ' ' ,
1082+ \ ' signature' : [],
1083+ \ ' singleLine' : ' ' ,
1084+ \ ' symbolName' : ' ' ,
1085+ \ ' linkPath' : ' ' ,
1086+ \ ' linkAnchor' : ' '
1087+ \ }
1088+
1089+ if ! has_key (a: contents , ' value' )
1090+ return l: value
1091+ endif
1092+
1093+ let l: contents = a: contents .value
1094+
1095+ " The signature is either a multiline identifier (e.g. a struct or an
1096+ " interface and will therefore end with a } on a line by itself or it's a
1097+ " single line identifier. Check for the former first and fallback to the
1098+ " latter.
1099+ let l: parts = split (l: contents , ' \n}\zs\n' )
1100+ if len (l: parts ) is 1
1101+ let l: parts = split (l: contents , ' \n' )
1102+ else
1103+ let l: parts = extend ([l: parts [0 ]], split (l: parts [1 ], ' \n' ))
1104+ endif
1105+
1106+ " The first part will have been the signature. If it was a multiline
1107+ " signature, then it was split on '\n}\zs\n' and needs to be split again
1108+ " on '\n'. If it was a single line signature, then there's no harm in
1109+ " splitting on '\n' again. Either way, a list is returned from split.
1110+ let l: value .signature = split (l: parts [0 ], ' \n' )
1111+
1112+ if len (l: value .signature) is 1
1113+ let l: value .singleLine = s: prepareSingleLineFromSignature (0 , l: value .signature[0 ])
1114+ else
1115+ let l: value .singleLine = join (map (copy (l: value .signature), function (' s:prepareSingleLineFromSignature' )), ' ' )
1116+ endif
1117+ if l: value .singleLine[-1 :] is ' ;'
1118+ let l: value .singleLine = l: value .singleLine[0 :-2 ]
1119+ endif
1120+
1121+ let l: fullDocumentation = l: parts [1 :]
1122+ let l: value .fullDocumentation = l: fullDocumentation
1123+
1124+ let l: idx= 0
1125+ for l: line in l: fullDocumentation
1126+ if l: line is ' '
1127+ break
1128+ end
1129+ let l: idx += 1
1130+ endfor
1131+
1132+ let l: value .synopsis = l: fullDocumentation [0 :(l: idx )]
1133+
1134+ return l: value
1135+ endfunction
1136+
1137+ function s: prepareSingleLineFromSignature (key , val) abort
1138+ let l: line = a: val
1139+ " remove comments
1140+ let l: line = substitute (l: line , ' \s\+//.*' ,' ' ,' g' )
1141+ " end field lines that aren't the beginning of a struct with a semicolon
1142+ if l: line !~ ' ^\t\+{$'
1143+ let l: line = substitute (l: line , ' $' ,' ;' ,' ' )
1144+ endif
1145+ " replace leading tabs with a single space on field lines
1146+ let l: line = substitute (l: line ,' ^\t\+\ze[{}]' ,' ' ,' g' )
1147+
1148+ return l: line
1149+ endfunction
1150+
10771151function ! go#lsp#DocLink () abort
10781152 let l: fname = expand (' %:p' )
10791153 let [l: line , l: col ] = go#lsp#lsp#Position ()
10801154
10811155 call go#lsp#DidChange (l: fname )
10821156
10831157 let l: lsp = s: lspfactory .get ()
1084- let l: msg = go#lsp#message#Hover (l: fname, l: line , l: col )
1158+ let l: msg = go#lsp#message#DocLink (l: fname )
10851159 let l: state = s: newHandlerState (' doc url' )
1086- let l: resultHandler = go#promise#New (function (' s:docLinkFromHoverResult ' , [], l: state ), 10000 , ' ' )
1160+ let l: resultHandler = go#promise#New (function (' s:handleDocLink ' , [l: line , l: col ], l: state ), 10000 , ' ' )
10871161 let l: state .handleResult = l: resultHandler .wrapper
10881162 let l: state .error = l: resultHandler .wrapper
10891163 call l: lsp .sendMessage (l: msg , l: state )
10901164 return l: resultHandler .await ()
10911165endfunction
10921166
1093- function ! s: docLinkFromHoverResult ( msg) abort dict
1167+ function ! s: handleDocLink ( line , character , msg) abort dict
10941168 if type (a: msg ) is type (' ' )
10951169 return [a: msg , 1 ]
10961170 endif
10971171
1098- if a: msg is v: null || ! has_key ( a: msg, ' contents ' )
1172+ if a: msg is v: null || ( type ( a: msg) is type ([]) && len ( a: msg ) == 0 )
10991173 return [' ' , 0 ]
11001174 endif
1101- let l: doc = json_decode (a: msg .contents.value)
11021175
1103- " for backward compatibility with older gopls
1104- if has_key (l: doc , ' link' )
1105- let l: link = l: doc .link
1106- return [l: doc .link, 0 ]
1107- endif
1176+ let l: link = ' '
1177+ for l: item in a: msg
1178+ if ! s: within (l: item .range , a: line , a: character )
1179+ continue
1180+ endif
1181+ let l: link = l: item .target
1182+ break
1183+ endfor
11081184
1109- if ! has_key (l: doc , ' linkPath' ) || empty (l: doc .linkPath)
1110- return [' ' , 0 ]
1111- endif
1112- let l: link = l: doc .linkPath . " #" . l: doc .linkAnchor
11131185 return [l: link , 0 ]
11141186endfunction
11151187
@@ -1183,7 +1255,7 @@ function! s:info(show, msg) abort dict
11831255 return
11841256 endif
11851257
1186- let l: value = json_decode (a: msg .contents.value )
1258+ let l: value = s: structuredHoverResult (a: msg .contents)
11871259 let l: content = [l: value .singleLine]
11881260 let l: content = s: infoFromHoverContent (l: content )
11891261
@@ -1678,7 +1750,7 @@ function! go#lsp#FillStruct() abort
16781750endfunction
16791751
16801752" Extract executes the refactor.extract code action for the current buffer
1681- " and configures the handler to only apply the fillstruct command for the
1753+ " and configures the handler to only apply the extract_function command for the
16821754" current location.
16831755function ! go#lsp#Extract (line1, line2) abort
16841756 let l: fname = expand (' %:p' )
0 commit comments