@@ -1004,21 +1004,22 @@ function! s:hoverHandler(next, diagnostic, msg) abort dict
1004
1004
endif
1005
1005
1006
1006
try
1007
- let l: value = json_decode (a: msg .contents.value)
1008
-
1009
1007
let l: msg = []
1010
1008
if len (a: diagnostic ) > 0
1011
- let l: msg = split (a: diagnostic , " \n " )
1009
+ let l: msg = split (a: diagnostic , ' \n ' )
1012
1010
let l: msg = add (l: msg , ' ' )
1013
1011
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)
1016
1016
if go#config#DocBalloon ()
1017
1017
" use synopsis instead of fullDocumentation to keep the hover window
1018
1018
" small.
1019
1019
let l: doc = l: value .synopsis
1020
1020
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 )
1022
1023
endif
1023
1024
endif
1024
1025
@@ -1065,51 +1066,122 @@ function! s:docFromHoverResult(msg) abort dict
1065
1066
return [' Undocumented' , 0 ]
1066
1067
endif
1067
1068
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 " )
1070
1071
if len (l: doc ) is 0
1071
1072
let l: doc = ' Undocumented'
1072
1073
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 )
1074
1075
return [l: content , 0 ]
1075
1076
endfunction
1076
1077
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
+
1077
1151
function ! go#lsp#DocLink () abort
1078
1152
let l: fname = expand (' %:p' )
1079
1153
let [l: line , l: col ] = go#lsp#lsp#Position ()
1080
1154
1081
1155
call go#lsp#DidChange (l: fname )
1082
1156
1083
1157
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 )
1085
1159
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 , ' ' )
1087
1161
let l: state .handleResult = l: resultHandler .wrapper
1088
1162
let l: state .error = l: resultHandler .wrapper
1089
1163
call l: lsp .sendMessage (l: msg , l: state )
1090
1164
return l: resultHandler .await ()
1091
1165
endfunction
1092
1166
1093
- function ! s: docLinkFromHoverResult ( msg) abort dict
1167
+ function ! s: handleDocLink ( line , character , msg) abort dict
1094
1168
if type (a: msg ) is type (' ' )
1095
1169
return [a: msg , 1 ]
1096
1170
endif
1097
1171
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 )
1099
1173
return [' ' , 0 ]
1100
1174
endif
1101
- let l: doc = json_decode (a: msg .contents.value)
1102
1175
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
1108
1184
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
1113
1185
return [l: link , 0 ]
1114
1186
endfunction
1115
1187
@@ -1183,7 +1255,7 @@ function! s:info(show, msg) abort dict
1183
1255
return
1184
1256
endif
1185
1257
1186
- let l: value = json_decode (a: msg .contents.value )
1258
+ let l: value = s: structuredHoverResult (a: msg .contents)
1187
1259
let l: content = [l: value .singleLine]
1188
1260
let l: content = s: infoFromHoverContent (l: content )
1189
1261
@@ -1678,7 +1750,7 @@ function! go#lsp#FillStruct() abort
1678
1750
endfunction
1679
1751
1680
1752
" 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
1682
1754
" current location.
1683
1755
function ! go#lsp#Extract (line1, line2) abort
1684
1756
let l: fname = expand (' %:p' )
0 commit comments