1
0
mirror of https://github.com/amix/vimrc synced 2025-06-16 01:25:00 +08:00

plugins update

This commit is contained in:
Geezus
2019-05-16 14:48:47 -05:00
parent 5bc3d0226c
commit 555992dd9b
162 changed files with 3534 additions and 1379 deletions

View File

@ -115,7 +115,7 @@ function! go#cmd#RunTerm(bang, mode, files) abort
else
let cmd .= go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
endif
call go#term#newmode(a:bang, cmd, a:mode)
call go#term#newmode(a:bang, cmd, s:runerrorformat(), a:mode)
endfunction
" Run runs the current file (and their dependencies if any) and outputs it.
@ -167,21 +167,25 @@ function! go#cmd#Run(bang, ...) abort
let l:listtype = go#list#Type("GoRun")
try
" backup user's errorformat, will be restored once we are finished
let l:old_errorformat = &errorformat
let &errorformat = s:runerrorformat()
if l:listtype == "locationlist"
exe 'lmake!'
else
exe 'make!'
endif
finally
"restore errorformat
let &errorformat = l:old_errorformat
let &makeprg = default_makeprg
endtry
let items = go#list#Get(l:listtype)
let errors = go#util#FilterValids(items)
let l:errors = go#list#Get(l:listtype)
call go#list#Populate(l:listtype, errors, &makeprg)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
endif
@ -278,6 +282,12 @@ function! go#cmd#Generate(bang, ...) abort
endfunction
function! s:runerrorformat()
let l:panicaddress = "%\\t%#%f:%l +0x%[0-9A-Fa-f]%\\+"
let l:errorformat = '%A' . l:panicaddress . "," . &errorformat
return l:errorformat
endfunction
" ---------------------
" | Vim job callbacks |
" ---------------------

View File

@ -93,22 +93,6 @@ endfunction
function! s:async_info(echo, showstatus)
let state = {'echo': a:echo}
function! s:complete(job, exit_status, messages) abort dict
if a:exit_status != 0
return
endif
if &encoding != 'utf-8'
let i = 0
while i < len(a:messages)
let a:messages[i] = iconv(a:messages[i], 'utf-8', &encoding)
let i += 1
endwhile
endif
let result = s:info_filter(self.echo, join(a:messages, "\n"))
call s:info_complete(self.echo, result)
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)
@ -151,6 +135,23 @@ function! s:async_info(echo, showstatus)
call go#job#Start(cmd, opts)
endfunction
function! s:complete(job, exit_status, messages) abort dict
if a:exit_status != 0
return
endif
if &encoding != 'utf-8'
let i = 0
while i < len(a:messages)
let a:messages[i] = iconv(a:messages[i], 'utf-8', &encoding)
let i += 1
endwhile
endif
let result = s:info_filter(self.echo, join(a:messages, "\n"))
call s:info_complete(self.echo, result)
endfunction
function! s:gocodeFile()
let file = tempname()
call writefile(go#util#GetLines(), file)
@ -241,15 +242,15 @@ function! go#complete#GocodeComplete(findstart, base) abort
if s =~ '[(){}\{\}]'
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
endif
return s:completions[1]
return s:completions
endif
endfunction
function! go#complete#Complete(findstart, base) abort
let l:state = {'done': 0, 'matches': []}
let l:state = {'done': 0, 'matches': [], 'start': -1}
function! s:handler(state, matches) abort dict
function! s:handler(state, start, matches) abort dict
let a:state.start = a:start
let a:state.matches = a:matches
let a:state.done = 1
endfunction
@ -262,15 +263,16 @@ function! go#complete#Complete(findstart, base) abort
sleep 10m
endwhile
let s:completions = l:state.matches
if len(l:state.matches) == 0
" no matches. cancel and leave completion mode.
call go#util#EchoInfo("no matches")
return -3
endif
return col('.')
let s:completions = l:state.matches
return l:state.start
else "findstart = 0 when we need to return the list of completions
return s:completions
endif

View File

@ -48,7 +48,7 @@ function! go#config#TermMode() abort
endfunction
function! go#config#TermEnabled() abort
return get(g:, 'go_term_enabled', 0)
return has('nvim') && get(g:, 'go_term_enabled', 0)
endfunction
function! go#config#SetTermEnabled(value) abort
@ -210,6 +210,10 @@ function! go#config#DebugCommands() abort
return g:go_debug_commands
endfunction
function! go#config#DebugLogOutput() abort
return get(g:, 'go_debug_log_output', 'debugger, rpc')
endfunction
function! go#config#LspLog() abort
" make sure g:go_lsp_log is set so that it can be added to easily.
let g:go_lsp_log = get(g:, 'go_lsp_log', [])
@ -462,6 +466,10 @@ function! go#config#EchoGoInfo() abort
return get(g:, "go_echo_go_info", 1)
endfunction
function! go#config#CodeCompletionEnabled() abort
return get(g:, "go_code_completion_enabled", 1)
endfunction
" Set the default value. A value of "1" is a shortcut for this, for
" compatibility reasons.
if exists("g:go_gorename_prefill") && g:go_gorename_prefill == 1

View File

@ -12,7 +12,6 @@ if !exists('s:state')
\ 'localVars': {},
\ 'functionArgs': {},
\ 'message': [],
\ 'is_test': 0,
\}
if go#util#HasDebug('debugger-state')
@ -282,7 +281,7 @@ function! go#debug#Stop() abort
silent! exe bufwinnr(bufnr('__GODEBUG_OUTPUT__')) 'wincmd c'
if has('balloon_eval')
let &noballooneval=s:ballooneval
let &ballooneval=s:ballooneval
let &balloonexpr=s:balloonexpr
endif
@ -507,7 +506,7 @@ function! s:out_cb(ch, msg) abort
if has('nvim')
let s:state['data'] = []
let l:state = {'databuf': ''}
" 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)
@ -589,50 +588,41 @@ function! go#debug#Start(is_test, ...) abort
endif
try
if len(a:000) > 0
let l:pkgname = a:1
if l:pkgname[0] == '.'
let l:pkgname = go#package#FromPath(l:pkgname)
endif
else
let l:pkgname = go#package#FromPath(getcwd())
endif
if l:pkgname is -1
call go#util#EchoError('could not determine package name')
return
endif
" cd in to test directory; this is also what running "go test" does.
if a:is_test
" TODO(bc): Either remove this if it's ok to do so or else record it and
" reset cwd after the job completes.
lcd %:p:h
endif
let s:state.is_test = a:is_test
let l:args = []
if len(a:000) > 1
let l:args = ['--'] + a:000[1:]
endif
let l:cmd = [
\ dlv,
\ (a:is_test ? 'test' : 'debug'),
\ l:pkgname,
\]
" append the package when it's given.
if len(a:000) > 0
let l:pkgname = go#package#FromPath(a:1)
if l:pkgname is -1
call go#util#EchoError('could not determine package name')
return
endif
let l:cmd += [l:pkgname]
endif
let l:cmd += [
\ '--output', tempname(),
\ '--headless',
\ '--api-version', '2',
\ '--log', '--log-output', 'debugger,rpc',
\ '--listen', go#config#DebugAddress(),
\]
let l:debugLogOutput = go#config#DebugLogOutput()
if l:debugLogOutput != ''
let cmd += ['--log', '--log-output', l:debugLogOutput]
endif
let buildtags = go#config#BuildTags()
let l:buildtags = go#config#BuildTags()
if buildtags isnot ''
let l:cmd += ['--build-flags', '--tags=' . buildtags]
endif
let l:cmd += l:args
if len(a:000) > 1
let l:cmd += ['--'] + a:000[1:]
endif
let s:state['message'] = []
let l:opts = {

View File

@ -31,8 +31,12 @@ function! go#doc#OpenBrowser(...) abort
let godoc_url = go#config#DocUrl()
let godoc_url .= "/" . import
if decl !~ "^package"
let godoc_url .= "#" . name
if decl !~ '^package'
let anchor = name
if decl =~ '^func ('
let anchor = substitute(decl, '^func ([^ ]\+ \*\?\([^)]\+\)) ' . name . '(.*', '\1', '') . "." . name
endif
let godoc_url .= "#" . anchor
endif
call go#util#OpenBrowser(godoc_url)

View File

@ -246,7 +246,7 @@ function! go#guru#DescribeInfo(showstatus) abort
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:info'),
\ 'disable_progress': 1,
\ 'disable_progress': a:showstatus == 0,
\ }
call s:run_guru(args)

