1
0
mirror of https://github.com/amix/vimrc synced 2025-06-16 09:35:01 +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

@ -1,5 +1,8 @@
### What did you do? (required. The issue will be **closed** when not provided.)
### What did you do? (required: The issue will be **closed** when not provided)
<!--
If possible, please provide clear steps for reproducing the problem.
-->
### What did you expect to happen?
@ -9,13 +12,18 @@
### Configuration (**MUST** fill this out):
* vim-go version:
#### vim-go version:
* `vimrc` you used to reproduce (use a *minimal* vimrc with other plugins disabled; do not link to a 2,000 line vimrc):
#### `vimrc` you used to reproduce (use a *minimal* vimrc with other plugins disabled; do not link to a 2,000 line vimrc):
<details><summary>vimrc</summary><br><pre>
* Vim version (first three lines from `:version`):
</pre></details>
* Go version (`go version`):
#### Vim version (first three lines from `:version`):
* Go environment (`go env`):
#### Go version (`go version`):
#### Go environment
<details><summary><code>go env</code> Output:</summary><br><pre>
</pre></details>

View File

@ -1,11 +1,50 @@
## unplanned
IMPROVEMENTS:
* Add a new option, `g:go_code_completion_enabled`, to control whether omnifunc
is set.
[[GH-2229]](https://github.com/fatih/vim-go/pull/2229)
* Use build tags with golangci-lint.
[[GH-2261]](https://github.com/fatih/vim-go/pull/2261)
* Allow debugging of packages outside of GOPATH without a go.mod file.
[[GH-2269]](https://github.com/fatih/vim-go/pull/2269)
* Show which example failed when Example tests fail
[[GH-2277]](https://github.com/fatih/vim-go/pull/2277)
* Show function signature and return types in preview window when autocompleting functions and methods.
[[GH-2289]](https://github.com/fatih/vim-go/pull/2289)
BUG FIXES:
* display info about function and function types whose parameters are
`interface{}` without truncating the function signature.
[[GH-2244]](https://github.com/fatih/vim-go/pull/2244)
* install tools that require GOPATH mode when in module mode.
[[GH-2253]](https://github.com/fatih/vim-go/pull/2253)
* Detect GOPATH when starting `gopls`
[[GH-2255]](https://github.com/fatih/vim-go/pull/2255)
* Handle `gopls` responses in the same window from which the respective request
originated.
[[GH-2266]](https://github.com/fatih/vim-go/pull/2266)
* Show completion matches from gocode.
[[GH-2267]](https://github.com/fatih/vim-go/pull/2267)
* Show the completion preview window.
[[GH-2268]](https://github.com/fatih/vim-go/pull/2268)
* Set the anchor for method documentation correctly.
[[GH-2276]](https://github.com/fatih/vim-go/pull/2276)
* Respect the LSP information for determining where candidate matches start.
[[GH-2291]](https://github.com/fatih/vim-go/pull/2291)
* Restore environment variables with backslashes correctly.
[[GH-2292]](https://github.com/fatih/vim-go/pull/2292)
## 1.20 - (April 22, 2019)
FEATURES:
* ***gopls support!***
* use gopls for autocompletion by default in Vim8 and Neovim.
* use gopls for `:GoDef` by setting `g:go_def_mode='gopls'`.
* use gopls for `:GoInfo` by setting `g:go_info_mode='gopls'`.
* Add support for golangci-lint.
* set `g:go_metalinter_command='golanci-lint'` to use golangci-lint instead
* set `g:go_metalinter_command='golangci-lint'` to use golangci-lint instead
of gometalinter.
* New `:GoDefType` command to jump to a type definition from an instance of the
type.
@ -60,6 +99,19 @@ IMPROVEMENTS:
[[GH-2172]](https://github.com/fatih/vim-go/pull/2172)
* Add support for golangci-lint.
[[GH-2182]](https://github.com/fatih/vim-go/pull/2182)
* Show hover balloon using gopls instead of gocode.
[[GH-2202]](https://github.com/fatih/vim-go/pull/2202)
* Add a new option, `g:go_debug_log_output`, to control logging with the
debugger.
[[GH-2203]](https://github.com/fatih/vim-go/pull/2203)
* Do not jump to quickfix or location list window when bang is used for async
jobs or linting.
[[GH-2205]](https://github.com/fatih/vim-go/pull/2205)
* Tab complete package names for commands from vendor directories and in
modules.
[[GH-2213]](https://github.com/fatih/vim-go/pull/2213)
* Add support for `gopls` to `g:go_info_mode`.
[[GH-2224]](https://github.com/fatih/vim-go/pull/2224)
BUG FIXES:
* Fix opening of non-existent file from `:GoDeclsDir` when the current
@ -93,6 +145,10 @@ BUG FIXES:
[[GH-2189]](https://github.com/fatih/vim-go/pull/2189)
* Highlight pre-release and metadata in versions in go.mod.
[[GH-2192]](https://github.com/fatih/vim-go/pull/2192)
* Handle runtime panics from `:GoRun` when using Neovim's terminal.
[[GH-2209]](https://github.com/fatih/vim-go/pull/2209)
* Fix adding tag option when a tag is added.
[[GH-2227]](https://github.com/fatih/vim-go/pull/2227)
## 1.19 - (November 4, 2018)

View File

@ -1,4 +1,4 @@
FROM golang:1.12.1
FROM golang:1.12.4
RUN apt-get update -y && \
apt-get install -y build-essential curl git libncurses5-dev python3-pip && \

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

View File

@ -32,17 +32,18 @@ CONTENTS *go-contents*
INTRO *go-intro*
Go (golang) support for Vim. vim-go comes with sensible predefined settings
(e.g. automatic `gofmt` on save), has autocomplete, snippet support, improved
syntax highlighting, go toolchain commands, etc. It is highly customizable,
and individual features can be toggled easily. vim-go leverages a number of
tools developed by the Go community to provide a seamless Vim experience.
(e.g. automatic `gofmt` on save), has code completion, snippet support,
improved syntax highlighting, go toolchain commands, etc. It is highly
customizable, and individual features can be toggled easily. vim-go leverages
a number of tools developed by the Go community to provide a seamless Vim
experience.
* Compile your package with |:GoBuild|, install it with |:GoInstall| or
test it with |:GoTest|. Run a single test with |:GoTestFunc|).
* Quickly execute your current file(s) with |:GoRun|.
* Improved syntax highlighting and folding.
* Debug programs with integrated `delve` support with |:GoDebugStart|.
* Completion support via `gocode` and `gopls`.
* Code completion support via `gocode` and `gopls`.
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
* Go to symbol/declaration with |:GoDef|.
* Look up documentation with |:GoDoc| or |:GoDocBrowser|.
@ -127,7 +128,7 @@ or $GOPATH/bin (default: $HOME/go/bin). It requires `git`.
Depending on your installation method, you may have to generate the plugin's
|:helptags| manually (e.g. `:helptags ALL`).
Autocompletion is enabled by default via 'omnifunc', which you can trigger
Code completion is enabled by default via 'omnifunc', which you can trigger
with |i_CTRL-X_CTRL-O| (`<C-x><C-o>`).
Supported Go plugins~ *vim-go-plugins*
@ -138,8 +139,12 @@ The following plugins are supported for use with vim-go:
https://github.com/Shougo/neocomplete.vim
* Real-time completion (Neovim and Vim 8):
https://github.com/Shougo/deoplete.nvim and
https://github.com/zchee/deoplete-go
https://github.com/Shougo/deoplete.nvim
Add the following line to your vimrc. This instructs deoplete to use omni
completion for Go files.
call deoplete#custom#option('omni_patterns', { 'go': '[^. *\t]\.\w*' })
* Display source code navigation in a sidebar:
https://github.com/majutsushi/tagbar
@ -149,9 +154,6 @@ The following plugins are supported for use with vim-go:
https://github.com/SirVer/ultisnips or
https://github.com/joereynolds/vim-minisnip
* Integration with `delve` (Neovim only):
https://github.com/jodosha/vim-godebug
* Interactive |:GoDecls| and |:GoDeclsDir|:
https://github.com/ctrlpvim/ctrlp.vim or
https://github.com/junegunn/fzf.vim or
@ -1202,6 +1204,13 @@ balloonexpr`.
==============================================================================
SETTINGS *go-settings*
*'g:go_code_completion_enabled'*
Enable code completion with |'omnifunc'|. By default it is enabled.
>
let g:go_code_completion_enabled = 1
<
*'g:go_test_show_name'*
Show the name of each failed test before the errors and logs output by the
@ -1251,8 +1260,8 @@ updated. By default it's disabled. The delay can be configured with the
Use this option to define the command to be used for |:GoInfo|. By default
`gocode` is being used as it's the fastest option. But one might also use
`guru` as it's covers more cases and is more accurate. Current valid options
are: `[gocode, guru]` >
`gopls` or `guru` as they cover more cases and are more accurate. Current
valid options are: `[gocode, guru, gopls]` >
let g:go_info_mode = 'gocode'
<
@ -1635,15 +1644,15 @@ same.
*'g:go_gocode_propose_builtins'*
Specifies whether `gocode` should add built-in types, functions and constants
to an autocompletion proposals. By default it is enabled.
to code completion proposals. By default it is enabled.
>
let g:go_gocode_propose_builtins = 1
<
*'g:go_gocode_propose_source'*
Specifies whether `gocode` should use source files instead of binary packages
for autocompletion proposals. When disabled, only identifiers from the current
package and packages that have been installed will proposed.
for code completion proposals. When disabled, only identifiers from the
current package and packages that have been installed will proposed.
>
let g:go_gocode_propose_source = 0
<
@ -1732,8 +1741,8 @@ i.e: |go#statusline#Show()|. By default it's enabled
<
*'g:go_echo_go_info'*
Use this option to show the identifier information when completion is done. By
default it's enabled >
Use this option to show the identifier information when code completion is
done. By default it's enabled. >
let g:go_echo_go_info = 1
<
@ -2017,8 +2026,9 @@ rest of the commands and mappings become available after starting debug mode.
* Setup the debug windows according to |'g:go_debug_windows'|.
* Make the `:GoDebug*` commands and `(go-debug-*)` mappings available.
The current directory is used if [pkg] is empty. Any other arguments will
be passed to the program.
The directory of the current buffer is used if [pkg] is empty. Any other
arguments will be passed to the program. When [pkg] is relative, it will
be interpreted relative to the directory of the current buffer.
Use |:GoDebugStop| to stop `dlv` and exit debugging mode.
@ -2031,7 +2041,6 @@ rest of the commands and mappings become available after starting debug mode.
Use `-test.flag` to pass flags to `go test` when debugging a test; for
example `-test.v` or `-test.run TestFoo`
*:GoDebugRestart*
:GoDebugRestart
@ -2153,6 +2162,16 @@ Defaults to `127.0.0.1:8181`:
let g:go_debug_address = '127.0.0.1:8181'
<
*'g:go_debug_log_output'*
Specifies log output options for `dlv`. Value should be a single string
of comma-separated options suitable for passing to `dlv`. An empty string
(`''`) will suppress logging entirely.
Default: `'debugger, rpc'`:
>
let g:go_debug_log = 'debugger, rpc'
<
*'g:go_highlight_debug'*
Highlight the current line and breakpoints in the debugger.
@ -2164,6 +2183,12 @@ Highlight the current line and breakpoints in the debugger.
==============================================================================
FAQ TROUBLESHOOTING *go-troubleshooting*
I get "Unknown function: go#config#..." error when I open a Go file.~
This often happens to vim-polyglot users when new config options are added to
vim-go. Run vim-polyglot's `build` script or make sure that vim-go is loaded
before vim-polyglot.
I get "not an editor command" error when I invoke :GoXXX~
This happens if vim-go is not installed properly. Be sure you have added this
@ -2364,14 +2389,14 @@ You can install and test all Vim versions by running `make`.
DONATION *go-donation*
People have asked for this for a long time, now you can be a fully supporter
by being a patreon at: https://www.patreon.com/fatih
by being a patreon at: https://www.patreon.com/bhcleek
By being a patron, you are enabling vim-go to grow and mature, helping me to
invest in bug fixes, new documentation, and improving both current and future
features. It's completely optional and is just a direct way to support
vim-go's ongoing development. Thanks!
Check it out: https://www.patreon.com/fatih
Check it out: https://www.patreon.com/bhcleek
==============================================================================

View File

@ -25,10 +25,12 @@ setlocal noexpandtab
compiler go
" Set autocompletion
setlocal omnifunc=go#complete#Complete
if !go#util#has_job()
setlocal omnifunc=go#complete#GocodeComplete
if go#config#CodeCompletionEnabled()
" Set autocompletion
setlocal omnifunc=go#complete#Complete
if !go#util#has_job()
setlocal omnifunc=go#complete#GocodeComplete
endif
endif
if get(g:, "go_doc_keywordprg_enabled", 1)
@ -82,9 +84,16 @@ endif
augroup vim-go-buffer
autocmd! * <buffer>
" TODO(bc): notify gopls about changes on CursorHold when the buffer is
" modified.
" TODO(bc): notify gopls that the file on disk is correct on BufWritePost
" The file is registered (textDocument/DidOpen) with gopls in plugin/go.vim
" on the FileType event.
" TODO(bc): handle all the other events that may be of interest to gopls,
" too (e.g. BufFilePost , CursorHold , CursorHoldI, FileReadPost,
" StdinReadPre, BufWritePost, TextChange, TextChangedI)
if go#util#has_job()
autocmd BufWritePost <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd FileChangedShell <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd BufDelete <buffer> call go#lsp#DidClose(expand('<afile>:p'))
endif
autocmd CursorHold <buffer> call go#auto#auto_type_info()
autocmd CursorHold <buffer> call go#auto#auto_sameids()

View File

@ -101,11 +101,11 @@ function! s:GoInstallBinaries(updateBinaries, ...)
" change $GOBIN so go get can automatically install to it
let $GOBIN = go_bin_path
" old_path is used to restore users own path
let old_path = $PATH
" vim's executable path is looking in PATH so add our go_bin path to it
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
let Restore_path = go#util#SetEnv('PATH', go_bin_path . go#util#PathListSep() . $PATH)
" GO111MODULE must be off to install golanci-lint and gometalinter
let Restore_modules = go#util#SetEnv('GO111MODULE', 'off')
" when shellslash is set on MS-* systems, shellescape puts single quotes
" around the output string. cmd on Windows does not handle single quotes
@ -185,7 +185,9 @@ function! s:GoInstallBinaries(updateBinaries, ...)
endfor
" restore back!
let $PATH = old_path
call call(Restore_path, [])
call call(Restore_modules, [])
if resetshellslash
set shellslash
endif
@ -234,6 +236,22 @@ function! s:gofiletype_post()
let &g:fileencodings = s:current_fileencodings
endfunction
function! s:register()
if !(&modifiable && expand('<amatch>') ==# 'go')
return
endif
let l:RestoreGopath = function('s:noop')
if go#config#AutodetectGopath()
let l:RestoreGopath = go#util#SetEnv('GOPATH', go#path#Detect())
endif
call go#lsp#DidOpen(expand('<afile>:p'))
call call(l:RestoreGopath, [])
endfunction
function! s:noop(...) abort
endfunction
augroup vim-go
autocmd!
@ -245,6 +263,10 @@ augroup vim-go
autocmd BufNewFile *.s if &modifiable | setlocal fileencoding=utf-8 fileformat=unix | endif
autocmd BufRead *.s call s:gofiletype_pre()
autocmd BufReadPost *.s call s:gofiletype_post()
if go#util#has_job()
autocmd FileType * call s:register()
endif
augroup end
" restore Vi compatibility settings

View File

@ -23,7 +23,7 @@ fi
dir="/tmp/vim-go-test/$1-install"
export GOPATH=$dir
export GO111MODULE=off
export GO111MODULE=auto
export PATH=${GOPATH}/bin:$PATH
shift