diff --git a/autoload/go/debug.vim b/autoload/go/debug.vim index 2bbd91457f..229995446d 100644 --- a/autoload/go/debug.vim +++ b/autoload/go/debug.vim @@ -507,23 +507,7 @@ function! s:out_cb(ch, msg) abort if has('nvim') let s:state['data'] = [] let l:state = {'databuf': ''} - function! s:on_data(ch, data, event) dict abort closure - let l:data = self.databuf - for msg in a:data - let l:data .= l:msg - endfor - - try - let l:res = json_decode(l:data) - let s:state['data'] = add(s:state['data'], l:res) - let self.databuf = '' - catch - " there isn't a complete message in databuf: buffer l:data and try - " again when more data comes in. - let self.databuf = l:data - finally - endtry - endfunction + " explicitly bind callback to state so that within it, self will " always refer to state. See :help Partial for more information. let l:state.on_data = function('s:on_data', [], l:state) @@ -560,6 +544,24 @@ function! s:out_cb(ch, msg) abort endif endfunction +function! s:on_data(ch, data, event) dict abort + let l:data = self.databuf + for l:msg in a:data + let l:data .= l:msg + endfor + + try + let l:res = json_decode(l:data) + let s:state['data'] = add(s:state['data'], l:res) + let self.databuf = '' + catch + " there isn't a complete message in databuf: buffer l:data and try + " again when more data comes in. + let self.databuf = l:data + finally + endtry +endfunction + " Start the debug mode. The first argument is the package name to compile and " debug, anything else will be passed to the running program. function! go#debug#Start(is_test, ...) abort diff --git a/autoload/go/guru.vim b/autoload/go/guru.vim index 703192d4e8..95c00caf63 100644 --- a/autoload/go/guru.vim +++ b/autoload/go/guru.vim @@ -128,10 +128,6 @@ function! s:async_guru(args) abort \ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output")) \ } - function! s:complete(job, exit_status, messages) dict abort - let output = join(a:messages, "\n") - call self.parse(a:exit_status, output, self.mode) - endfunction " explicitly bind complete to state so that within it, self will " always refer to state. See :help Partial for more information. let state.complete = function('s:complete', [], state) @@ -164,6 +160,11 @@ function! s:async_guru(args) abort endif endfunc +function! s:complete(job, exit_status, messages) dict abort + let output = join(a:messages, "\n") + call self.parse(a:exit_status, output, self.mode) +endfunction + " run_guru runs the given guru argument function! s:run_guru(args) abort if go#util#has_job() @@ -239,101 +240,101 @@ function! go#guru#DescribeInfo(showstatus) abort return endif - function! s:info(exit_val, output, mode) - if a:exit_val != 0 - return - endif + let args = { + \ 'mode': 'describe', + \ 'format': 'json', + \ 'selected': -1, + \ 'needs_scope': 0, + \ 'custom_parse': function('s:info'), + \ 'disable_progress': 1, + \ } - if a:output[0] !=# '{' - return - endif + call s:run_guru(args) +endfunction - if empty(a:output) || type(a:output) != type("") - return - endif +function! s:info(exit_val, output, mode) + if a:exit_val != 0 + return + endif - let result = json_decode(a:output) - if type(result) != type({}) - call go#util#EchoError(printf("malformed output from guru: %s", a:output)) - return - endif + if a:output[0] !=# '{' + return + endif - if !has_key(result, 'detail') - " if there is no detail check if there is a description and print it - if has_key(result, "desc") - call go#util#EchoInfo(result["desc"]) - return - endif + if empty(a:output) || type(a:output) != type("") + return + endif + + let result = json_decode(a:output) + if type(result) != type({}) + call go#util#EchoError(printf("malformed output from guru: %s", a:output)) + return + endif - call go#util#EchoError("detail key is missing. Please open a bug report on vim-go repo.") + if !has_key(result, 'detail') + " if there is no detail check if there is a description and print it + if has_key(result, "desc") + call go#util#EchoInfo(result["desc"]) return endif - let detail = result['detail'] - let info = "" - - " guru gives different information based on the detail mode. Let try to - " extract the most useful information + call go#util#EchoError("detail key is missing. Please open a bug report on vim-go repo.") + return + endif - if detail == "value" - if !has_key(result, 'value') - call go#util#EchoError("value key is missing. Please open a bug report on vim-go repo.") - return - endif + let detail = result['detail'] + let info = "" - let val = result["value"] - if !has_key(val, 'type') - call go#util#EchoError("type key is missing (value.type). Please open a bug report on vim-go repo.") - return - endif + " guru gives different information based on the detail mode. Let try to + " extract the most useful information - let info = val["type"] - elseif detail == "type" - if !has_key(result, 'type') - call go#util#EchoError("type key is missing. Please open a bug report on vim-go repo.") - return - endif + if detail == "value" + if !has_key(result, 'value') + call go#util#EchoError("value key is missing. Please open a bug report on vim-go repo.") + return + endif - let type = result["type"] - if !has_key(type, 'type') - call go#util#EchoError("type key is missing (type.type). Please open a bug report on vim-go repo.") - return - endif + let val = result["value"] + if !has_key(val, 'type') + call go#util#EchoError("type key is missing (value.type). Please open a bug report on vim-go repo.") + return + endif - let info = type["type"] - elseif detail == "package" - if !has_key(result, 'package') - call go#util#EchoError("package key is missing. Please open a bug report on vim-go repo.") - return - endif + let info = val["type"] + elseif detail == "type" + if !has_key(result, 'type') + call go#util#EchoError("type key is missing. Please open a bug report on vim-go repo.") + return + endif - let package = result["package"] - if !has_key(package, 'path') - call go#util#EchoError("path key is missing (package.path). Please open a bug report on vim-go repo.") - return - endif + let type = result["type"] + if !has_key(type, 'type') + call go#util#EchoError("type key is missing (type.type). Please open a bug report on vim-go repo.") + return + endif - let info = printf("package %s", package["path"]) - elseif detail == "unknown" - let info = result["desc"] - else - call go#util#EchoError(printf("unknown detail mode found '%s'. Please open a bug report on vim-go repo", detail)) + let info = type["type"] + elseif detail == "package" + if !has_key(result, 'package') + call go#util#EchoError("package key is missing. Please open a bug report on vim-go repo.") return endif - call go#util#ShowInfo(info) - endfunction + let package = result["package"] + if !has_key(package, 'path') + call go#util#EchoError("path key is missing (package.path). Please open a bug report on vim-go repo.") + return + endif - let args = { - \ 'mode': 'describe', - \ 'format': 'json', - \ 'selected': -1, - \ 'needs_scope': 0, - \ 'custom_parse': function('s:info'), - \ 'disable_progress': 1, - \ } + let info = printf("package %s", package["path"]) + elseif detail == "unknown" + let info = result["desc"] + else + call go#util#EchoError(printf("unknown detail mode found '%s'. Please open a bug report on vim-go repo", detail)) + return + endif - call s:run_guru(args) + call go#util#ShowInfo(info) endfunction " Show possible targets of selected function call @@ -609,105 +610,6 @@ function! go#guru#DescribeBalloon() abort return endif - function! s:describe_balloon(exit_val, output, mode) - if a:exit_val != 0 - return - endif - - if a:output[0] !=# '{' - return - endif - - if empty(a:output) || type(a:output) != type("") - return - endif - - let l:result = json_decode(a:output) - if type(l:result) != type({}) - call go#util#EchoError(printf('malformed output from guru: %s', a:output)) - return - endif - - let l:info = [] - if has_key(l:result, 'desc') - if l:result['desc'] != 'identifier' - let l:info = add(l:info, l:result['desc']) - endif - endif - - if has_key(l:result, 'detail') - let l:detail = l:result['detail'] - - " guru gives different information based on the detail mode. Let try to - " extract the most useful information - - if l:detail == 'value' - if !has_key(l:result, 'value') - call go#util#EchoError('value key is missing. Please open a bug report on vim-go repo.') - return - endif - - let l:val = l:result['value'] - if !has_key(l:val, 'type') - call go#util#EchoError('type key is missing (value.type). Please open a bug report on vim-go repo.') - return - endif - - let l:info = add(l:info, printf('type: %s', l:val['type'])) - if has_key(l:val, 'value') - let l:info = add(l:info, printf('value: %s', l:val['value'])) - endif - elseif l:detail == 'type' - if !has_key(l:result, 'type') - call go#util#EchoError('type key is missing. Please open a bug report on vim-go repo.') - return - endif - - let l:type = l:result['type'] - if !has_key(l:type, 'type') - call go#util#EchoError('type key is missing (type.type). Please open a bug report on vim-go repo.') - return - endif - - let l:info = add(l:info, printf('type: %s', l:type['type'])) - - if has_key(l:type, 'methods') - let l:info = add(l:info, 'methods:') - for l:m in l:type.methods - let l:info = add(l:info, printf("\t%s", l:m['name'])) - endfor - endif - elseif l:detail == 'package' - if !has_key(l:result, 'package') - call go#util#EchoError('package key is missing. Please open a bug report on vim-go repo.') - return - endif - - let l:package = result['package'] - if !has_key(l:package, 'path') - call go#util#EchoError('path key is missing (package.path). Please open a bug report on vim-go repo.') - return - endif - - let l:info = add(l:info, printf('package: %s', l:package["path"])) - elseif l:detail == 'unknown' - " the description is already included in l:info, and there's no other - " information on unknowns. - else - call go#util#EchoError(printf('unknown detail mode (%s) found. Please open a bug report on vim-go repo', l:detail)) - return - endif - endif - - if has('balloon_eval') - call balloon_show(join(l:info, "\n")) - return - endif - - call balloon_show(l:info) - endfunction - - " change the active window to the window where the cursor is. let l:winid = win_getid(winnr()) call win_gotoid(v:beval_winid) @@ -730,6 +632,104 @@ function! go#guru#DescribeBalloon() abort return '' endfunction +function! s:describe_balloon(exit_val, output, mode) + if a:exit_val != 0 + return + endif + + if a:output[0] !=# '{' + return + endif + + if empty(a:output) || type(a:output) != type("") + return + endif + + let l:result = json_decode(a:output) + if type(l:result) != type({}) + call go#util#EchoError(printf('malformed output from guru: %s', a:output)) + return + endif + + let l:info = [] + if has_key(l:result, 'desc') + if l:result['desc'] != 'identifier' + let l:info = add(l:info, l:result['desc']) + endif + endif + + if has_key(l:result, 'detail') + let l:detail = l:result['detail'] + + " guru gives different information based on the detail mode. Let try to + " extract the most useful information + + if l:detail == 'value' + if !has_key(l:result, 'value') + call go#util#EchoError('value key is missing. Please open a bug report on vim-go repo.') + return + endif + + let l:val = l:result['value'] + if !has_key(l:val, 'type') + call go#util#EchoError('type key is missing (value.type). Please open a bug report on vim-go repo.') + return + endif + + let l:info = add(l:info, printf('type: %s', l:val['type'])) + if has_key(l:val, 'value') + let l:info = add(l:info, printf('value: %s', l:val['value'])) + endif + elseif l:detail == 'type' + if !has_key(l:result, 'type') + call go#util#EchoError('type key is missing. Please open a bug report on vim-go repo.') + return + endif + + let l:type = l:result['type'] + if !has_key(l:type, 'type') + call go#util#EchoError('type key is missing (type.type). Please open a bug report on vim-go repo.') + return + endif + + let l:info = add(l:info, printf('type: %s', l:type['type'])) + + if has_key(l:type, 'methods') + let l:info = add(l:info, 'methods:') + for l:m in l:type.methods + let l:info = add(l:info, printf("\t%s", l:m['name'])) + endfor + endif + elseif l:detail == 'package' + if !has_key(l:result, 'package') + call go#util#EchoError('package key is missing. Please open a bug report on vim-go repo.') + return + endif + + let l:package = result['package'] + if !has_key(l:package, 'path') + call go#util#EchoError('path key is missing (package.path). Please open a bug report on vim-go repo.') + return + endif + + let l:info = add(l:info, printf('package: %s', l:package["path"])) + elseif l:detail == 'unknown' + " the description is already included in l:info, and there's no other + " information on unknowns. + else + call go#util#EchoError(printf('unknown detail mode (%s) found. Please open a bug report on vim-go repo', l:detail)) + return + endif + endif + + if has('balloon_eval') + call balloon_show(join(l:info, "\n")) + return + endif + + call balloon_show(l:info) +endfunction + " restore Vi compatibility settings let &cpo = s:cpo_save unlet s:cpo_save diff --git a/autoload/go/job.vim b/autoload/go/job.vim index 410307bb6b..648d3583ec 100644 --- a/autoload/go/job.vim +++ b/autoload/go/job.vim @@ -145,23 +145,6 @@ function! go#job#Options(args) let state.custom_complete = a:args.complete endif - function! s:start(args) dict - if go#config#EchoCommandInfo() && self.statustype != "" - let prefix = '[' . self.statustype . '] ' - call go#util#EchoSuccess(prefix . "dispatched") - endif - - if self.statustype != '' - let status = { - \ 'desc': 'current status', - \ 'type': self.statustype, - \ 'state': "started", - \ } - - call go#statusline#Update(self.jobdir, status) - endif - let self.started_at = reltime() - endfunction " explicitly bind _start to state so that within it, self will " always refer to state. See :help Partial for more information. " @@ -169,35 +152,14 @@ function! go#job#Options(args) " outside of this file. let cbs._start = function('s:start', [''], state) - function! s:callback(chan, msg) dict - call add(self.messages, a:msg) - endfunction " explicitly bind callback to state so that within it, self will " always refer to state. See :help Partial for more information. let cbs.callback = function('s:callback', [], state) - function! s:exit_cb(job, exitval) dict - let self.exit_status = a:exitval - let self.exited = 1 - - call self.show_status(a:job, a:exitval) - - if self.closed || has('nvim') - call self.complete(a:job, self.exit_status, self.messages) - endif - endfunction " explicitly bind exit_cb to state so that within it, self will always refer " to state. See :help Partial for more information. let cbs.exit_cb = function('s:exit_cb', [], state) - function! s:close_cb(ch) dict - let self.closed = 1 - - if self.exited - let job = ch_getjob(a:ch) - call self.complete(job, self.exit_status, self.messages) - endif - endfunction " explicitly bind close_cb to state so that within it, self will " always refer to state. See :help Partial for more information. let cbs.close_cb = function('s:close_cb', [], state) @@ -261,6 +223,48 @@ function! go#job#Options(args) return cbs endfunction +function! s:start(args) dict + if go#config#EchoCommandInfo() && self.statustype != "" + let prefix = '[' . self.statustype . '] ' + call go#util#EchoSuccess(prefix . "dispatched") + endif + + if self.statustype != '' + let status = { + \ 'desc': 'current status', + \ 'type': self.statustype, + \ 'state': "started", + \ } + + call go#statusline#Update(self.jobdir, status) + endif + let self.started_at = reltime() +endfunction + +function! s:callback(chan, msg) dict + call add(self.messages, a:msg) +endfunction + +function! s:exit_cb(job, exitval) dict + let self.exit_status = a:exitval + let self.exited = 1 + + call self.show_status(a:job, a:exitval) + + if self.closed || has('nvim') + call self.complete(a:job, self.exit_status, self.messages) + endif +endfunction + +function! s:close_cb(ch) dict + let self.closed = 1 + + if self.exited + let job = ch_getjob(a:ch) + call self.complete(job, self.exit_status, self.messages) + endif +endfunction + " go#job#Start runs a job. The options are expected to be the options " suitable for Vim8 jobs. When called from Neovim, Vim8 options will be " transformed to their Neovim equivalents. @@ -355,16 +359,10 @@ function! s:neooptions(options) let l:options['callback'] = a:options['callback'] if !has_key(a:options, 'out_cb') - function! s:callback2on_stdout(mode, ch, data, event) dict - let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.callback) - endfunction let l:options['on_stdout'] = function('s:callback2on_stdout', [l:out_mode], l:options) endif if !has_key(a:options, 'err_cb') - function! s:callback2on_stderr(mode, ch, data, event) dict - let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.callback) - endfunction let l:options['on_stderr'] = function('s:callback2on_stderr', [l:err_mode], l:options) endif @@ -373,9 +371,6 @@ function! s:neooptions(options) if key == 'out_cb' let l:options['out_cb'] = a:options['out_cb'] - function! s:on_stdout(mode, ch, data, event) dict - let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.out_cb) - endfunction let l:options['on_stdout'] = function('s:on_stdout', [l:out_mode], l:options) continue @@ -383,9 +378,6 @@ function! s:neooptions(options) if key == 'err_cb' let l:options['err_cb'] = a:options['err_cb'] - function! s:on_stderr(mode, ch, data, event) dict - let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.err_cb ) - endfunction let l:options['on_stderr'] = function('s:on_stderr', [l:err_mode], l:options) continue @@ -393,9 +385,6 @@ function! s:neooptions(options) if key == 'exit_cb' let l:options['exit_cb'] = a:options['exit_cb'] - function! s:on_exit(jobid, exitval, event) dict - call self.exit_cb(a:jobid, a:exitval) - endfunction let l:options['on_exit'] = function('s:on_exit', [], l:options) continue @@ -415,6 +404,26 @@ function! s:neooptions(options) return l:options endfunction +function! s:callback2on_stdout(mode, ch, data, event) dict + let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.callback) +endfunction + +function! s:callback2on_stderr(mode, ch, data, event) dict + let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.callback) +endfunction + +function! s:on_stdout(mode, ch, data, event) dict + let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.out_cb) +endfunction + +function! s:on_stderr(mode, ch, data, event) dict + let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.err_cb ) +endfunction + +function! s:on_exit(jobid, exitval, event) dict + call self.exit_cb(a:jobid, a:exitval) +endfunction + function! go#job#Stop(job) abort if has('nvim') call jobstop(a:job) diff --git a/autoload/go/lsp.vim b/autoload/go/lsp.vim index 50e790be4e..d8bfa3bf9e 100644 --- a/autoload/go/lsp.vim +++ b/autoload/go/lsp.vim @@ -261,55 +261,10 @@ function! s:newHandlerState(statustype) \ 'jobdir': getcwd(), \ } - function! s:requestComplete(ok) abort dict - if self.statustype == '' - return - endif - - if go#config#EchoCommandInfo() - let prefix = '[' . self.statustype . '] ' - if a:ok - call go#util#EchoSuccess(prefix . "SUCCESS") - else - call go#util#EchoError(prefix . "FAIL") - endif - endif - - let status = { - \ 'desc': 'last status', - \ 'type': self.statustype, - \ 'state': "success", - \ } - - if !a:ok - let status.state = "failed" - endif - - if has_key(self, 'started_at') - let elapsed_time = reltimestr(reltime(self.started_at)) - " strip whitespace - let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') - let status.state .= printf(" (%ss)", elapsed_time) - endif - - call go#statusline#Update(self.jobdir, status) - endfunction " explicitly bind requestComplete to state so that within it, self will " always refer to state. See :help Partial for more information. let l:state.requestComplete = funcref('s:requestComplete', [], l:state) - function! s:start() abort dict - if self.statustype != '' - let status = { - \ 'desc': 'current status', - \ 'type': self.statustype, - \ 'state': "started", - \ } - - call go#statusline#Update(self.jobdir, status) - endif - let self.started_at = reltime() - endfunction " explicitly bind start to state so that within it, self will " always refer to state. See :help Partial for more information. let l:state.start = funcref('s:start', [], l:state) @@ -317,19 +272,59 @@ function! s:newHandlerState(statustype) return l:state endfunction +function! s:requestComplete(ok) abort dict + if self.statustype == '' + return + endif + + if go#config#EchoCommandInfo() + let prefix = '[' . self.statustype . '] ' + if a:ok + call go#util#EchoSuccess(prefix . "SUCCESS") + else + call go#util#EchoError(prefix . "FAIL") + endif + endif + + let status = { + \ 'desc': 'last status', + \ 'type': self.statustype, + \ 'state': "success", + \ } + + if !a:ok + let status.state = "failed" + endif + + if has_key(self, 'started_at') + let elapsed_time = reltimestr(reltime(self.started_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + let status.state .= printf(" (%ss)", elapsed_time) + endif + + call go#statusline#Update(self.jobdir, status) +endfunction + +function! s:start() abort dict + if self.statustype != '' + let status = { + \ 'desc': 'current status', + \ 'type': self.statustype, + \ 'state': "started", + \ } + + call go#statusline#Update(self.jobdir, status) + endif + let self.started_at = reltime() +endfunction + " go#lsp#Definition calls gopls to get the definition of the identifier at " line and col in fname. handler should be a dictionary function that takes a " list of strings in the form 'file:line:col: message'. handler will be " attached to a dictionary that manages state (statuslines, sets the winid, " etc.) function! go#lsp#Definition(fname, line, col, handler) - function! s:definitionHandler(next, msg) abort dict - " gopls returns a []Location; just take the first one. - let l:msg = a:msg[0] - let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]] - call call(a:next, l:args) - endfunction - call go#lsp#DidChange(a:fname) let l:lsp = s:lspfactory.get() @@ -339,19 +334,19 @@ function! go#lsp#Definition(fname, line, col, handler) call l:lsp.sendMessage(l:msg, l:state) endfunction +function! s:definitionHandler(next, msg) abort dict + " gopls returns a []Location; just take the first one. + let l:msg = a:msg[0] + let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]] + call call(a:next, l:args) +endfunction + " go#lsp#Type calls gopls to get the type definition of the identifier at " line and col in fname. handler should be a dictionary function that takes a " list of strings in the form 'file:line:col: message'. handler will be " attached to a dictionary that manages state (statuslines, sets the winid, " etc.) function! go#lsp#TypeDef(fname, line, col, handler) - function! s:typeDefinitionHandler(next, msg) abort dict - " gopls returns a []Location; just take the first one. - let l:msg = a:msg[0] - let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]] - call call(a:next, l:args) - endfunction - call go#lsp#DidChange(a:fname) let l:lsp = s:lspfactory.get() @@ -361,6 +356,13 @@ function! go#lsp#TypeDef(fname, line, col, handler) call l:lsp.sendMessage(l:msg, l:state) endfunction +function! s:typeDefinitionHandler(next, msg) abort dict + " gopls returns a []Location; just take the first one. + let l:msg = a:msg[0] + let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]] + call call(a:next, l:args) +endfunction + function! go#lsp#DidOpen(fname) if get(b:, 'go_lsp_did_open', 0) return @@ -402,39 +404,39 @@ function! go#lsp#DidClose(fname) endfunction function! go#lsp#Completion(fname, line, col, handler) - function! s:completionHandler(next, msg) abort dict - " gopls returns a CompletionList. - let l:matches = [] - for l:item in a:msg.items - let l:match = {'abbr': l:item.label, 'word': l:item.textEdit.newText, 'info': '', 'kind': go#lsp#completionitemkind#Vim(l:item.kind)} - if has_key(l:item, 'detail') - let l:item.info = l:item.detail - endif - - if has_key(l:item, 'documentation') - let l:match.info .= "\n\n" . l:item.documentation - endif - - let l:matches = add(l:matches, l:match) - endfor - let l:args = [l:matches] - call call(a:next, l:args) - endfunction - - function! s:errorHandler(next, error) abort dict - call call(a:next, [[]]) - endfunction - call go#lsp#DidChange(a:fname) let l:lsp = s:lspfactory.get() let l:msg = go#lsp#message#Completion(a:fname, a:line, a:col) let l:state = s:newHandlerState('completion') let l:state.handleResult = funcref('s:completionHandler', [function(a:handler, [], l:state)], l:state) - let l:state.error = funcref('s:errorHandler', [function(a:handler, [], l:state)], l:state) + let l:state.error = funcref('s:completionErrorHandler', [function(a:handler, [], l:state)], l:state) call l:lsp.sendMessage(l:msg, l:state) endfunction +function! s:completionHandler(next, msg) abort dict + " gopls returns a CompletionList. + let l:matches = [] + for l:item in a:msg.items + let l:match = {'abbr': l:item.label, 'word': l:item.textEdit.newText, 'info': '', 'kind': go#lsp#completionitemkind#Vim(l:item.kind)} + if has_key(l:item, 'detail') + let l:item.info = l:item.detail + endif + + if has_key(l:item, 'documentation') + let l:match.info .= "\n\n" . l:item.documentation + endif + + let l:matches = add(l:matches, l:match) + endfor + let l:args = [l:matches] + call call(a:next, l:args) +endfunction + +function! s:completionErrorHandler(next, error) abort dict + call call(a:next, [[]]) +endfunction + " restore Vi compatibility settings let &cpo = s:cpo_save unlet s:cpo_save