View File

@ -5,7 +5,7 @@ set cpo&vim
let s:templatepath = go#util#Join(expand('<sfile>:p:h:h:h'), '.github', 'ISSUE_TEMPLATE.md')
function! go#issue#New() abort
let body = go#uriEncode(s:issuebody())
let body = go#uri#Encode(s:issuebody())
let url = "https://github.com/fatih/vim-go/issues/new?body=" . l:body
call go#util#OpenBrowser(l:url)
endfunction

View File

@ -214,7 +214,9 @@ function! go#job#Options(args)
" the job was started.
if self.winid == l:winid
call go#list#Window(l:listtype, len(errors))
if !self.bang
if self.bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
endif
@ -489,7 +491,9 @@ function! s:neocb(mode, ch, buf, data, callback)
let l:buf = ''
" a single empty string means EOF was reached.
" A single empty string means EOF was reached. The first item will never be
" an empty string except for when it's the only item and is signaling that
" EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.

View File

@ -86,15 +86,18 @@ function! go#lint#Gometa(bang, autosave, ...) abort
call go#list#Clean(l:listtype)
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
else
let l:winid = win_getid(winnr())
" Parse and populate our location list
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !a:autosave && !a:bang
call go#list#JumpToFirst(l:listtype)
if a:autosave || a:bang
call win_gotoid(l:winid)
return
endif
call go#list#JumpToFirst(l:listtype)
endif
endfunction
@ -112,13 +115,18 @@ function! go#lint#Golint(bang, ...) abort
return
endif
let l:winid = win_getid(winnr())
let l:listtype = go#list#Type("GoLint")
call go#list#Parse(l:listtype, l:out, "GoLint")
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if !a:bang
call go#list#JumpToFirst(l:listtype)
if a:bang
call win_gotoid(l:winid)
return
endif
call go#list#JumpToFirst(l:listtype)
endfunction
" Vet calls 'go vet' on the current directory. Any warnings are populated in
@ -138,12 +146,15 @@ function! go#lint#Vet(bang, ...) abort
let l:listtype = go#list#Type("GoVet")
if l:err != 0
let l:winid = win_getid(winnr())
let errorformat = "%-Gexit status %\\d%\\+," . &errorformat
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
else
call win_gotoid(l:winid)
endif
else
call go#list#Clean(l:listtype)
@ -171,6 +182,7 @@ function! go#lint#Errcheck(bang, ...) abort
let l:listtype = go#list#Type("GoErrCheck")
if l:err != 0
let l:winid = win_getid(winnr())
let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
" Parse and populate our location list
@ -187,6 +199,8 @@ function! go#lint#Errcheck(bang, ...) abort
call go#list#Window(l:listtype, len(errors))
if !a:bang
call go#list#JumpToFirst(l:listtype)
else
call win_gotoid(l:winid)
endif
endif
else
@ -255,11 +269,13 @@ function! s:golangcilintcmd(bin_path)
let cmd = [a:bin_path]
let cmd += ["run"]
let cmd += ["--print-issued-lines=false"]
let cmd += ['--build-tags', go#config#BuildTags()]
let cmd += ["--disable-all"]
" do not use the default exclude patterns, because doing so causes golint
" problems about missing doc strings to be ignored and other things that
" golint identifies.
let cmd += ["--exclude-use-default=false"]
return cmd
endfunction

View File

@ -7,8 +7,7 @@ scriptencoding utf-8
let s:lspfactory = {}
function! s:lspfactory.get() dict abort
if !has_key(self, 'current')
" TODO(bc): check that the lsp is still running.
if !has_key(self, 'current') || empty(self.current)
let self.current = s:newlsp()
endif
@ -21,7 +20,7 @@ function! s:lspfactory.reset() dict abort
endif
endfunction
function! s:newlsp()
function! s:newlsp() abort
if !go#util#has_job()
" TODO(bc): start the server in the background using a shell that waits for the right output before returning.
call go#util#EchoError('This feature requires either Vim 8.0.0087 or newer with +job or Neovim.')
@ -115,16 +114,27 @@ function! s:newlsp()
try
let l:handler = self.handlers[l:response.id]
let l:winid = win_getid(winnr())
" Always set the active window to the window that was active when
" the request was sent. Among other things, this makes sure that
" the correct window's location list will be populated when the
" list type is 'location' and the user has moved windows since
" sending the reques.
call win_gotoid(l:handler.winid)
if has_key(l:response, 'error')
call l:handler.requestComplete(0)
call go#util#EchoError(l:response.error.message)
if has_key(l:handler, 'error')
call call(l:handler.error, [l:response.error.message])
else
call go#util#EchoError(l:response.error.message)
endif
call win_gotoid(l:winid)
return
endif
call l:handler.requestComplete(1)
call call(l:handler.handleResult, [l:response.result])
call win_gotoid(l:winid)
finally
call remove(self.handlers, l:response.id)
endtry
@ -149,9 +159,19 @@ function! s:newlsp()
if !self.last_request_id
" TODO(bc): run a server per module and one per GOPATH? (may need to
" keep track of servers by rootUri).
let l:msg = self.newMessage(go#lsp#message#Initialize(getcwd()))
let l:wd = go#util#ModuleRoot()
if l:wd == -1
call go#util#EchoError('could not determine appropriate working directory for gopls')
return
endif
let l:state = s:newHandlerState('gopls')
if l:wd == ''
let l:wd = getcwd()
endif
let l:msg = self.newMessage(go#lsp#message#Initialize(l:wd))
let l:state = s:newHandlerState('')
let l:state.handleResult = funcref('self.handleInitializeResult', [], l:self)
let self.handlers[l:msg.id] = l:state
@ -194,8 +214,8 @@ function! s:newlsp()
endfunction
function! l:lsp.write(msg) dict abort
let l:body = json_encode(a:msg)
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
let l:body = json_encode(a:msg)
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
if go#util#HasDebug('lsp')
let g:go_lsp_log = add(go#config#LspLog(), "->\n" . l:data)
@ -251,10 +271,10 @@ function! s:newlsp()
return l:lsp
endfunction
function! s:noop()
function! s:noop(...) abort
endfunction
function! s:newHandlerState(statustype)
function! s:newHandlerState(statustype) abort
let l:state = {
\ 'winid': win_getid(winnr()),
\ 'statustype': a:statustype,
@ -324,7 +344,7 @@ endfunction
" 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! go#lsp#Definition(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)
let l:lsp = s:lspfactory.get()
@ -346,7 +366,7 @@ endfunction
" 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! go#lsp#TypeDef(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)
let l:lsp = s:lspfactory.get()
@ -363,11 +383,15 @@ function! s:typeDefinitionHandler(next, msg) abort dict
call call(a:next, l:args)
endfunction
function! go#lsp#DidOpen(fname)
function! go#lsp#DidOpen(fname) abort
if get(b:, 'go_lsp_did_open', 0)
return
endif
if !filereadable(a:fname)
return
endif
let l:lsp = s:lspfactory.get()
let l:msg = go#lsp#message#DidOpen(fnamemodify(a:fname, ':p'), join(go#util#GetLines(), "\n") . "\n")
let l:state = s:newHandlerState('')
@ -377,9 +401,18 @@ function! go#lsp#DidOpen(fname)
let b:go_lsp_did_open = 1
endfunction
function! go#lsp#DidChange(fname)
if get(b:, 'go_lsp_did_open', 0)
return go#lsp#DidOpen(a:fname)
function! go#lsp#DidChange(fname) abort
" DidChange is called even when fname isn't open in a buffer (e.g. via
" go#lsp#Info); don't report the file as open or as having changed when it's
" not actually a buffer.
if bufnr(a:fname) == -1
return
endif
call go#lsp#DidOpen(a:fname)
if !filereadable(a:fname)
return
endif
let l:lsp = s:lspfactory.get()
@ -389,7 +422,11 @@ function! go#lsp#DidChange(fname)
call l:lsp.sendMessage(l:msg, l:state)
endfunction
function! go#lsp#DidClose(fname)
function! go#lsp#DidClose(fname) abort
if !filereadable(a:fname)
return
endif
if !get(b:, 'go_lsp_did_open', 0)
return
endif
@ -403,7 +440,7 @@ function! go#lsp#DidClose(fname)
let b:go_lsp_did_open = 0
endfunction
function! go#lsp#Completion(fname, line, col, handler)
function! go#lsp#Completion(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)
let l:lsp = s:lspfactory.get()
@ -417,10 +454,17 @@ endfunction
function! s:completionHandler(next, msg) abort dict
" gopls returns a CompletionList.
let l:matches = []
let l:start = -1
for l:item in a:msg.items
let l:start = l:item.textEdit.range.start.character
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
let l:match.info = l:item.detail
if go#lsp#completionitemkind#IsFunction(l:item.kind) || go#lsp#completionitemkind#IsMethod(l:item.kind)
let l:match.info = printf('func %s %s', l:item.label, l:item.detail)
endif
endif
if has_key(l:item, 'documentation')
@ -429,7 +473,7 @@ function! s:completionHandler(next, msg) abort dict
let l:matches = add(l:matches, l:match)
endfor
let l:args = [l:matches]
let l:args = [l:start, l:matches]
call call(a:next, l:args)
endfunction
@ -437,6 +481,80 @@ function! s:completionErrorHandler(next, error) abort dict
call call(a:next, [[]])
endfunction
function! go#lsp#Hover(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)
let l:lsp = s:lspfactory.get()
let l:msg = go#lsp#message#Hover(a:fname, a:line, a:col)
let l:state = s:newHandlerState('')
let l:state.handleResult = funcref('s:hoverHandler', [function(a:handler, [], l:state)], l:state)
let l:state.error = funcref('s:noop')
call l:lsp.sendMessage(l:msg, l:state)
endfunction
function! s:hoverHandler(next, msg) abort dict
let l:content = split(a:msg.contents.value, '; ')
if len(l:content) > 1
let l:curly = stridx(l:content[0], '{')
let l:content = extend([l:content[0][0:l:curly]], map(extend([l:content[0][l:curly+1:]], l:content[1:]), '"\t" . v:val'))
let l:content[len(l:content)-1] = '}'
endif
let l:args = [l:content]
call call(a:next, l:args)
endfunction
function! go#lsp#Info(showstatus)
let l:fname = expand('%:p')
let [l:line, l:col] = getpos('.')[1:2]
call go#lsp#DidChange(l:fname)
let l:lsp = s:lspfactory.get()
if a:showstatus
let l:state = s:newHandlerState('info')
else
let l:state = s:newHandlerState('')
endif
let l:state.handleResult = funcref('s:infoDefinitionHandler', [function('s:info', []), a:showstatus], l:state)
let l:state.error = funcref('s:noop')
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
call l:lsp.sendMessage(l:msg, l:state)
endfunction
function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
" gopls returns a []Location; just take the first one.
let l:msg = a:msg[0]
let l:fname = go#path#FromURI(l:msg.uri)
let l:line = l:msg.range.start.line+1
let l:col = l:msg.range.start.character+1
let l:lsp = s:lspfactory.get()
let l:msg = go#lsp#message#Hover(l:fname, l:line, l:col)
if a:showstatus
let l:state = s:newHandlerState('info')
else
let l:state = s:newHandlerState('')
endif
let l:state.handleResult = funcref('s:hoverHandler', [function('s:info', [], l:state)], l:state)
let l:state.error = funcref('s:noop')
call l:lsp.sendMessage(l:msg, l:state)
endfunction
function! s:info(content) abort dict
let l:content = a:content[0]
" strip off the method set and fields of structs and interfaces.
if l:content =~# '^type [^ ]\+ \(struct\|interface\)'
let l:content = substitute(l:content, '{.*', '', '')
endif
call go#util#ShowInfo(l:content)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View File

@ -28,7 +28,7 @@ let s:Event = 23
let s:Operator = 24
let s:TypeParameter = 25
function! go#lsp#completionitemkind#Vim(kind)
function! go#lsp#completionitemkind#Vim(kind) abort
if a:kind == s:Method || a:kind == s:Function || a:kind == s:Constructor
return 'f'
elseif a:kind == s:Variable || a:kind == s:Constant
@ -40,6 +40,22 @@ function! go#lsp#completionitemkind#Vim(kind)
endif
endfunction
function! go#lsp#completionitemkind#IsFunction(kind) abort
if a:kind == s:Function
return 1
endif
return 0
endfunction
function! go#lsp#completionitemkind#IsMethod(kind) abort
if a:kind == s:Method
return 1
endif
return 0
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View File

@ -2,7 +2,7 @@
let s:cpo_save = &cpo
set cpo&vim
function! go#lsp#message#Initialize(wd)
function! go#lsp#message#Initialize(wd) abort
return {
\ 'notification': 0,
\ 'method': 'initialize',
@ -11,13 +11,17 @@ function! go#lsp#message#Initialize(wd)
\ 'rootUri': go#path#ToURI(a:wd),
\ 'capabilities': {
\ 'workspace': {},
\ 'textDocument': {}
\ 'textDocument': {
\ 'hover': {
\ 'contentFormat': ['plaintext'],
\ },
\ }
\ }
\ }
\ }
endfunction
function! go#lsp#message#Definition(file, line, col)
function! go#lsp#message#Definition(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/definition',
@ -30,8 +34,7 @@ function! go#lsp#message#Definition(file, line, col)
\ }
endfunction
function! go#lsp#message#TypeDefinition(file, line, col)
function! go#lsp#message#TypeDefinition(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/typeDefinition',
@ -44,7 +47,7 @@ function! go#lsp#message#TypeDefinition(file, line, col)
\ }
endfunction
function! go#lsp#message#DidOpen(file, content)
function! go#lsp#message#DidOpen(file, content) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didOpen',
@ -58,7 +61,7 @@ function! go#lsp#message#DidOpen(file, content)
\ }
endfunction
function! go#lsp#message#DidChange(file, content)
function! go#lsp#message#DidChange(file, content) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didChange',
@ -75,7 +78,7 @@ function! go#lsp#message#DidChange(file, content)
\ }
endfunction
function! go#lsp#message#DidClose(file)
function! go#lsp#message#DidClose(file) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didClose',
@ -87,7 +90,7 @@ function! go#lsp#message#DidClose(file)
\ }
endfunction
function! go#lsp#message#Completion(file, line, col)
function! go#lsp#message#Completion(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/completion',
@ -100,7 +103,20 @@ function! go#lsp#message#Completion(file, line, col)
\ }
endfunction
function! s:position(line, col)
function! go#lsp#message#Hover(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/hover',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col),
\ }
\ }
endfunction
function! s:position(line, col) abort
return {'line': a:line - 1, 'character': a:col-1}
endfunction

View File

@ -7,7 +7,7 @@ let s:go_major_version = ""
function! go#mod#Format() abort
" go mod only exists in `v1.11`
if empty(s:go_major_version)
let tokens = matchlist(go#util#System("go version"), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
let tokens = matchlist(go#util#Exec(['go', 'version']), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
let s:go_major_version = str2nr(tokens[1])
endif

View File

@ -32,7 +32,7 @@ if len(s:goarch) == 0
endif
endif
function! go#package#Paths() abort
function! s:paths() abort
let dirs = []
if !exists("s:goroot")
@ -58,6 +58,58 @@ function! go#package#Paths() abort
return dirs
endfunction
function! s:module() abort
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Dir}}'])
if l:err != 0
return {}
endif
let l:dir = split(l:out, '\n')[0]
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Path}}'])
if l:err != 0
return {}
endif
let l:path = split(l:out, '\n')[0]
return {'dir': l:dir, 'path': l:path}
endfunction
function! s:vendordirs() abort
let l:vendorsuffix = go#util#PathSep() . 'vendor'
let l:module = s:module()
if empty(l:module)
let [l:root, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Root}}'])
if l:err != 0
return []
endif
let l:root = split(l:root, '\n')[0] . go#util#PathSep() . 'src'
let [l:dir, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Dir}}'])
if l:err != 0
return []
endif
let l:dir = split(l:dir, '\n')[0]
let l:vendordirs = []
while l:dir != l:root
let l:vendordir = l:dir . l:vendorsuffix
if isdirectory(l:vendordir)
let l:vendordirs = add(l:vendordirs, l:vendordir)
endif
let l:dir = fnamemodify(l:dir, ':h')
endwhile
return l:vendordirs
endif
let l:vendordir = l:module.dir . l:vendorsuffix
if !isdirectory(l:vendordir)
return []
endif
return [l:vendordir]
endfunction
let s:import_paths = {}
" ImportPath returns the import path of the package for current buffer.
function! go#package#ImportPath() abort
@ -85,7 +137,9 @@ function! go#package#ImportPath() abort
endfunction
" FromPath returns the import path of arg.
" go#package#FromPath returns the import path of arg. -1 is returned when arg
" does not specify a package. -2 is returned when arg is a relative path
" outside of GOPATH and not in a module.
function! go#package#FromPath(arg) abort
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:dir = getcwd()
@ -104,10 +158,10 @@ function! go#package#FromPath(arg) abort
let l:importpath = split(l:out, '\n')[0]
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
" Check it and retun an error if that is the case
" go list returns '_CURRENTDIRECTORY' if the directory is neither in GOPATH
" nor in a module. Check it and retun an error if that is the case
if l:importpath[0] ==# '_'
return -1
return -2
endif
return l:importpath
@ -144,33 +198,80 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
return go#package#CompleteMembers(words[1], words[2])
endif
let dirs = go#package#Paths()
let dirs = s:paths()
let module = s:module()
if len(dirs) == 0
if len(dirs) == 0 && empty(module)
" should not happen
return []
endif
let vendordirs = s:vendordirs()
let ret = {}
for dir in dirs
" this may expand to multiple lines
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
call add(root, expand(dir . '/src'))
for r in root
for i in split(globpath(r, a:ArgLead.'*'), "\n")
if isdirectory(i)
let i .= '/'
elseif i !~ '\.a$'
let root = add(root, expand(dir . '/src'), )
let root = extend(root, vendordirs)
let root = add(root, module)
for item in root
" item may be a dictionary when operating in a module.
if type(item) == type({})
if empty(item)
continue
endif
let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'),
let dir = item.dir
let path = item.path
else
let dir = item
let path = item
endif
if !empty(module) && dir ==# module.dir
if stridx(a:ArgLead, module.path) == 0
if len(a:ArgLead) != len(module.path)
let glob = globpath(module.dir, substitute(a:ArgLead, module.path . '/\?', '', '').'*')
else
let glob = module.dir
endif
elseif stridx(module.path, a:ArgLead) == 0 && stridx(module.path, '/', len(a:ArgLead)) < 0
" use the module directory when a:ArgLead is contained in
" module.path and module.path does not have any path segments after
" a:ArgLead.
let glob = module.dir
else
continue
endif
else
let glob = globpath(dir, a:ArgLead.'*')
endif
for candidate in split(glob)
if isdirectory(candidate)
" TODO(bc): use wildignore instead of filtering out vendor
" directories manually?
if fnamemodify(candidate, ':t') == 'vendor'
continue
endif
let candidate .= '/'
elseif candidate !~ '\.a$'
continue
endif
if dir !=# path
let candidate = substitute(candidate, '^' . dir, path, 'g')
else
let candidate = candidate[len(dir)+1:]
endif
" replace a backslash with a forward slash and drop .a suffixes
let candidate = substitute(substitute(candidate, '[\\]', '/', 'g'),
\ '\.a$', '', 'g')
" without this the result can have duplicates in form of
" 'encoding/json' and '/encoding/json/'
let i = go#util#StripPathSep(i)
let candidate = go#util#StripPathSep(candidate)
let ret[i] = i
let ret[candidate] = candidate
endfor
endfor
endfor

View File

@ -0,0 +1,58 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_Complete_GOPATH_simple() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
call s:complete('package', ['package'])
endfunc
func! Test_Complete_Module_simple() abort
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
call s:complete('package', ['package'])
endfunc
func! Test_Complete_GOPATH_subdirs() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
call s:complete('package/', ['package/bar', 'package/baz'])
endfunc
func! Test_Complete_Module_subdirs() abort
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
call s:complete('package/', ['package/bar', 'package/baz'])
endfunc
func! Test_Complete_GOPATH_baronly() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
call s:complete('package/bar', ['package/bar'])
endfunc
func! Test_Complete_Module_baronly() abort
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
call s:complete('package/bar', ['package/bar'])
endfunc
func! Test_Complete_GOPATH_vendor() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
call s:complete('foo', ['foo'])
endfunc
func! Test_Complete_Module_vendor() abort
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
call s:complete('foo', ['foo'])
endfunc
func! s:complete(arglead, expected) abort
let l:candidates = go#package#Complete(a:arglead, '', 1)
call assert_equal(a:expected, l:candidates)
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -209,13 +209,18 @@ endfunction
" Relative paths cannot be properly converted to a URI; when path is a
" relative path, the file scheme will not be prepended.
function! go#path#ToURI(path)
let l:absolute = !go#util#IsWin() && a:path[0] is# '/'
let l:prefix = ''
let l:path = a:path
if l:path[1:2] is# ':\'
let l:path = '/' . l:path[0:1] . l:path[3:]
if go#util#IsWin() && l:path[1:2] is# ':\'
let l:absolute = 1
let l:prefix = '/' . l:path[0:1]
let l:path = l:path[2:]
endif
return substitute(
\ (l:path[0] is# '/' ? 'file://' : '') . go#uri#EncodePath(l:path),
\ (l:absolute ? 'file://' : '') . l:prefix . go#uri#EncodePath(l:path),
\ '\\',
\ '/',
\ 'g',
@ -229,7 +234,7 @@ function! go#path#FromURI(uri) abort
let l:path = go#uri#Decode(l:encoded_path)
" If the path is like /C:/foo/bar, it should be C:\foo\bar instead.
if l:path =~# '^/[a-zA-Z]:'
if go#util#IsWin() && l:path =~# '^/[a-zA-Z]:'
let l:path = substitute(l:path[1:], '/', '\\', 'g')
endif

View File

@ -163,17 +163,17 @@ func s:create_cmd(args) abort
endfor
endif
" construct options
" default value
if empty(l:tags)
let l:tags = ["json"]
endif
" construct tags
call extend(cmd, ["-add-tags", join(l:tags, ",")])
" construct options
if !empty(l:options)
call extend(cmd, ["-add-options", join(l:options, ",")])
else
" default value
if empty(l:tags)
let l:tags = ["json"]
endif
" construct tags
call extend(cmd, ["-add-tags", join(l:tags, ",")])
endif
elseif l:mode == "remove"
if empty(l:cmd_args)

View File

@ -2,7 +2,7 @@
let s:cpo_save = &cpo
set cpo&vim
func! Test_add_tags() abort
func! TestAddTags() abort
try
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1)
@ -13,6 +13,28 @@ func! Test_add_tags() abort
endfunc
func! TestAddTags_WithOptions() abort
try
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json,omitempty')
call gotest#assert_fixture('tags/add_all_golden_options.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! TestAddTags_AddOptions() abort
try
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json')
call gotest#assert_fixture('tags/add_all_golden.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json,omitempty')
call gotest#assert_fixture('tags/add_all_golden_options.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_remove_tags() abort
try
let l:tmp = gotest#load_fixture('tags/remove_all_input.go')

View File

@ -4,31 +4,33 @@ set cpo&vim
" new creates a new terminal with the given command. Mode is set based on the
" global variable g:go_term_mode, which is by default set to :vsplit
function! go#term#new(bang, cmd) abort
return go#term#newmode(a:bang, a:cmd, go#config#TermMode())
function! go#term#new(bang, cmd, errorformat) abort
return go#term#newmode(a:bang, a:cmd, a:errorformat, go#config#TermMode())
endfunction
" new creates a new terminal with the given command and window mode.
function! go#term#newmode(bang, cmd, mode) abort
let mode = a:mode
if empty(mode)
let mode = go#config#TermMode()
" go#term#newmode creates a new terminal with the given command and window mode.
function! go#term#newmode(bang, cmd, errorformat, mode) abort
let l:mode = a:mode
if empty(l:mode)
let l:mode = go#config#TermMode()
endif
let state = {
let l:state = {
\ 'cmd': a:cmd,
\ 'bang' : a:bang,
\ 'winid': win_getid(winnr()),
\ 'stdout': []
\ 'stdout': [],
\ 'stdout_buf': '',
\ 'errorformat': a:errorformat,
\ }
" execute go build in the files directory
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
execute cd . fnameescape(expand("%:p:h"))
execute l:cd . fnameescape(expand("%:p:h"))
execute mode.' __go_term__'
execute l:mode . ' __go_term__'
setlocal filetype=goterm
setlocal bufhidden=delete
@ -41,83 +43,103 @@ function! go#term#newmode(bang, cmd, mode) abort
"
" Don't set an on_stderr, because it will be passed the same data as
" on_stdout. See https://github.com/neovim/neovim/issues/2836
let job = {
let l:job = {
\ 'on_stdout': function('s:on_stdout', [], state),
\ 'on_exit' : function('s:on_exit', [], state),
\ }
let state.id = termopen(a:cmd, job)
let state.termwinid = win_getid(winnr())
let l:state.id = termopen(a:cmd, l:job)
let l:state.termwinid = win_getid(winnr())
execute cd . fnameescape(dir)
execute l:cd . fnameescape(l:dir)
" resize new term if needed.
let height = go#config#TermHeight()
let width = go#config#TermWidth()
let l:height = go#config#TermHeight()
let l:width = go#config#TermWidth()
" Adjust the window width or height depending on whether it's a vertical or
" horizontal split.
if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
exe 'vertical resize ' . width
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
exe 'vertical resize ' . l:width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . height
exe 'resize ' . l:height
endif
" we also need to resize the pty, so there you go...
call jobresize(state.id, width, height)
call jobresize(l:state.id, l:width, l:height)
call win_gotoid(state.winid)
call win_gotoid(l:state.winid)
return state.id
return l:state.id
endfunction
function! s:on_stdout(job_id, data, event) dict abort
call extend(self.stdout, a:data)
" A single empty string means EOF was reached. The first item will never be
" the empty string except for when it's the only item and is signaling that
" EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.
if self.stdout_buf == ''
return
endif
let self.stdout = add(self.stdout, self.stdout_buf)
else
let l:data = copy(a:data)
let l:data[0] = self.stdout_buf . l:data[0]
" The last element may be a partial line; save it for next time.
let self.stdout_buf = l:data[-1]
let self.stdout = extend(self.stdout, l:data[:-2])
endif
endfunction
function! s:on_exit(job_id, exit_status, event) dict abort
let l:winid = win_getid(winnr())
call win_gotoid(self.winid)
let l:listtype = go#list#Type("_term")
" usually there is always output so never branch into this clause
if empty(self.stdout)
call s:cleanlist(self.winid, l:listtype)
if a:exit_status == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
return
endif
let errors = go#util#ParseErrors(self.stdout)
let errors = go#util#FilterValids(errors)
call win_gotoid(self.winid)
if !empty(errors)
" close terminal; we don't need it anymore
call win_gotoid(self.termwinid)
close
let l:title = self.cmd
if type(l:title) == v:t_list
let l:title = join(self.cmd)
endif
call win_gotoid(self.winid)
let l:i = 0
while l:i < len(self.stdout)
let self.stdout[l:i] = substitute(self.stdout[l:i], "\r$", '', 'g')
let l:i += 1
endwhile
let title = self.cmd
if type(title) == v:t_list
let title = join(self.cmd)
endif
call go#list#Populate(l:listtype, errors, title)
call go#list#Window(l:listtype, len(errors))
if !self.bang
call go#list#JumpToFirst(l:listtype)
endif
call go#list#ParseFormat(l:listtype, self.errorformat, self.stdout, l:title)
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if empty(l:errors)
call go#util#EchoError( '[' . l:title . '] ' . "FAIL")
call win_gotoid(l:winid)
return
endif
call s:cleanlist(self.winid, l:listtype)
endfunction
" close terminal; we don't need it anymore
call win_gotoid(self.termwinid)
close!
function! s:cleanlist(winid, listtype) abort
" There are no errors. Clean and close the list. Jump to the window to which
" the location list is attached, close the list, and then jump back to the
" current window.
let winid = win_getid(winnr())
call win_gotoid(a:winid)
call go#list#Clean(a:listtype)
call win_gotoid(l:winid)
if self.bang
call win_gotoid(l:winid)
return
endif
call win_gotoid(self.winid)
call go#list#JumpToFirst(l:listtype)
endfunction
" restore Vi compatibility settings

View File

@ -17,7 +17,7 @@ func! Test_GoTermNewMode()
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set nosplitright
call go#term#newmode(0, cmd, '')
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)
@ -41,7 +41,7 @@ func! Test_GoTermNewMode_SplitRight()
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set splitright
call go#term#newmode(0, cmd, '')
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)

View File

@ -0,0 +1,3 @@
module package
go 1.12

View File

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go")
}

View File

@ -0,0 +1,10 @@
package main
import (
"fmt"
)
func ExampleHelloWorld() {
fmt.Println("Hello, World")
// Output: What's shakin
}

View File

@ -30,8 +30,8 @@ function! go#test#Test(bang, compile, ...) abort
call add(args, printf("-timeout=%s", timeout))
endif
if has('nvim') && go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args)
if go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args, s:errorformat())
endif
if go#util#has_job()
@ -75,14 +75,17 @@ function! go#test#Test(bang, compile, ...) abort
execute cd fnameescape(expand("%:p:h"))
if l:err != 0
let l:winid = win_getid(winnr())
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), l:cmd)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
elseif empty(errors)
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(out)
elseif a:bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
else
call go#list#Clean(l:listtype)
@ -163,9 +166,17 @@ function! s:errorformat() abort
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
" Match failure lines.
"
" Example failures start with '--- FAIL: ', followed by the example name
" followed by a space , followed by the duration of the example in
" parantheses. They aren't nested, though, so don't check for indentation.
" The errors from them also aren't indented and don't report file location
" or line numbers, so those won't show up. This will at least let the user
" know which example failed, though.
let format .= ',%G--- FAIL: %\\%(Example%\\)%\\@=%m (%.%#)'
" Test failures start with '--- FAIL: ', followed by the test name followed
" by a space the duration of the test in parentheses
" by a space, followed by the duration of the test in parentheses.
"
" e.g.:
" '--- FAIL: TestSomething (0.00s)'

View File

@ -66,9 +66,9 @@ endfunc
func! Test_GoTestShowName() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
\ {'lnum': 6, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
\ {'lnum': 6, 'bufnr': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld/sub'},
\ {'lnum': 9, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ {'lnum': 9, 'bufnr': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ ]
let g:go_test_show_name=1
@ -78,20 +78,27 @@ endfunc
func! Test_GoTestVet() abort
let expected = [
\ {'lnum': 6, 'bufnr': 16, 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
\ {'lnum': 6, 'bufnr': 11, 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
\ ]
call s:test('veterror/veterror.go', expected)
endfunc
func! Test_GoTestTestCompilerError() abort
let expected = [
\ {'lnum': 10, 'bufnr': 11, 'col': 16, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'cannot use r (type struct {}) as type io.Reader in argument to ioutil.ReadAll:'},
\ {'lnum': 10, 'bufnr': 9, 'col': 16, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'cannot use r (type struct {}) as type io.Reader in argument to ioutil.ReadAll:'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'struct {} does not implement io.Reader (missing Read method)'}
\ ]
call s:test('testcompilerror/testcompilerror_test.go', expected)
endfunc
func! Test_GoTestExample() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'ExampleHelloWorld'}
\ ]
call s:test('example/example_test.go', expected)
endfunc
func! s:test(file, expected, ...) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
silent exe 'e ' . $GOPATH . '/src/' . a:file

View File

@ -86,8 +86,10 @@ function! go#tool#Info(showstatus) abort
call go#complete#Info(a:showstatus)
elseif l:mode == 'guru'
call go#guru#DescribeInfo(a:showstatus)
elseif l:mode == 'gopls'
call go#lsp#Info(a:showstatus)
else
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru]')
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru, gopls]')
endif
endfunction
@ -112,7 +114,22 @@ function! go#tool#Exists(importpath) abort
endfunction
function! go#tool#DescribeBalloon()
return go#guru#DescribeBalloon()
let l:fname = fnamemodify(bufname(v:beval_bufnr), ':p')
call go#lsp#Hover(l:fname, v:beval_lnum, v:beval_col, funcref('s:balloon', []))
return ''
endfunction
function! s:balloon(msg)
let l:msg = a:msg
if has('balloon_eval')
if has('balloon_multiline')
let l:msg = join(a:msg, "\n")
else
let l:msg = substitute(join(map(deepcopy(a:msg), 'substitute(v:val, "\t", "", "")'), '; '), '{;', '{', '')
endif
endif
call balloon_show(l:msg)
endfunction
" restore Vi compatibility settings

View File

@ -7,7 +7,11 @@ function! go#uri#Encode(value) abort
endfunction
function! go#uri#EncodePath(value) abort
return s:encode(a:value, '[^/A-Za-z0-9_.~-]')
let l:separator = '/'
if go#util#IsWin()
let l:separator = '\\'
endif
return s:encode(a:value, '[^' . l:separator . 'A-Za-z0-9_.~-]')
endfunction
function! s:encode(value, unreserved)

View File

@ -137,11 +137,33 @@ function! go#util#gomod() abort
return substitute(s:exec(['go', 'env', 'GOMOD'])[0], '\n', '', 'g')
endfunction
function! go#util#osarch() abort
return go#util#env("goos") . '_' . go#util#env("goarch")
endfunction
" go#util#ModuleRoot returns the root directory of the module of the current
" buffer.
function! go#util#ModuleRoot() abort
let [l:out, l:err] = go#util#ExecInDir(['go', 'env', 'GOMOD'])
if l:err != 0
return -1
endif
let l:module = split(l:out, '\n', 1)[0]
" When run with `GO111MODULE=on and not in a module directory, the module will be reported as /dev/null.
let l:fakeModule = '/dev/null'
if go#util#IsWin()
let l:fakeModule = 'NUL'
endif
if l:fakeModule == l:module
return expand('%:p:h')
endif
return fnamemodify(l:module, ':p:h')
endfunction
" Run a shell command.
"
" It will temporary set the shell to /bin/sh for Unix-like systems if possible,
@ -511,42 +533,6 @@ function! go#util#ParseErrors(lines) abort
return errors
endfunction
" FilterValids filters the given items with only items that have a valid
" filename. Any non valid filename is filtered out.
function! go#util#FilterValids(items) abort
" Remove any nonvalid filename from the location list to avoid opening an
" empty buffer. See https://github.com/fatih/vim-go/issues/287 for
" details.
let filtered = []
let is_readable = {}
for item in a:items
if has_key(item, 'bufnr')
let filename = bufname(item.bufnr)
elseif has_key(item, 'filename')
let filename = item.filename
else
" nothing to do, add item back to the list
call add(filtered, item)
continue
endif
if !has_key(is_readable, filename)
let is_readable[filename] = filereadable(filename)
endif
if is_readable[filename]
call add(filtered, item)
endif
endfor
for k in keys(filter(is_readable, '!v:val'))
echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"'
echohl Identifier | echon " from location list (nonvalid filename)" | echohl None
endfor
return filtered
endfunction
function! go#util#ShowInfo(info)
if empty(a:info)
return
@ -555,6 +541,39 @@ function! go#util#ShowInfo(info)
echo "vim-go: " | echohl Function | echon a:info | echohl None
endfunction
" go#util#SetEnv takes the name of an environment variable and what its value
" should be and returns a function that will restore it to its original value.
function! go#util#SetEnv(name, value) abort
let l:state = {}
if len(a:name) == 0
return function('s:noop', [], l:state)
endif
let l:remove = 0
if exists('$' . a:name)
let l:oldvalue = eval('$' . a:name)
else
let l:remove = 1
endif
" wrap the value in single quotes so that it will work on windows when there
" are backslashes present in the value (e.g. $PATH).
call execute('let $' . a:name . " = '" . a:value . "'")
if l:remove
function! s:remove(name) abort
call execute('unlet $' . a:name)
endfunction
return function('s:remove', [a:name], l:state)
endif
return function('go#util#SetEnv', [a:name, l:oldvalue], l:state)
endfunction
function! s:noop(...) abort dict
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save