1
0
mirror of https://github.com/amix/vimrc synced 2025-07-07 00:15:00 +08:00

Removed vim-go

This commit is contained in:
Amir Salihefendic
2019-11-17 16:45:35 +01:00
parent 3b7a7ded31
commit 8c6f4bdf7f
172 changed files with 0 additions and 24061 deletions

View File

@ -1,35 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Test alternates between the implementation of code and the test code.
function! go#alternate#Switch(bang, cmd) abort
let file = expand('%')
if empty(file)
call go#util#EchoError("no buffer name")
return
elseif file =~# '^\f\+_test\.go$'
let l:root = split(file, '_test.go$')[0]
let l:alt_file = l:root . ".go"
elseif file =~# '^\f\+\.go$'
let l:root = split(file, ".go$")[0]
let l:alt_file = l:root . '_test.go'
else
call go#util#EchoError("not a go file")
return
endif
if !filereadable(alt_file) && !bufexists(alt_file) && !a:bang
call go#util#EchoError("couldn't find ".alt_file)
return
elseif empty(a:cmd)
execute ":" . go#config#AlternateMode() . " " . alt_file
else
execute ":" . a:cmd . " " . alt_file
endif
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,76 +0,0 @@
" asmfmt.vim: Vim command to format Go asm files with asmfmt
" (github.com/klauspost/asmfmt).
"
" This filetype plugin adds new commands for asm buffers:
"
" :Fmt
"
" Filter the current asm buffer through asmfmt.
" It tries to preserve cursor position and avoids
" replacing the buffer with stderr output.
"
" Options:
"
" g:go_asmfmt_autosave [default=0]
"
" Flag to automatically call :Fmt when file is saved.
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:got_fmt_error = 0
" This is a trimmed-down version of the logic in fmt.vim.
function! go#asmfmt#Format() abort
" Save state.
let l:curw = winsaveview()
" Write the current buffer to a tempfile.
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
" Run asmfmt.
let [l:out, l:err] = go#util#Exec(['asmfmt', '-w', l:tmpname])
if l:err
call go#util#EchoError(l:out)
return
endif
" Remove undo point caused by BufWritePre.
try | silent undojoin | catch | endtry
" Replace the current file with the temp file; then reload the buffer.
let old_fileformat = &fileformat
" save old file permissions
let original_fperm = getfperm(expand('%'))
call rename(l:tmpname, expand('%'))
" restore old file permissions
call setfperm(expand('%'), original_fperm)
silent edit!
let &fileformat = old_fileformat
let &syntax = &syntax
" Restore the cursor/window positions.
call winrestview(l:curw)
endfunction
function! go#asmfmt#ToggleAsmFmtAutoSave() abort
if go#config#AsmfmtAutosave()
call go#config#SetAsmfmtAutosave(1)
call go#util#EchoProgress("auto asmfmt enabled")
return
end
call go#config#SetAsmfmtAutosave(0)
call go#util#EchoProgress("auto asmfmt disabled")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,139 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#auto#template_autocreate()
if !go#config#TemplateAutocreate() || !&modifiable
return
endif
" create new template from scratch
call go#template#create()
endfunction
function! go#auto#echo_go_info()
if !go#config#EchoGoInfo()
return
endif
if !exists('v:completed_item') || empty(v:completed_item)
return
endif
let item = v:completed_item
if !has_key(item, "info")
return
endif
if empty(item.info)
return
endif
redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None
endfunction
let s:timer_id = 0
" go#auto#update_autocmd() will be called on BufEnter,CursorHold. This
" configures the augroup according to conditions below.
"
" | # | has_timer | should_enable | do |
" |---|-----------|---------------|------------------------------------|
" | 1 | false | false | return early |
" | 2 | true | true | return early |
" | 3 | true | false | clear the group and stop the timer |
" | 4 | false | true | configure the group |
function! go#auto#update_autocmd()
let has_timer = get(b:, 'has_timer')
let should_enable = go#config#AutoTypeInfo() || go#config#AutoSameids()
if (!has_timer && !should_enable) || (has_timer && should_enable)
return
endif
if has_timer
augroup vim-go-buffer-auto
autocmd! * <buffer>
augroup END
let b:has_timer = 0
call s:timer_stop()
return
endif
augroup vim-go-buffer-auto
autocmd! * <buffer>
autocmd CursorMoved <buffer> call s:timer_restart()
autocmd BufLeave <buffer> call s:timer_stop()
augroup END
let b:has_timer = 1
call s:timer_start()
endfunction
function! s:timer_restart()
if isdirectory(expand('%:p:h'))
call s:timer_stop()
call s:timer_start()
endif
endfunction
function! s:timer_stop()
if s:timer_id
call timer_stop(s:timer_id)
let s:timer_id = 0
endif
endfunction
function! s:timer_start()
let s:timer_id = timer_start(go#config#Updatetime(), function('s:handler'))
endfunction
function! s:handler(timer_id)
if go#config#AutoTypeInfo()
call go#tool#Info(0)
endif
if go#config#AutoSameids()
call go#guru#SameIds(0)
endif
let s:timer_id = 0
endfunction
function! go#auto#fmt_autosave()
if !(go#config#FmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif
" Go code formatting on save
call go#fmt#Format(-1)
endfunction
function! go#auto#metalinter_autosave()
if !go#config#MetalinterAutosave() || !isdirectory(expand('%:p:h'))
return
endif
" run gometalinter on save
call go#lint#Gometa(!g:go_jump_to_error, 1)
endfunction
function! go#auto#modfmt_autosave()
if !(go#config#ModFmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif
" go.mod code formatting on save
call go#mod#Format()
endfunction
function! go#auto#asmfmt_autosave()
if !(go#config#AsmfmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif
" Go asm formatting on save
call go#asmfmt#Format()
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,353 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#cmd#autowrite() abort
if &autowrite == 1 || &autowriteall == 1
silent! wall
else
for l:nr in range(0, bufnr('$'))
if buflisted(l:nr) && getbufvar(l:nr, '&modified')
" Sleep one second to make sure people see the message. Otherwise it is
" often immediately overwritten by the async messages (which also
" doesn't invoke the "hit ENTER" prompt).
call go#util#EchoWarning('[No write since last change]')
sleep 1
return
endif
endfor
endif
endfunction
" Build builds the source code without producing any output binary. We live in
" an editor so the best is to build it to catch errors and fix them. By
" default it tries to call simply 'go build', but it first tries to get all
" dependent files for the current folder and passes it to go build.
function! go#cmd#Build(bang, ...) abort
" Create our command arguments. go build discards any results when it
" compiles multiple packages. So we pass the `errors` package just as an
" placeholder with the current folder (indicated with '.').
let l:args =
\ ['build', '-tags', go#config#BuildTags()] +
\ map(copy(a:000), "expand(v:val)") +
\ [".", "errors"]
" Vim and Neovim async
if go#util#has_job()
call s:cmd_job({
\ 'cmd': ['go'] + args,
\ 'bang': a:bang,
\ 'for': 'GoBuild',
\ 'statustype': 'build'
\})
" Vim without async
else
let l:status = {
\ 'desc': 'current status',
\ 'type': 'build',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
let default_makeprg = &makeprg
let &makeprg = "go " . join(go#util#Shelllist(args), ' ')
let l:listtype = go#list#Type("GoBuild")
" execute make inside the source folder so we can parse the errors
" correctly
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd . fnameescape(expand("%:p:h"))
if l:listtype == "locationlist"
silent! exe 'lmake!'
else
silent! exe 'make!'
endif
redraw!
finally
execute cd . fnameescape(dir)
let &makeprg = default_makeprg
endtry
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)
let l:status.state = 'failed'
else
let l:status.state = 'success'
if go#config#EchoCommandInfo()
call go#util#EchoSuccess("[build] SUCCESS")
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endif
endfunction
" BuildTags sets or shows the current build tags used for tools
function! go#cmd#BuildTags(bang, ...) abort
if a:0
let v = a:1
if v == '""' || v == "''"
let v = ""
endif
call go#config#SetBuildTags(v)
let tags = go#config#BuildTags()
if empty(tags)
call go#util#EchoSuccess("build tags are cleared")
else
call go#util#EchoSuccess("build tags are changed to: " . tags)
endif
return
endif
let tags = go#config#BuildTags()
if empty(tags)
call go#util#EchoSuccess("build tags are not set")
else
call go#util#EchoSuccess("current build tags: " . tags)
endif
endfunction
" Run runs the current file (and their dependencies if any) in a new terminal.
function! go#cmd#RunTerm(bang, mode, files) abort
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
endif
if empty(a:files)
let cmd .= go#util#Shelljoin(go#tool#Files())
else
let cmd .= go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
endif
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.
" This is intended to test small programs and play with them. It's not
" suitable for long running apps, because vim is blocking by default and
" calling long running apps will block the whole UI.
function! go#cmd#Run(bang, ...) abort
if has('nvim')
call go#cmd#RunTerm(a:bang, '', a:000)
return
endif
if go#util#has_job()
" NOTE(arslan): 'term': 'open' case is not implement for +jobs. This means
" executions waiting for stdin will not work. That's why we don't do
" anything. Once this is implemented we're going to make :GoRun async
endif
let l:status = {
\ 'desc': 'current status',
\ 'type': 'run',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
endif
if go#util#IsWin()
if a:0 == 0
exec '!' . cmd . go#util#Shelljoin(go#tool#Files(), 1)
else
exec '!' . cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif
let l:status.state = 'success'
if v:shell_error
let l:status.state = 'failed'
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoError('[run] FAILED')
endif
else
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoSuccess('[run] SUCCESS')
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
return
endif
" :make expands '%' and '#' wildcards, so they must also be escaped
let default_makeprg = &makeprg
if a:0 == 0
let &makeprg = cmd . go#util#Shelljoin(go#tool#Files(), 1)
else
let &makeprg = cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif
let l:listtype = go#list#Type("GoRun")
let l:status.state = 'success'
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 l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:errors)
let l:status.state = 'failed'
if !a:bang
call go#list#JumpToFirst(l:listtype)
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endfunction
" Install installs the package by simple calling 'go install'. If any argument
" is given(which are passed directly to 'go install') it tries to install
" those packages. Errors are populated in the location window.
function! go#cmd#Install(bang, ...) abort
" use vim's job functionality to call it asynchronously
if go#util#has_job()
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
call s:cmd_job({
\ 'cmd': ['go', 'install', '-tags', go#config#BuildTags()] + goargs,
\ 'bang': a:bang,
\ 'for': 'GoInstall',
\ 'statustype': 'install'
\})
return
endif
let default_makeprg = &makeprg
" :make expands '%' and '#' wildcards, so they must also be escaped
let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
let &makeprg = "go install " . goargs
let l:listtype = go#list#Type("GoInstall")
" execute make inside the source folder so we can parse the errors
" correctly
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd . fnameescape(expand("%:p:h"))
if l:listtype == "locationlist"
silent! exe 'lmake!'
else
silent! exe 'make!'
endif
redraw!
finally
execute cd . fnameescape(dir)
let &makeprg = default_makeprg
endtry
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 go#util#EchoSuccess("installed to ". go#path#Default())
endif
endfunction
" Generate runs 'go generate' in similar fashion to go#cmd#Build()
function! go#cmd#Generate(bang, ...) abort
let default_makeprg = &makeprg
" :make expands '%' and '#' wildcards, so they must also be escaped
let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
if go#util#ShellError() != 0
let &makeprg = "go generate " . goargs
else
let gofiles = go#util#Shelljoin(go#tool#Files(), 1)
let &makeprg = "go generate " . goargs . ' ' . gofiles
endif
let l:status = {
\ 'desc': 'current status',
\ 'type': 'generate',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
if go#config#EchoCommandInfo()
call go#util#EchoProgress('generating ...')
endif
let l:listtype = go#list#Type("GoGenerate")
try
if l:listtype == "locationlist"
silent! exe 'lmake!'
else
silent! exe 'make!'
endif
finally
redraw!
let &makeprg = default_makeprg
endtry
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors)
let l:status.status = 'failed'
if !a:bang
call go#list#JumpToFirst(l:listtype)
endif
else
let l:status.status = 'success'
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoSuccess('[generate] SUCCESS')
endif
endif
call go#statusline#Update(expand(':%:p:h'), l:status)
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 |
" ---------------------
function! s:cmd_job(args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()
call go#job#Spawn(a:args.cmd, a:args)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,38 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_GoBuildErrors()
try
let l:filename = 'cmd/bad.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/cmd'
" set the compiler type so that the errorformat option will be set
" correctly.
compiler go
let expected = [{'lnum': 4, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'undefined: notafunc'}]
" clear the quickfix lists
call setqflist([], 'r')
call go#cmd#Build(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,306 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! s:gocodeCommand(cmd, args) abort
let l:gocode_bin = "gocode"
let l:gomod = go#util#gomod()
if filereadable(l:gomod)
let l:gocode_bin = "gocode-gomod"
endif
let bin_path = go#path#CheckBinPath(l:gocode_bin)
if empty(bin_path)
return []
endif
let socket_type = go#config#GocodeSocketType()
let cmd = [bin_path]
let cmd = extend(cmd, ['-sock', socket_type])
let cmd = extend(cmd, ['-f', 'vim'])
if go#config#GocodeProposeBuiltins()
let cmd = extend(cmd, ['-builtin'])
endif
if go#config#GocodeProposeSource()
let cmd = extend(cmd, ['-source'])
else
let cmd = extend(cmd, ['-fallback-to-source', '-cache'])
endif
if go#config#GocodeUnimportedPackages()
let cmd = extend(cmd, ['-unimported-packages'])
endif
let cmd = extend(cmd, [a:cmd])
let cmd = extend(cmd, a:args)
return cmd
endfunction
function! s:sync_gocode(cmd, args, input) abort
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let old_goroot = $GOROOT
let $GOROOT = go#util#env("goroot")
try
let cmd = s:gocodeCommand(a:cmd, a:args)
" gocode can sometimes be slow, so redraw now to avoid waiting for gocode
" to return before redrawing automatically.
redraw
let [l:result, l:err] = go#util#Exec(cmd, a:input)
finally
let $GOROOT = old_goroot
endtry
if l:err != 0
return "[0, []]"
endif
if &encoding != 'utf-8'
let l:result = iconv(l:result, 'utf-8', &encoding)
endif
return l:result
endfunction
function! s:gocodeAutocomplete() abort
" use the offset as is, because the cursor position is the position for
" which autocomplete candidates are needed.
return s:sync_gocode('autocomplete',
\ [expand('%:p'), go#util#OffsetCursor()],
\ go#util#GetLines())
endfunction
" go#complete#GoInfo returns the description of the identifier under the
" cursor.
function! go#complete#GetInfo() abort
let l:mode = go#config#InfoMode()
if l:mode == 'gopls' && go#util#has_job()
return go#lsp#GetInfo()
else
return s:sync_info(0)
endif
endfunction
function! go#complete#Info(showstatus) abort
if go#util#has_job(1)
return s:async_info(1, a:showstatus)
else
return s:sync_info(1)
endif
endfunction
function! s:async_info(echo, showstatus)
let state = {'echo': a:echo}
" 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)
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let env = {
\ "GOROOT": go#util#env("goroot")
\ }
let opts = {
\ 'bang': 1,
\ 'complete': state.complete,
\ 'for': '_',
\ }
if a:showstatus
let opts.statustype = 'gocode'
endif
let opts = go#job#Options(l:opts)
let cmd = s:gocodeCommand('autocomplete',
\ [expand('%:p'), offset])
" TODO(bc): Don't write the buffer to a file; pass the buffer directly to
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
" doesn't work.
call extend(opts, {
\ 'env': env,
\ 'in_io': 'file',
\ 'in_name': s:gocodeFile(),
\ })
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)
return file
endfunction
function! s:sync_info(echo)
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
let result = s:sync_gocode('autocomplete',
\ [expand('%:p'), offset],
\ go#util#GetLines())
let result = s:info_filter(a:echo, result)
return s:info_complete(a:echo, result)
endfunction
function! s:info_filter(echo, result) abort
if empty(a:result)
return ""
endif
let l:result = eval(a:result)
if len(l:result) != 2
return ""
endif
let l:candidates = l:result[1]
if len(l:candidates) == 1
" When gocode panics in vim mode, it returns
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
if a:echo && l:candidates[0].info ==# "PANIC PANIC PANIC"
return ""
endif
return l:candidates[0].info
endif
let filtered = []
let wordMatch = '\<' . expand("<cword>") . '\>'
" escape single quotes in wordMatch before passing it to filter
let wordMatch = substitute(wordMatch, "'", "''", "g")
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
if len(l:filtered) == 0
return "no matches"
elseif len(l:filtered) > 1
return "ambiguous match"
endif
return l:filtered[0].info
endfunction
function! s:info_complete(echo, result) abort
if a:echo
call go#util#ShowInfo(a:result)
endif
return a:result
endfunction
function! s:trim_bracket(val) abort
let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '')
return a:val
endfunction
let s:completions = []
function! go#complete#GocodeComplete(findstart, base) abort
"findstart = 1 when we need to get the text length
if a:findstart == 1
let l:completions = []
execute "silent let l:completions = " . s:gocodeAutocomplete()
if len(l:completions) == 0 || len(l:completions) >= 2 && len(l:completions[1]) == 0
" no matches. cancel and leave completion mode.
call go#util#EchoInfo("no matches")
return -3
endif
let s:completions = l:completions[1]
return col('.') - l:completions[0] - 1
"findstart = 0 when we need to return the list of completions
else
let s = getline(".")[col('.') - 1]
if s =~ '[(){}\{\}]'
return map(copy(s:completions), 's:trim_bracket(v:val)')
endif
return s:completions
endif
endfunction
function! go#complete#Complete(findstart, base) abort
let l:state = {'done': 0, 'matches': [], 'start': -1}
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
"findstart = 1 when we need to get the start of the match
if a:findstart == 1
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:completion = go#lsp#Completion(expand('%:p'), l:line, l:col, funcref('s:handler', [l:state]))
if l:completion
return -3
endif
while !l:state.done
sleep 10m
endwhile
if len(l:state.matches) == 0
" no matches. cancel and leave completion mode.
call go#util#EchoInfo("no matches")
return -3
endif
let s:completions = l:state.matches
return go#lsp#lsp#PositionOf(getline(l:line+1), l:state.start-1)
else "findstart = 0 when we need to return the list of completions
return s:completions
endif
endfunction
function! go#complete#ToggleAutoTypeInfo() abort
if go#config#AutoTypeInfo()
call go#config#SetAutoTypeInfo(0)
call go#util#EchoProgress("auto type info disabled")
else
call go#config#SetAutoTypeInfo(1)
call go#util#EchoProgress("auto type info enabled")
endif
call go#auto#update_autocmd()
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,41 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_GetInfo_gocode()
let g:go_info_mode = 'gocode'
call s:getinfo()
unlet g:go_info_mode
endfunction
func! Test_GetInfo_guru()
let g:go_info_mode = 'guru'
call s:getinfo()
unlet g:go_info_mode
endfunction
func! Test_GetInfo_gopls()
let g:go_info_mode = 'gopls'
call s:getinfo()
unlet g:go_info_mode
endfunction
func! s:getinfo()
let l:filename = 'complete/complete.go'
let l:tmp = gotest#load_fixture(l:filename)
try
call cursor(8, 3)
let expected = 'func Example(s string)'
let actual = go#complete#GetInfo()
call assert_equal(expected, actual)
finally
call delete(l:tmp, 'rf')
endtry
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,531 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#config#AutodetectGopath() abort
return get(g:, 'go_autodetect_gopath', 0)
endfunction
function! go#config#ListTypeCommands() abort
return get(g:, 'go_list_type_commands', {})
endfunction
function! go#config#VersionWarning() abort
return get(g:, 'go_version_warning', 1)
endfunction
function! go#config#BuildTags() abort
return get(g:, 'go_build_tags', '')
endfunction
function! go#config#SetBuildTags(value) abort
if a:value is ''
silent! unlet g:go_build_tags
call go#lsp#ResetWorkspaceDirectories()
return
endif
let g:go_build_tags = a:value
call go#lsp#ResetWorkspaceDirectories()
endfunction
function! go#config#TestTimeout() abort
return get(g:, 'go_test_timeout', '10s')
endfunction
function! go#config#TestShowName() abort
return get(g:, 'go_test_show_name', 0)
endfunction
function! go#config#TermHeight() abort
return get(g:, 'go_term_height', winheight(0))
endfunction
function! go#config#TermWidth() abort
return get(g:, 'go_term_width', winwidth(0))
endfunction
function! go#config#TermMode() abort
return get(g:, 'go_term_mode', 'vsplit')
endfunction
function! go#config#TermCloseOnExit() abort
return get(g:, 'go_term_close_on_exit', 1)
endfunction
function! go#config#SetTermCloseOnExit(value) abort
let g:go_term_close_on_exit = a:value
endfunction
function! go#config#TermEnabled() abort
return has('nvim') && get(g:, 'go_term_enabled', 0)
endfunction
function! go#config#SetTermEnabled(value) abort
let g:go_term_enabled = a:value
endfunction
function! go#config#TemplateUsePkg() abort
return get(g:, 'go_template_use_pkg', 0)
endfunction
function! go#config#TemplateTestFile() abort
return get(g:, 'go_template_test_file', "hello_world_test.go")
endfunction
function! go#config#TemplateFile() abort
return get(g:, 'go_template_file', "hello_world.go")
endfunction
function! go#config#StatuslineDuration() abort
return get(g:, 'go_statusline_duration', 60000)
endfunction
function! go#config#SnippetEngine() abort
return get(g:, 'go_snippet_engine', 'automatic')
endfunction
function! go#config#PlayBrowserCommand() abort
if go#util#IsWin()
let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
elseif go#util#IsMac()
let go_play_browser_command = 'open %URL%'
elseif executable('xdg-open')
let go_play_browser_command = 'xdg-open %URL%'
elseif executable('firefox')
let go_play_browser_command = 'firefox %URL% &'
elseif executable('chromium')
let go_play_browser_command = 'chromium %URL% &'
else
let go_play_browser_command = ''
endif
return get(g:, 'go_play_browser_command', go_play_browser_command)
endfunction
function! go#config#MetalinterDeadline() abort
" gometalinter has a default deadline of 5 seconds only when asynchronous
" jobs are not supported.
let deadline = '5s'
if go#util#has_job() && has('lambda')
let deadline = ''
endif
return get(g:, 'go_metalinter_deadline', deadline)
endfunction
function! go#config#ListType() abort
return get(g:, 'go_list_type', '')
endfunction
function! go#config#ListAutoclose() abort
return get(g:, 'go_list_autoclose', 1)
endfunction
function! go#config#InfoMode() abort
return get(g:, 'go_info_mode', 'gopls')
endfunction
function! go#config#GuruScope() abort
let scope = get(g:, 'go_guru_scope', [])
if !empty(scope)
" strip trailing slashes for each path in scope. bug:
" https://github.com/golang/go/issues/14584
let scopes = go#util#StripTrailingSlash(scope)
endif
return scope
endfunction
function! go#config#SetGuruScope(scope) abort
if empty(a:scope)
if exists('g:go_guru_scope')
unlet g:go_guru_scope
endif
else
let g:go_guru_scope = a:scope
endif
endfunction
function! go#config#GocodeUnimportedPackages() abort
return get(g:, 'go_gocode_unimported_packages', 0)
endfunction
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
function! go#config#GocodeSocketType() abort
return get(g:, 'go_gocode_socket_type', s:sock_type)
endfunction
function! go#config#GocodeProposeBuiltins() abort
return get(g:, 'go_gocode_propose_builtins', 1)
endfunction
function! go#config#GocodeProposeSource() abort
return get(g:, 'go_gocode_propose_source', 0)
endfunction
function! go#config#EchoCommandInfo() abort
return get(g:, 'go_echo_command_info', 1)
endfunction
function! go#config#DocUrl() abort
let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
if godoc_url isnot 'https://godoc.org'
" strip last '/' character if available
let last_char = strlen(godoc_url) - 1
if godoc_url[last_char] == '/'
let godoc_url = strpart(godoc_url, 0, last_char)
endif
" custom godoc installations expect /pkg before package names
let godoc_url .= "/pkg"
endif
return godoc_url
endfunction
function! go#config#DocPopupWindow() abort
return get(g:, 'go_doc_popup_window', 0)
endfunction
function! go#config#DefReuseBuffer() abort
return get(g:, 'go_def_reuse_buffer', 0)
endfunction
function! go#config#DefMode() abort
return get(g:, 'go_def_mode', 'gopls')
endfunction
function! go#config#DeclsIncludes() abort
return get(g:, 'go_decls_includes', 'func,type')
endfunction
function! go#config#Debug() abort
return get(g:, 'go_debug', [])
endfunction
function! go#config#DebugWindows() abort
return get(g:, 'go_debug_windows', {
\ 'vars': 'leftabove 30vnew',
\ 'stack': 'leftabove 20new',
\ 'goroutines': 'botright 10new',
\ 'out': 'botright 5new',
\ }
\ )
endfunction
function! go#config#DebugAddress() abort
return get(g:, 'go_debug_address', '127.0.0.1:8181')
endfunction
function! go#config#DebugCommands() abort
" make sure g:go_debug_commands is set so that it can be added to easily.
let g:go_debug_commands = get(g:, 'go_debug_commands', [])
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', [])
return g:go_lsp_log
endfunction
function! go#config#SetDebugDiag(value) abort
let g:go_debug_diag = a:value
endfunction
function! go#config#AutoSameids() abort
return get(g:, 'go_auto_sameids', 0)
endfunction
function! go#config#SetAutoSameids(value) abort
let g:go_auto_sameids = a:value
endfunction
function! go#config#AddtagsTransform() abort
return get(g:, 'go_addtags_transform', "snakecase")
endfunction
function! go#config#TemplateAutocreate() abort
return get(g:, "go_template_autocreate", 1)
endfunction
function! go#config#SetTemplateAutocreate(value) abort
let g:go_template_autocreate = a:value
endfunction
function! go#config#MetalinterCommand() abort
return get(g:, "go_metalinter_command", "golangci-lint")
endfunction
function! go#config#MetalinterAutosaveEnabled() abort
let l:default_enabled = ["vet", "golint"]
if go#config#MetalinterCommand() == "golangci-lint"
let l:default_enabled = ["govet", "golint"]
endif
return get(g:, "go_metalinter_autosave_enabled", default_enabled)
endfunction
function! go#config#MetalinterEnabled() abort
let l:default_enabled = ["vet", "golint", "errcheck"]
if go#config#MetalinterCommand() == "golangci-lint"
let l:default_enabled = ["govet", "golint"]
endif
return get(g:, "go_metalinter_enabled", default_enabled)
endfunction
function! go#config#GolintBin() abort
return get(g:, "go_golint_bin", "golint")
endfunction
function! go#config#ErrcheckBin() abort
return get(g:, "go_errcheck_bin", "errcheck")
endfunction
function! go#config#MetalinterAutosave() abort
return get(g:, "go_metalinter_autosave", 0)
endfunction
function! go#config#SetMetalinterAutosave(value) abort
let g:go_metalinter_autosave = a:value
endfunction
function! go#config#ListHeight() abort
return get(g:, "go_list_height", 0)
endfunction
function! go#config#FmtAutosave() abort
return get(g:, "go_fmt_autosave", 1)
endfunction
function! go#config#SetFmtAutosave(value) abort
let g:go_fmt_autosave = a:value
endfunction
function! go#config#AsmfmtAutosave() abort
return get(g:, "go_asmfmt_autosave", 0)
endfunction
function! go#config#SetAsmfmtAutosave(value) abort
let g:go_asmfmt_autosave = a:value
endfunction
function! go#config#ModFmtAutosave() abort
return get(g:, "go_mod_fmt_autosave", 1)
endfunction
function! go#config#SetModFmtAutosave(value) abort
let g:go_mod_fmt_autosave = a:value
endfunction
function! go#config#DocMaxHeight() abort
return get(g:, "go_doc_max_height", 20)
endfunction
function! go#config#AutoTypeInfo() abort
return get(g:, "go_auto_type_info", 0)
endfunction
function! go#config#SetAutoTypeInfo(value) abort
let g:go_auto_type_info = a:value
endfunction
function! go#config#AlternateMode() abort
return get(g:, "go_alternate_mode", "edit")
endfunction
function! go#config#DeclsMode() abort
return get(g:, "go_decls_mode", "")
endfunction
function! go#config#FmtCommand() abort
return get(g:, "go_fmt_command", "gofmt")
endfunction
function! go#config#FmtOptions() abort
return get(g:, "go_fmt_options", {})
endfunction
function! go#config#FmtFailSilently() abort
return get(g:, "go_fmt_fail_silently", 0)
endfunction
function! go#config#FmtExperimental() abort
return get(g:, "go_fmt_experimental", 0 )
endfunction
function! go#config#PlayOpenBrowser() abort
return get(g:, "go_play_open_browser", 1)
endfunction
function! go#config#GorenameCommand() abort
" delegate to go#config#GorenameBin for backwards compatability.
return get(g:, "go_gorename_command", go#config#GorenameBin())
endfunction
function! go#config#GorenameBin() abort
return get(g:, "go_gorename_bin", "gorename")
endfunction
function! go#config#GorenamePrefill() abort
return get(g:, "go_gorename_prefill", 'expand("<cword>") =~# "^[A-Z]"' .
\ '? go#util#pascalcase(expand("<cword>"))' .
\ ': go#util#camelcase(expand("<cword>"))')
endfunction
function! go#config#TextobjIncludeFunctionDoc() abort
return get(g:, "go_textobj_include_function_doc", 1)
endfunction
function! go#config#TextobjIncludeVariable() abort
return get(g:, "go_textobj_include_variable", 1)
endfunction
function! go#config#BinPath() abort
return get(g:, "go_bin_path", "")
endfunction
function! go#config#SearchBinPathFirst() abort
return get(g:, 'go_search_bin_path_first', 1)
endfunction
function! go#config#HighlightArrayWhitespaceError() abort
return get(g:, 'go_highlight_array_whitespace_error', 0)
endfunction
function! go#config#HighlightChanWhitespaceError() abort
return get(g:, 'go_highlight_chan_whitespace_error', 0)
endfunction
function! go#config#HighlightExtraTypes() abort
return get(g:, 'go_highlight_extra_types', 0)
endfunction
function! go#config#HighlightSpaceTabError() abort
return get(g:, 'go_highlight_space_tab_error', 0)
endfunction
function! go#config#HighlightTrailingWhitespaceError() abort
return get(g:, 'go_highlight_trailing_whitespace_error', 0)
endfunction
function! go#config#HighlightOperators() abort
return get(g:, 'go_highlight_operators', 0)
endfunction
function! go#config#HighlightFunctions() abort
return get(g:, 'go_highlight_functions', 0)
endfunction
function! go#config#HighlightFunctionParameters() abort
" fallback to highlight_function_arguments for backwards compatibility
return get(g:, 'go_highlight_function_parameters', get(g:, 'go_highlight_function_arguments', 0))
endfunction
function! go#config#HighlightFunctionCalls() abort
return get(g:, 'go_highlight_function_calls', 0)
endfunction
function! go#config#HighlightFields() abort
return get(g:, 'go_highlight_fields', 0)
endfunction
function! go#config#HighlightTypes() abort
return get(g:, 'go_highlight_types', 0)
endfunction
function! go#config#HighlightBuildConstraints() abort
return get(g:, 'go_highlight_build_constraints', 0)
endfunction
function! go#config#HighlightStringSpellcheck() abort
return get(g:, 'go_highlight_string_spellcheck', 1)
endfunction
function! go#config#HighlightFormatStrings() abort
return get(g:, 'go_highlight_format_strings', 1)
endfunction
function! go#config#HighlightGenerateTags() abort
return get(g:, 'go_highlight_generate_tags', 0)
endfunction
function! go#config#HighlightVariableAssignments() abort
return get(g:, 'go_highlight_variable_assignments', 0)
endfunction
function! go#config#HighlightVariableDeclarations() abort
return get(g:, 'go_highlight_variable_declarations', 0)
endfunction
function! go#config#HighlightDiagnosticErrors() abort
return get(g:, 'go_highlight_diagnostic_errors', 1)
endfunction
function! go#config#HighlightDiagnosticWarnings() abort
return get(g:, 'go_highlight_diagnostic_warnings', 1)
endfunction
function! go#config#HighlightDebug() abort
return get(g:, 'go_highlight_debug', 1)
endfunction
function! go#config#FoldEnable(...) abort
if a:0 > 0
return index(go#config#FoldEnable(), a:1) > -1
endif
return get(g:, 'go_fold_enable', ['block', 'import', 'varconst', 'package_comment'])
endfunction
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
function! go#config#Updatetime() abort
let go_updatetime = get(g:, 'go_updatetime', 800)
return go_updatetime == 0 ? &updatetime : go_updatetime
endfunction
function! go#config#ReferrersMode() abort
return get(g:, 'go_referrers_mode', 'gopls')
endfunction
function! go#config#GoplsCompleteUnimported() abort
return get(g:, 'go_gopls_complete_unimported', 0)
endfunction
function! go#config#GoplsDeepCompletion() abort
return get(g:, 'go_gopls_deep_completion', 1)
endfunction
function! go#config#GoplsFuzzyMatching() abort
return get(g:, 'go_gopls_fuzzy_matching', 1)
endfunction
function! go#config#GoplsUsePlaceholders() abort
return get(g:, 'go_gopls_use_placeholders', 0)
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
unlet g:go_gorename_prefill
endif
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,86 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
func! Test_SetBuildTags() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:dir = 'test-fixtures/config/buildtags'
let l:jumpstart = [0, 4, 2, 0]
execute 'e ' . printf('%s/buildtags.go', l:dir)
let l:jumpstartbuf = bufnr('')
call setpos('.', [l:jumpstartbuf, l:jumpstart[1], l:jumpstart[2], 0])
let l:expectedfilename = printf('%s/foo.go', l:dir)
let l:expected = [0, 5, 1, 0]
call assert_notequal(l:expected, l:jumpstart)
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expectedfilename, bufname("%"))
call assert_equal(l:expected, getpos('.'))
execute 'e ' . printf('%s/buildtags.go', l:dir)
" prepare to wait for the workspace/configuration request
let g:go_debug=['lsp']
" set the build constraint
call go#config#SetBuildTags('constrained')
" wait for the workspace/configuration request
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
let l:start = reltime()
while match(l:lsplog, 'workspace/configuration') == -1 && reltimefloat(reltime(l:start)) < 10
sleep 50m
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
endwhile
unlet g:go_debug
" close the __GOLSP_LOG__ window
only
" verify the cursor position within buildtags.go
call setpos('.', [l:jumpstartbuf, l:jumpstart[1], l:jumpstart[2], 0])
call assert_equal(l:jumpstart, getpos('.'))
let l:expectedfilename = printf('%s/constrainedfoo.go', l:dir)
let l:expected = [0, 6, 1, 0]
call assert_notequal(l:expected, l:jumpstart)
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expectedfilename, bufname("%"))
call assert_equal(l:expected, getpos('.'))
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
finally
call go#config#SetBuildTags('')
unlet g:go_def_mode
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,297 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:toggle = 0
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
" the current buffers highlighting to show covered and uncovered sections of
" the code. If run again it clears the annotation.
function! go#coverage#BufferToggle(bang, ...) abort
if s:toggle
call go#coverage#Clear()
return
endif
if a:0 == 0
return call(function('go#coverage#Buffer'), [a:bang])
endif
return call(function('go#coverage#Buffer'), [a:bang] + a:000)
endfunction
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
" the current buffers highlighting to show covered and uncovered sections of
" the code. Calling it again reruns the tests and shows the last updated
" coverage.
function! go#coverage#Buffer(bang, ...) abort
" check if the version of Vim being tested supports matchaddpos()
if !exists("*matchaddpos")
call go#util#EchoError("GoCoverage is not supported by your version of Vim.")
return -1
endif
" check if there is any test file, if not we just return
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd . fnameescape(expand("%:p:h"))
if empty(glob("*_test.go"))
call go#util#EchoError("no test files available")
return
endif
finally
execute cd . fnameescape(dir)
endtry
let s:toggle = 1
let l:tmpname = tempname()
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000,
\ 'complete': function('s:coverage_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ 'statustype': 'coverage',
\ })
return
endif
if go#config#EchoCommandInfo()
call go#util#EchoProgress("testing...")
endif
let args = [a:bang, 0, "-coverprofile", l:tmpname]
if a:0
call extend(args, a:000)
endif
let id = call('go#test#Test', args)
if go#util#ShellError() == 0
call go#coverage#overlay(l:tmpname)
endif
call delete(l:tmpname)
endfunction
" Clear clears and resets the buffer annotation matches
function! go#coverage#Clear() abort
call clearmatches()
if exists("s:toggle") | let s:toggle = 0 | endif
" remove the autocmd we defined
augroup vim-go-coverage
autocmd! * <buffer>
augroup end
endfunction
" Browser creates a new cover profile with 'go test -coverprofile' and opens
" a new HTML coverage page from that profile in a new browser
function! go#coverage#Browser(bang, ...) abort
let l:tmpname = tempname()
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname],
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ 'statustype': 'coverage',
\ })
return
endif
let args = [a:bang, 0, "-coverprofile", l:tmpname]
if a:0
call extend(args, a:000)
endif
let id = call('go#test#Test', args)
if go#util#ShellError() == 0
call go#util#ExecInDir(['go', 'tool', 'cover', '-html=' . l:tmpname])
endif
call delete(l:tmpname)
endfunction
" Parses a single line from the cover file generated via go test -coverprofile
" and returns a single coverage profile block.
function! go#coverage#parsegocoverline(line) abort
" file:startline.col,endline.col numstmt count
let mx = '\([^:]\+\):\(\d\+\)\.\(\d\+\),\(\d\+\)\.\(\d\+\)\s\(\d\+\)\s\(\d\+\)'
let tokens = matchlist(a:line, mx)
let ret = {}
let ret.file = tokens[1]
let ret.startline = str2nr(tokens[2])
let ret.startcol = str2nr(tokens[3])
let ret.endline = str2nr(tokens[4])
let ret.endcol = str2nr(tokens[5])
let ret.numstmt = tokens[6]
let ret.cnt = tokens[7]
return ret
endfunction
" Generates matches to be added to matchaddpos for the given coverage profile
" block
function! go#coverage#genmatch(cov) abort
let color = 'goCoverageCovered'
if a:cov.cnt == 0
let color = 'goCoverageUncover'
endif
let matches = []
" if start and end are the same, also specify the byte length
" example: foo.go:92.2,92.65 1 0
if a:cov.startline == a:cov.endline
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.startline, a:cov.startcol, a:cov.endcol - a:cov.startcol]],
\ 'priority': 2,
\ })
return matches
endif
" add start columns. Because we don't know the length of the of
" the line, we assume it is at maximum 200 bytes. I know this is hacky,
" but that's only way of fixing the issue
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.startline, a:cov.startcol, 200]],
\ 'priority': 2,
\ })
" and then the remaining lines
let start_line = a:cov.startline
while start_line < a:cov.endline
let start_line += 1
call add(matches, {
\ 'group': color,
\ 'pos': [[start_line]],
\ 'priority': 2,
\ })
endwhile
" finally end columns
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.endline, a:cov.endcol-1]],
\ 'priority': 2,
\ })
return matches
endfunction
" Reads the given coverprofile file and annotates the current buffer
function! go#coverage#overlay(file) abort
if !filereadable(a:file)
return
endif
let lines = readfile(a:file)
" cover mode, by default it's 'set'. Just here for debugging purposes
let mode = lines[0]
" contains matches for matchaddpos()
let matches = []
" first mark all lines as goCoverageNormalText. We use a custom group to not
" interfere with other buffers highlightings. Because the priority is
" lower than the cover and uncover matches, it'll be overridden.
let cnt = 1
while cnt <= line('$')
call add(matches, {'group': 'goCoverageNormalText', 'pos': [cnt], 'priority': 1})
let cnt += 1
endwhile
let fname = expand('%')
" when called for a _test.go file, run the coverage for the actuall file
" file
if fname =~# '^\f\+_test\.go$'
let l:root = split(fname, '_test.go$')[0]
let fname = l:root . ".go"
if !filereadable(fname)
call go#util#EchoError("couldn't find ".fname)
return
endif
" open the alternate file to show the coverage
exe ":edit ". fnamemodify(fname, ":p")
endif
" cov.file includes only the filename itself, without full path
let fname = fnamemodify(fname, ":t")
for line in lines[1:]
let cov = go#coverage#parsegocoverline(line)
" TODO(arslan): for now only include the coverage for the current
" buffer
if fname != fnamemodify(cov.file, ':t')
continue
endif
call extend(matches, go#coverage#genmatch(cov))
endfor
" clear the matches if we leave the buffer
augroup vim-go-coverage
autocmd! * <buffer>
autocmd BufWinLeave <buffer> call go#coverage#Clear()
augroup end
for m in matches
call matchaddpos(m.group, m.pos)
endfor
endfunction
" ---------------------
" | Vim job callbacks |
" ---------------------
"
function s:coverage_job(args)
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let disabled_term = 0
if go#config#TermEnabled()
let disabled_term = 1
call go#config#SetTermEnabled(0)
endif
call go#job#Spawn(a:args.cmd, a:args)
if disabled_term
call go#config#SetTermEnabled(1)
endif
endfunction
" coverage_callback is called when the coverage execution is finished
function! s:coverage_callback(coverfile, job, exit_status, data)
if a:exit_status == 0
call go#coverage#overlay(a:coverfile)
endif
call delete(a:coverfile)
endfunction
function! s:coverage_browser_callback(coverfile, job, exit_status, data)
if a:exit_status == 0
call go#util#ExecInDir(['go', 'tool', 'cover', '-html=' . a:coverfile])
endif
call delete(a:coverfile)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

File diff suppressed because it is too large Load Diff

View File

@ -1,114 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! Test_GoDebugStart_Empty() abort
call s:debug()
endfunction
function! Test_GoDebugStart_RelativePackage() abort
call s:debug('./debug/debugmain')
endfunction
function! Test_GoDebugStart_RelativePackage_NullModule() abort
call s:debug('./debug/debugmain', 1)
endfunction
function! Test_GoDebugStart_Package() abort
call s:debug('debug/debugmain')
endfunction
function! Test_GoDebugStart_Errors() abort
if !go#util#has_job()
return
endif
try
let l:tmp = gotest#load_fixture('debug/compilerror/main.go')
let l:expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': '# debug/compilerror'},
\ {'lnum': 6, 'bufnr': bufnr('%'), 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ' syntax error: unexpected newline, expecting comma or )'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exit status 2'}
\]
call setqflist([], 'r')
call assert_false(exists(':GoDebugStop'))
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
execute l:cd . ' debug/compilerror'
call go#debug#Start(0)
let l:actual = getqflist()
let l:start = reltime()
while len(l:actual) == 0 && reltimefloat(reltime(l:start)) < 10
sleep 100m
let l:actual = getqflist()
endwhile
call gotest#assert_quickfix(l:actual, l:expected)
call assert_false(exists(':GoDebugStop'))
finally
call delete(l:tmp, 'rf')
" clear the quickfix lists
call setqflist([], 'r')
endtry
endfunction
" s:debug takes 2 optional arguments. The first is a package to debug. The
" second is a flag to indicate whether to reset GOPATH after
" gotest#load_fixture is called in order to test behavior outside of GOPATH.
function! s:debug(...) abort
if !go#util#has_job()
return
endif
try
let $oldgopath = $GOPATH
let l:tmp = gotest#load_fixture('debug/debugmain/debugmain.go')
if a:0 > 1 && a:2 == 1
let $GOPATH = $oldgopath
endif
call go#debug#Breakpoint(6)
call assert_false(exists(':GoDebugStop'))
if a:0 == 0
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
execute l:cd . ' debug/debugmain'
let l:job = go#debug#Start(0)
else
let l:job = go#debug#Start(0, a:1)
endif
let l:start = reltime()
while !exists(':GoDebugStop') && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_true(exists(':GoDebugStop'))
call gotest#assert_quickfix(getqflist(), [])
call go#debug#Stop()
if !has('nvim')
call assert_equal(job_status(l:job), 'dead')
endif
call assert_false(exists(':GoDebugStop'))
finally
call go#debug#Breakpoint(6)
call delete(l:tmp, 'rf')
endtry
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,26 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#decls#Decls(mode, ...) abort
let decls_mode = go#config#DeclsMode()
if decls_mode == 'ctrlp'
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
elseif decls_mode == 'fzf'
call call("fzf#decls#cmd", [a:mode] + a:000)
else
if globpath(&rtp, 'plugin/ctrlp.vim') != ""
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
elseif globpath(&rtp, 'plugin/fzf.vim') != ""
call call("fzf#decls#cmd", [a:mode] + a:000)
else
call go#util#EchoError("neither ctrlp.vim nor fzf.vim are installed. Please install either one")
end
end
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,358 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:go_stack = []
let s:go_stack_level = 0
function! go#def#Jump(mode, type) abort
let l:fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
" so guru right now is slow for some people. previously we were using
" godef which also has it's own quirks. But this issue come up so many
" times I've decided to support both. By default we still use guru as it
" covers all edge cases, but now anyone can switch to godef if they wish
let bin_name = go#config#DefMode()
if bin_name == 'godef'
let l:cmd = ['godef',
\ '-f=' . l:fname,
\ '-o=' . go#util#OffsetCursor(),
\ '-t']
if &modified
let l:stdin_content = join(go#util#GetLines(), "\n")
call add(l:cmd, "-i")
let [l:out, l:err] = go#util#ExecInDir(l:cmd, l:stdin_content)
else
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
endif
elseif bin_name == 'guru'
let cmd = [go#path#CheckBinPath(bin_name)]
let buildtags = go#config#BuildTags()
if buildtags isnot ''
let cmd += ['-tags', buildtags]
endif
let stdin_content = ""
if &modified
let content = join(go#util#GetLines(), "\n")
let stdin_content = fname . "\n" . strlen(content) . "\n" . content
call add(cmd, "-modified")
endif
call extend(cmd, ["definition", fname . ':#' . go#util#OffsetCursor()])
if go#util#has_job()
let l:state = {}
let l:spawn_args = {
\ 'cmd': cmd,
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name], l:state),
\ 'for': '_',
\ 'statustype': 'searching declaration',
\ }
if &modified
let l:spawn_args.input = stdin_content
endif
call s:def_job(spawn_args, l:state)
return
endif
if &modified
let [l:out, l:err] = go#util#ExecInDir(l:cmd, l:stdin_content)
else
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
endif
elseif bin_name == 'gopls'
let [l:line, l:col] = go#lsp#lsp#Position()
" delegate to gopls, with an empty job object and an exit status of 0
" (they're irrelevant for gopls).
if a:type
call go#lsp#TypeDef(l:fname, l:line, l:col, function('s:jump_to_declaration_cb', [a:mode, 'gopls', {}, 0]))
else
call go#lsp#Definition(l:fname, l:line, l:col, function('s:jump_to_declaration_cb', [a:mode, 'gopls', {}, 0]))
endif
return
else
call go#util#EchoError('go_def_mode value: '. bin_name .' is not valid. Valid values are: [godef, guru, gopls]')
return
endif
if l:err
call go#util#EchoError(out)
return
endif
call go#def#jump_to_declaration(out, a:mode, bin_name)
endfunction
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort dict
if a:exit_status != 0
return
endif
call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name)
" capture the active window so that callbacks for jobs, exit_cb and
" close_cb, and callbacks for gopls can return to it when a:mode caused a
" split.
let self.winid = win_getid(winnr())
endfunction
" go#def#jump_to_declaration parses out (expected to be
" 'filename:line:col: message').
function! go#def#jump_to_declaration(out, mode, bin_name) abort
let final_out = a:out
if a:bin_name == "godef"
" append the type information to the same line so it will be parsed
" correctly using guru's output format.
" This makes it compatible with guru output.
let final_out = join(split(a:out, '\n'), ':')
endif
" strip line ending
let out = split(final_out, go#util#LineEnding())[0]
if go#util#IsWin()
let parts = split(out, '\(^[a-zA-Z]\)\@<!:')
else
let parts = split(out, ':')
endif
if len(parts) == 0
call go#util#EchoError('go jump_to_declaration '. a:bin_name .' output is not valid.')
return
endif
let line = 1
let col = 1
let ident = 0
let filename = parts[0]
if len(parts) > 1
let line = parts[1]
endif
if len(parts) > 2
let col = parts[2]
endif
if len(parts) > 3
let ident = parts[3]
endif
" Remove anything newer than the current position, just like basic
" vim tag support
if s:go_stack_level == 0
let s:go_stack = []
else
let s:go_stack = s:go_stack[0:s:go_stack_level-1]
endif
" increment the stack counter
let s:go_stack_level += 1
" push it on to the jumpstack
let stack_entry = {'line': line("."), 'col': col("."), 'file': expand('%:p'), 'ident': ident}
call add(s:go_stack, stack_entry)
" needed for restoring back user setting this is because there are two
" modes of switchbuf which we need based on the split mode
let old_switchbuf = &switchbuf
normal! m'
if filename != fnamemodify(expand("%"), ':p:gs?\\?/?')
" jump to existing buffer if, 1. we have enabled it, 2. the buffer is loaded
" and 3. there is buffer window number we switch to
if go#config#DefReuseBuffer() && bufwinnr(filename) != -1
" jump to existing buffer if it exists
call win_gotoid(bufwinnr(filename))
else
if &modified
let cmd = 'hide edit'
else
let cmd = 'edit'
endif
if a:mode == "tab"
let &switchbuf = "useopen,usetab,newtab"
if bufloaded(filename) == 0
tab split
else
let cmd = 'sbuf'
endif
elseif a:mode == "split"
split
elseif a:mode == "vsplit"
vsplit
endif
" open the file and jump to line and column
try
exec cmd fnameescape(fnamemodify(filename, ':.'))
catch
if stridx(v:exception, ':E325:') < 0
call go#util#EchoError(v:exception)
endif
endtry
endif
endif
call cursor(line, col)
" also align the line to middle of the view
normal! zz
let &switchbuf = old_switchbuf
endfunction
function! go#def#SelectStackEntry() abort
let target_window = go#ui#GetReturnWindow()
if empty(target_window)
let target_window = winnr()
endif
let highlighted_stack_entry = matchstr(getline("."), '^..\zs\(\d\+\)')
if !empty(highlighted_stack_entry)
execute target_window . "wincmd w"
call go#def#Stack(str2nr(highlighted_stack_entry))
endif
call go#ui#CloseWindow()
endfunction
function! go#def#StackUI() abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
let stackOut = ['" <Up>,<Down>:navigate <Enter>:jump <Esc>,q:exit']
let i = 0
while i < len(s:go_stack)
let entry = s:go_stack[i]
let prefix = ""
if i == s:go_stack_level
let prefix = ">"
else
let prefix = " "
endif
call add(stackOut, printf("%s %d %s|%d col %d|%s",
\ prefix, i+1, entry["file"], entry["line"], entry["col"], entry["ident"]))
let i += 1
endwhile
if s:go_stack_level == i
call add(stackOut, "> ")
endif
call go#ui#OpenWindow("GoDef Stack", stackOut, "godefstack")
noremap <buffer> <silent> <CR> :<C-U>call go#def#SelectStackEntry()<CR>
noremap <buffer> <silent> <Esc> :<C-U>call go#ui#CloseWindow()<CR>
noremap <buffer> <silent> q :<C-U>call go#ui#CloseWindow()<CR>
endfunction
function! go#def#StackClear(...) abort
let s:go_stack = []
let s:go_stack_level = 0
endfunction
function! go#def#StackPop(...) abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
if s:go_stack_level == 0
call go#util#EchoError("at bottom of the godef stack")
return
endif
if !len(a:000)
let numPop = 1
else
let numPop = a:1
endif
let newLevel = str2nr(s:go_stack_level) - str2nr(numPop)
call go#def#Stack(newLevel + 1)
endfunction
function! go#def#Stack(...) abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
if !len(a:000)
" Display interactive stack
call go#def#StackUI()
return
else
let jumpTarget = a:1
endif
if jumpTarget !~ '^\d\+$'
if jumpTarget !~ '^\s*$'
call go#util#EchoError("location must be a number")
endif
return
endif
let jumpTarget = str2nr(jumpTarget) - 1
if jumpTarget >= 0 && jumpTarget < len(s:go_stack)
let s:go_stack_level = jumpTarget
let target = s:go_stack[s:go_stack_level]
" jump
if expand('%:p') != target["file"]
if &modified
exec 'hide edit' target["file"]
else
exec 'edit' target["file"]
endif
endif
call cursor(target["line"], target["col"])
normal! zz
else
call go#util#EchoError("invalid location. Try :GoDefStack to see the list of valid entries")
endif
endfunction
function s:def_job(args, state) abort
let l:start_options = go#job#Options(a:args)
let l:state = a:state
function! s:exit_cb(next, job, exitval) dict
call call(a:next, [a:job, a:exitval])
if has_key(self, 'winid')
call win_gotoid(self.winid)
endif
endfunction
let l:start_options.exit_cb = funcref('s:exit_cb', [l:start_options.exit_cb], l:state)
function! s:close_cb(next, ch) dict
call call(a:next, [a:ch])
if has_key(self, 'winid')
call win_gotoid(self.winid)
endif
endfunction
let l:start_options.close_cb = funcref('s:close_cb', [l:start_options.close_cb], l:state)
if &modified
let l:tmpname = tempname()
call writefile(split(a:args.input, "\n"), l:tmpname, "b")
let l:start_options.in_io = "file"
let l:start_options.in_name = l:tmpname
endif
call go#job#Start(a:args.cmd, l:start_options)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,223 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
func! Test_jump_to_declaration_guru() abort
try
let l:filename = 'def/jump.go'
let l:lnum = 5
let l:col = 6
let l:tmp = gotest#load_fixture(l:filename)
let l:guru_out = printf("%s:%d:%d: defined here as func main", l:filename, l:lnum, l:col)
call go#def#jump_to_declaration(l:guru_out, "", 'guru')
call assert_equal(l:filename, bufname("%"))
call assert_equal(l:lnum, getcurpos()[1])
call assert_equal(l:col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_jump_to_declaration_godef() abort
try
let l:filename = 'def/jump.go'
let l:lnum = 5
let l:col = 6
let l:tmp = gotest#load_fixture(l:filename)
let l:godef_out = printf("%s:%d:%d\ndefined here as func main", l:filename, l:lnum, l:col)
call go#def#jump_to_declaration(godef_out, "", 'godef')
call assert_equal(l:filename, bufname("%"))
call assert_equal(l:lnum, getcurpos()[1])
call assert_equal(l:col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Jump_leaves_lists() abort
try
let l:filename = 'def/jump.go'
let l:tmp = gotest#load_fixture(l:filename)
let l:expected = [{'lnum': 10, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'quux'}]
call setloclist(winnr(), copy(l:expected), 'r' )
call setqflist(copy(l:expected), 'r' )
let l:bufnr = bufnr('%')
call cursor(6, 7)
if !go#util#has_job()
let g:go_def_mode='godef'
endif
call go#def#Jump('', 0)
if !go#util#has_job()
unlet g:go_def_mode
endif
let l:start = reltime()
while bufnr('%') == l:bufnr && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
let l:actual = getloclist(winnr())
call gotest#assert_quickfix(l:actual, l:expected)
let l:actual = getqflist()
call gotest#assert_quickfix(l:actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_DefJump_gopls_simple_first() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('simple/firstposition/firstposition.go', [
\ 'package firstposition',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" . '"id:", id)',
\ '}',
\ ] )
let l:expected = [0, 4, 2, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_simple_last() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('simple/lastposition/lastposition.go', [
\ 'package lastposition',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" . '"id:", id)',
\ '}',
\ ] )
let l:expected = [0, 4, 2, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_MultipleCodeUnit_first() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('multiplecodeunit/firstposition/firstposition.go', [
\ 'package firstposition',
\ '',
\ 'func Example() {',
\ "\t𐐀, id := " . '"foo", "bar"',
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
\ '}',
\ ] )
let l:expected = [0, 4, 8, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_MultipleCodeUnit_last() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('multiplecodeunit/lastposition/lastposition.go', [
\ 'package lastposition',
\ '',
\ 'func Example() {',
\ "\t𐐀, id := " . '"foo", "bar"',
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
\ '}',
\ ] )
let l:expected = [0, 4, 8, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,247 +0,0 @@
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:buf_nr = -1
function! go#doc#OpenBrowser(...) abort
" check if we have gogetdoc as it gives us more and accurate information.
" Only supported if we have json_decode as it's not worth to parse the plain
" non-json output of gogetdoc
let bin_path = go#path#CheckBinPath('gogetdoc')
if !empty(bin_path) && exists('*json_decode')
let [l:json_out, l:err] = s:gogetdoc(1)
if l:err
call go#util#EchoError(json_out)
return
endif
let out = json_decode(json_out)
if type(out) != type({})
call go#util#EchoError("gogetdoc output is malformed")
endif
let import = out["import"]
let name = out["name"]
let decl = out["decl"]
let godoc_url = go#config#DocUrl()
let godoc_url .= "/" . import
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)
return
endif
let pkgs = s:godocWord(a:000)
if empty(pkgs)
return
endif
let pkg = pkgs[0]
let exported_name = pkgs[1]
" example url: https://godoc.org/github.com/fatih/set#Set
let godoc_url = go#config#DocUrl() . "/" . pkg . "#" . exported_name
call go#util#OpenBrowser(godoc_url)
endfunction
function! go#doc#Open(newmode, mode, ...) abort
" With argument: run "godoc [arg]".
if len(a:000)
let [l:out, l:err] = go#util#Exec(['go', 'doc'] + a:000)
else " Without argument: run gogetdoc on cursor position.
let [l:out, l:err] = s:gogetdoc(0)
if out == -1
return
endif
endif
if l:err
call go#util#EchoError(out)
return
endif
call s:GodocView(a:newmode, a:mode, out)
endfunction
function! s:GodocView(newposition, position, content) abort
" popup window
if go#config#DocPopupWindow()
if exists('*popup_atcursor') && exists('*popup_clear')
call popup_clear()
call popup_atcursor(split(a:content, '\n'), {
\ 'padding': [1, 1, 1, 1],
\ 'borderchars': ['-','|','-','|','+','+','+','+'],
\ "border": [1, 1, 1, 1],
\ })
elseif has('nvim') && exists('*nvim_open_win')
let lines = split(a:content, '\n')
let height = 0
let width = 0
for line in lines
let lw = strdisplaywidth(line)
if lw > width
let width = lw
endif
let height += 1
endfor
let width += 1 " right margin
let max_height = go#config#DocMaxHeight()
if height > max_height
let height = max_height
endif
let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, lines)
let opts = {
\ 'relative': 'cursor',
\ 'row': 1,
\ 'col': 0,
\ 'width': width,
\ 'height': height,
\ 'style': 'minimal',
\ }
call nvim_open_win(buf, v:true, opts)
setlocal nomodified nomodifiable filetype=godoc
" close easily with CR, Esc and q
noremap <buffer> <silent> <CR> :<C-U>close<CR>
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
noremap <buffer> <silent> q :<C-U>close<CR>
endif
return
endif
" reuse existing buffer window if it exists otherwise create a new one
let is_visible = bufexists(s:buf_nr) && bufwinnr(s:buf_nr) != -1
if !bufexists(s:buf_nr)
execute a:newposition
sil file `="[Godoc]"`
let s:buf_nr = bufnr('%')
elseif bufwinnr(s:buf_nr) == -1
execute a:position
execute s:buf_nr . 'buffer'
elseif bufwinnr(s:buf_nr) != bufwinnr('%')
execute bufwinnr(s:buf_nr) . 'wincmd w'
endif
" if window was not visible then resize it
if !is_visible
if a:position == "split"
" cap window height to 20, but resize it for smaller contents
let max_height = go#config#DocMaxHeight()
let content_height = len(split(a:content, "\n"))
if content_height > max_height
exe 'resize ' . max_height
else
exe 'resize ' . content_height
endif
else
" set a sane maximum width for vertical splits. In this case the minimum
" that fits the godoc for package http without extra linebreaks and line
" numbers on
exe 'vertical resize 84'
endif
endif
setlocal filetype=godoc
setlocal bufhidden=delete
setlocal buftype=nofile
setlocal noswapfile
setlocal nobuflisted
setlocal nocursorline
setlocal nocursorcolumn
setlocal iskeyword+=:
setlocal iskeyword-=-
setlocal modifiable
%delete _
call append(0, split(a:content, "\n"))
sil $delete _
setlocal nomodifiable
sil normal! gg
" close easily with enter
noremap <buffer> <silent> <CR> :<C-U>close<CR>
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
" make sure any key that sends an escape as a prefix (e.g. the arrow keys)
" don't cause the window to close.
nnoremap <buffer> <silent> <Esc>[ <Esc>[
endfunction
function! s:gogetdoc(json) abort
let l:cmd = [
\ 'gogetdoc',
\ '-tags', go#config#BuildTags(),
\ '-pos', expand("%:p:gs!\\!/!") . ':#' . go#util#OffsetCursor()]
if a:json
let l:cmd += ['-json']
endif
if &modified
let l:cmd += ['-modified']
return go#util#Exec(l:cmd, go#util#archive())
endif
return go#util#Exec(l:cmd)
endfunction
" returns the package and exported name. exported name might be empty.
" ie: fmt and Println
" ie: github.com/fatih/set and New
function! s:godocWord(args) abort
if !executable('godoc')
let msg = "godoc command not found."
let msg .= " install with: go get golang.org/x/tools/cmd/godoc"
call go#util#EchoWarning(msg)
return []
endif
if !len(a:args)
let oldiskeyword = &iskeyword
setlocal iskeyword+=.
let word = expand('<cword>')
let &iskeyword = oldiskeyword
let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
let words = split(word, '\.\ze[^./]\+$')
else
let words = a:args
endif
if !len(words)
return []
endif
let pkg = words[0]
if len(words) == 1
let exported_name = ""
else
let exported_name = words[1]
endif
let packages = go#tool#Imports()
if has_key(packages, pkg)
let pkg = packages[pkg]
endif
return [pkg, exported_name]
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,72 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#fillstruct#FillStruct() abort
let l:cmd = ['fillstruct',
\ '-file', bufname(''),
\ '-offset', go#util#OffsetCursor(),
\ '-line', line('.')]
" Needs: https://github.com/davidrjenni/reftools/pull/14
"\ '-tags', go#config#BuildTags()]
" Read from stdin if modified.
if &modified
call add(l:cmd, '-modified')
let [l:out, l:err] = go#util#Exec(l:cmd, go#util#archive())
else
let [l:out, l:err] = go#util#Exec(l:cmd)
endif
if l:err
call go#util#EchoError(l:out)
return
endif
try
let l:json = json_decode(l:out)
catch
call go#util#EchoError(l:out)
return
endtry
" Output is array:
"[
" {"start": 92, "end": 106, "code": "mail.Address{\n\tName: \"\",\n\tAddress: \"\",\n}"},
" {...second struct...}
" ]
let l:pos = getpos('.')
try
for l:struct in l:json
let l:code = split(l:struct['code'], "\n")
" Add any code before/after the struct.
exe l:struct['start'] . 'go'
let l:code[0] = getline('.')[:col('.')-1] . l:code[0]
exe l:struct['end'] . 'go'
let l:code[len(l:code)-1] .= getline('.')[col('.'):]
" Indent every line except the first one; makes it look nice.
let l:indent = repeat("\t", indent('.') / &tabstop)
for l:i in range(1, len(l:code)-1)
let l:code[l:i] = l:indent . l:code[l:i]
endfor
" Out with the old ...
exe 'normal! ' . l:struct['start'] . 'gov' . l:struct['end'] . 'gox'
" ... in with the new.
call setline('.', l:code[0])
call append('.', l:code[1:])
endfor
finally
call setpos('.', l:pos)
endtry
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,98 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_fillstruct() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ "var addr = mail.\x1fAddress{}"])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ "\x1f" . 'var addr = mail.Address{}'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "\x1f" . 'func x() { fmt.Println(mail.Address{}, mail.Address{}) }'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_cursor() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "func x() { fmt.Println(mail.Address{}, mail.Ad\x1fdress{}) }"])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,214 +0,0 @@
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
"
" fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible
" toorls, such as goimports).
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" we have those problems :
" http://stackoverflow.com/questions/12741977/prevent-vim-from-updating-its-undo-tree
" http://stackoverflow.com/questions/18532692/golang-formatter-and-vim-how-to-destroy-history-record?rq=1
"
" The below function is an improved version that aims to fix all problems.
" it doesn't undo changes and break undo history. If you are here reading
" this and have VimL experience, please look at the function for
" improvements, patches are welcome :)
function! go#fmt#Format(withGoimport) abort
if go#config#FmtExperimental()
" Using winsaveview to save/restore cursor state has the problem of
" closing folds on save:
" https://github.com/fatih/vim-go/issues/502
" One fix is to use mkview instead. Unfortunately, this sometimes causes
" other bad side effects:
" https://github.com/fatih/vim-go/issues/728
" and still closes all folds if foldlevel>0:
" https://github.com/fatih/vim-go/issues/732
let l:curw = {}
try
mkview!
catch
let l:curw = winsaveview()
endtry
" save our undo file to be restored after we are done. This is needed to
" prevent an additional undo jump due to BufWritePre auto command and also
" restore 'redo' history because it's getting being destroyed every
" BufWritePre
let tmpundofile = tempname()
exe 'wundo! ' . tmpundofile
else
" Save cursor position and many other things.
let l:curw = winsaveview()
endif
" Write current unsaved buffer to a temp file
let l:tmpname = tempname() . '.go'
call writefile(go#util#GetLines(), l:tmpname)
if go#util#IsWin()
let l:tmpname = tr(l:tmpname, '\', '/')
endif
let bin_name = go#config#FmtCommand()
if a:withGoimport == 1
let bin_name = "goimports"
endif
let current_col = col('.')
let [l:out, l:err] = go#fmt#run(bin_name, l:tmpname, expand('%'))
let diff_offset = len(readfile(l:tmpname)) - line('$')
if l:err == 0
call go#fmt#update_file(l:tmpname, expand('%'))
elseif !go#config#FmtFailSilently()
let errors = s:parse_errors(expand('%'), out)
call s:show_errors(errors)
endif
" We didn't use the temp file, so clean up
call delete(l:tmpname)
if go#config#FmtExperimental()
" restore our undo history
silent! exe 'rundo ' . tmpundofile
call delete(tmpundofile)
" Restore our cursor/windows positions, folds, etc.
if empty(l:curw)
silent! loadview
else
call winrestview(l:curw)
endif
else
" Restore our cursor/windows positions.
call winrestview(l:curw)
endif
" be smart and jump to the line the new statement was added/removed
call cursor(line('.') + diff_offset, current_col)
" Syntax highlighting breaks less often.
syntax sync fromstart
endfunction
" update_file updates the target file with the given formatted source
function! go#fmt#update_file(source, target)
" remove undo point caused via BufWritePre
try | silent undojoin | catch | endtry
let old_fileformat = &fileformat
if exists("*getfperm")
" save file permissions
let original_fperm = getfperm(a:target)
endif
call rename(a:source, a:target)
" restore file permissions
if exists("*setfperm") && original_fperm != ''
call setfperm(a:target , original_fperm)
endif
" reload buffer to reflect latest changes
silent edit!
let &fileformat = old_fileformat
let &syntax = &syntax
let l:listtype = go#list#Type("GoFmt")
" clean up previous list
if l:listtype == "quickfix"
let l:list_title = getqflist({'title': 1})
else
let l:list_title = getloclist(0, {'title': 1})
endif
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype)
endif
endfunction
" run runs the gofmt/goimport command for the given source file and returns
" the output of the executed command. Target is the real file to be formatted.
function! go#fmt#run(bin_name, source, target)
let l:cmd = s:fmt_cmd(a:bin_name, a:source, a:target)
if empty(l:cmd)
return
endif
return go#util#Exec(l:cmd)
endfunction
" fmt_cmd returns the command to run as a list.
function! s:fmt_cmd(bin_name, source, target)
let l:cmd = [a:bin_name, '-w']
" add the options for binary (if any). go_fmt_options was by default of type
" string, however to allow customization it's now a dictionary of binary
" name mapping to options.
let opts = go#config#FmtOptions()
if type(opts) == type({})
let opts = has_key(opts, a:bin_name) ? opts[a:bin_name] : ""
endif
call extend(cmd, split(opts, " "))
if a:bin_name is# 'goimports'
call extend(cmd, ["-srcdir", a:target])
endif
call add(cmd, a:source)
return cmd
endfunction
" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
let splitted = split(a:content, '\n')
" list of errors to be put into location list
let errors = []
for line in splitted
let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
if !empty(tokens)
call add(errors,{
\"filename": a:filename,
\"lnum": tokens[2],
\"col": tokens[3],
\"text": tokens[4],
\ })
endif
endfor
return errors
endfunction
" show_errors opens a location list and shows the given errors. If the given
" errors is empty, it closes the the location list
function! s:show_errors(errors) abort
let l:listtype = go#list#Type("GoFmt")
if !empty(a:errors)
call go#list#Populate(l:listtype, a:errors, 'Format')
endif
" this closes the window if there are no errors or it opens
" it if there is any
call go#list#Window(l:listtype, len(a:errors))
endfunction
function! go#fmt#ToggleFmtAutoSave() abort
if go#config#FmtAutosave()
call go#config#SetFmtAutosave(0)
call go#util#EchoProgress("auto fmt disabled")
return
end
call go#config#SetFmtAutosave(1)
call go#util#EchoProgress("auto fmt enabled")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,57 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_run_fmt() abort
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file)
let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n")
" run our code
call go#fmt#run("gofmt", actual_file, "test-fixtures/fmt/hello.go")
" this should now contain the formatted code
let actual = join(readfile(actual_file), "\n")
call assert_equal(expected, actual)
endfunc
func! Test_update_file() abort
let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n")
let source_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello_golden.go"), source_file)
let target_file = tempname()
call writefile([""], target_file)
" update_file now
call go#fmt#update_file(source_file, target_file)
" this should now contain the formatted code
let actual = join(readfile(target_file), "\n")
call assert_equal(expected, actual)
endfunc
func! Test_goimports() abort
let $GOPATH = 'test-fixtures/fmt/'
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/src/imports/goimports.go"), actual_file)
let expected = join(readfile("test-fixtures/fmt/src/imports/goimports_golden.go"), "\n")
" run our code
call go#fmt#run("goimports", actual_file, "test-fixtures/fmt/src/imports/goimports.go")
" this should now contain the formatted code
let actual = join(readfile(actual_file), "\n")
call assert_equal(expected, actual)
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,731 +0,0 @@
" guru.vim -- Vim integration for the Go guru.
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" guru_cmd returns a dict that contains the command to execute guru. args
" is dict with following options:
" mode : guru mode, such as 'implements'
" format : output format, either 'plain' or 'json'
" needs_scope : if 1, adds the current package to the scope
" selected : if 1, means it's a range of selection, otherwise it picks up the
" offset under the cursor
" example output:
" {'cmd' : ['guru', '-json', 'implements', 'demo/demo.go:#66']}
function! s:guru_cmd(args) range abort
let mode = a:args.mode
let format = a:args.format
let needs_scope = a:args.needs_scope
let selected = a:args.selected
let postype = get(a:args, 'postype', 'cursor')
let result = {}
"return with a warning if the binary doesn't exist
let bin_path = go#path#CheckBinPath("guru")
if empty(bin_path)
return {'err': "bin path not found"}
endif
" start constructing the command
let cmd = [bin_path, '-tags', go#config#BuildTags()]
if &modified
let result.stdin_content = go#util#archive()
call add(cmd, "-modified")
endif
" enable outputting in json format
if format == "json"
call add(cmd, "-json")
endif
let scopes = go#config#GuruScope()
if empty(scopes)
" some modes require scope to be defined (such as callers). For these we
" choose a sensible setting, which is using the current file's package
if needs_scope
let pkg = go#package#ImportPath()
if pkg == -1
return {'err': "current directory is not inside of a valid GOPATH"}
endif
let scopes = [pkg]
endif
endif
" Add the scope.
if !empty(scopes)
" guru expect a comma-separated list of patterns.
let l:scope = join(scopes, ",")
let result.scope = l:scope
call extend(cmd, ["-scope", l:scope])
endif
if postype == 'balloon'
let pos = printf("#%s", go#util#Offset(v:beval_lnum, v:beval_col))
else
let pos = printf("#%s", go#util#OffsetCursor())
if selected != -1
" means we have a range, get it
let pos1 = go#util#Offset(line("'<"), col("'<"))
let pos2 = go#util#Offset(line("'>"), col("'>"))
let pos = printf("#%s,#%s", pos1, pos2)
endif
endif
let l:filename = fnamemodify(expand("%"), ':p:gs?\\?/?') . ':' . pos
call extend(cmd, [mode, l:filename])
let result.cmd = cmd
return result
endfunction
" sync_guru runs guru in sync mode with the given arguments
function! s:sync_guru(args) abort
let result = s:guru_cmd(a:args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return -1
endif
if !has_key(a:args, 'disable_progress')
if a:args.needs_scope
call go#util#EchoProgress("analysing with scope ". result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
elseif a:args.mode !=# 'what'
" the query might take time, let us give some feedback
call go#util#EchoProgress("analysing ...")
endif
endif
" run, forrest run!!!
if has_key(l:result, 'stdin_content')
let [l:out, l:err] = go#util#Exec(l:result.cmd, l:result.stdin_content)
else
let [l:out, l:err] = go#util#Exec(l:result.cmd)
endif
if has_key(a:args, 'custom_parse')
call a:args.custom_parse(l:err, l:out, a:args.mode)
else
call s:parse_guru_output(l:err, l:out, a:args.mode)
endif
return l:out
endfunc
" async_guru runs guru in async mode with the given arguments
function! s:async_guru(args) abort
let result = s:guru_cmd(a:args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return
endif
let state = {
\ 'mode': a:args.mode,
\ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output"))
\ }
" 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)
let opts = {
\ 'statustype': get(a:args, 'statustype', a:args.mode),
\ 'for': '_',
\ 'errorformat': "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m",
\ 'complete': state.complete,
\ }
if has_key(a:args, 'disable_progress')
let opts.statustype = ''
endif
let opts = go#job#Options(l:opts)
if has_key(result, 'stdin_content')
let l:tmpname = tempname()
call writefile(split(result.stdin_content, "\n"), l:tmpname, "b")
let l:opts.in_io = "file"
let l:opts.in_name = l:tmpname
endif
call go#job#Start(result.cmd, opts)
if a:args.needs_scope && go#config#EchoCommandInfo() && !has_key(a:args, 'disable_progress')
call go#util#EchoProgress("analysing with scope " . result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
endif
endfunc
function! s:complete(job, exit_status, messages) dict abort
let output = join(a:messages, "\n")
call self.parse(a:exit_status, output, self.mode)
endfunction
" run_guru runs the given guru argument
function! s:run_guru(args) abort
if go#util#has_job()
let res = s:async_guru(a:args)
else
let res = s:sync_guru(a:args)
endif
return res
endfunction
" Show 'implements' relation for selected package
function! go#guru#Implements(selected) abort
let args = {
\ 'mode': 'implements',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Shows the set of possible objects to which a pointer may point.
function! go#guru#PointsTo(selected) abort
let l:args = {
\ 'mode': 'pointsto',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(l:args)
endfunction
" Report the possible constants, global variables, and concrete types that may
" appear in a value of type error
function! go#guru#Whicherrs(selected) abort
let args = {
\ 'mode': 'whicherrs',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
" TODO(arslan): handle empty case for both sync/async
" if empty(out.out)
" call go#util#EchoSuccess("no error variables found. Try to change the scope with :GoGuruScope")
" return
" endif
call s:run_guru(args)
endfunction
" Describe selected syntax: definition, methods, etc
function! go#guru#Describe(selected) abort
let args = {
\ 'mode': 'describe',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
function! go#guru#DescribeInfo(showstatus) abort
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("GoDescribeInfo requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
let args = {
\ 'mode': 'describe',
\ 'format': 'json',
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:info'),
\ 'disable_progress': a:showstatus == 0,
\ }
call s:run_guru(args)
endfunction
function! s:info(exit_val, output, mode)
if a:exit_val != 0
return
endif
if a:output[0] !=# '{'
return
endif
if empty(a:output) || type(a:output) != type("")
return
endif
let result = json_decode(a:output)
if type(result) != type({})
call go#util#EchoError(printf("malformed output from guru: %s", a:output))
return
endif
if !has_key(result, 'detail')
" if there is no detail check if there is a description and print it
if has_key(result, "desc")
call go#util#EchoInfo(result["desc"])
return
endif
call go#util#EchoError("detail key is missing. Please open a bug report on vim-go repo.")
return
endif
let detail = result['detail']
let info = ""
" guru gives different information based on the detail mode. Let try to
" extract the most useful information
if detail == "value"
if !has_key(result, 'value')
call go#util#EchoError("value key is missing. Please open a bug report on vim-go repo.")
return
endif
let val = result["value"]
if !has_key(val, 'type')
call go#util#EchoError("type key is missing (value.type). Please open a bug report on vim-go repo.")
return
endif
let info = val["type"]
elseif detail == "type"
if !has_key(result, 'type')
call go#util#EchoError("type key is missing. Please open a bug report on vim-go repo.")
return
endif
let type = result["type"]
if !has_key(type, 'type')
call go#util#EchoError("type key is missing (type.type). Please open a bug report on vim-go repo.")
return
endif
let info = type["type"]
elseif detail == "package"
if !has_key(result, 'package')
call go#util#EchoError("package key is missing. Please open a bug report on vim-go repo.")
return
endif
let package = result["package"]
if !has_key(package, 'path')
call go#util#EchoError("path key is missing (package.path). Please open a bug report on vim-go repo.")
return
endif
let info = printf("package %s", package["path"])
elseif detail == "unknown"
let info = result["desc"]
else
call go#util#EchoError(printf("unknown detail mode found '%s'. Please open a bug report on vim-go repo", detail))
return
endif
call go#util#ShowInfo(info)
endfunction
" Show possible targets of selected function call
function! go#guru#Callees(selected) abort
let args = {
\ 'mode': 'callees',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show possible callers of selected function
function! go#guru#Callers(selected) abort
let args = {
\ 'mode': 'callers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show path from callgraph root to selected function
function! go#guru#Callstack(selected) abort
let args = {
\ 'mode': 'callstack',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show free variables of selection
function! go#guru#Freevars(selected) abort
" Freevars requires a selection
if a:selected == -1
call go#util#EchoError("GoFreevars requires a selection (range) of code")
return
endif
let args = {
\ 'mode': 'freevars',
\ 'format': 'plain',
\ 'selected': 1,
\ 'needs_scope': 0,
\ }
call s:run_guru(args)
endfunction
" Show send/receive corresponding to selected channel op
function! go#guru#ChannelPeers(selected) abort
let args = {
\ 'mode': 'peers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show all refs to entity denoted by selected identifier
function! go#guru#Referrers(selected) abort
let l:mode = go#config#ReferrersMode()
if l:mode == 'guru'
let args = {
\ 'mode': 'referrers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 0,
\ }
call s:run_guru(args)
return
elseif l:mode == 'gopls'
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:fname = expand('%:p')
call go#lsp#Referrers(l:fname, l:line, l:col, funcref('s:parse_guru_output'))
return
else
call go#util#EchoWarning('unknown value for g:go_referrers_mode')
endif
endfunction
function! go#guru#SameIds(showstatus) abort
" check if the version of Vim being tested supports matchaddpos()
if !exists("*matchaddpos")
call go#util#EchoError("GoSameIds requires 'matchaddpos'. Update your Vim/Neovim version.")
return
endif
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("GoSameIds requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
call go#lsp#SameIDs(0, expand('%:p'), l:line, l:col, funcref('s:same_ids_highlight'))
endfunction
function! s:same_ids_highlight(exit_val, output, mode) abort
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
if a:output[0] !=# '{'
if !go#config#AutoSameids()
call go#util#EchoError(a:output)
endif
return
endif
let result = json_decode(a:output)
if type(result) != type({}) && !go#config#AutoSameids()
call go#util#EchoError("malformed output from guru")
return
endif
if !has_key(result, 'sameids')
if !go#config#AutoSameids()
call go#util#EchoError("no same_ids founds for the given identifier")
endif
return
endif
let poslen = 0
for enclosing in result['enclosing']
if enclosing['desc'] == 'identifier'
let poslen = enclosing['end'] - enclosing['start']
break
endif
endfor
" return when there's no identifier to highlight.
if poslen == 0
return
endif
let same_ids = result['sameids']
" highlight the lines
let l:matches = []
for item in same_ids
let pos = split(item, ':')
let l:matches = add(l:matches, [str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)])
endfor
call matchaddpos('goSameId', l:matches)
if go#config#AutoSameids()
" re-apply SameIds at the current cursor position at the time the buffer
" is redisplayed: e.g. :edit, :GoRename, etc.
augroup vim-go-sameids
autocmd! * <buffer>
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
augroup end
endif
endfunction
" ClearSameIds returns 0 when it removes goSameId groups and non-zero if no
" goSameId groups are found.
function! go#guru#ClearSameIds() abort
let l:cleared = go#util#ClearGroupFromMatches('goSameId')
if !l:cleared
return 1
endif
" remove the autocmds we defined
augroup vim-go-sameids
autocmd! * <buffer>
augroup end
return 0
endfunction
function! go#guru#ToggleSameIds() abort
if go#guru#ClearSameIds() != 0
call go#guru#SameIds(1)
endif
endfunction
function! go#guru#AutoToggleSameIds() abort
if go#config#AutoSameids()
call go#util#EchoProgress("sameids auto highlighting disabled")
call go#guru#ClearSameIds()
call go#config#SetAutoSameids(0)
else
call go#util#EchoSuccess("sameids auto highlighting enabled")
call go#config#SetAutoSameids(1)
endif
call go#auto#update_autocmd()
endfunction
""""""""""""""""""""""""""""""""""""""""
"" HELPER FUNCTIONS
""""""""""""""""""""""""""""""""""""""""
" This uses Vim's errorformat to parse the output from Guru's 'plain output
" and put it into location list. I believe using errorformat is much more
" easier to use. If we need more power we can always switch back to parse it
" via regex. Match two possible styles of errorformats:
"
" 'file:line.col-line2.col2: message'
" 'file:line:col: message'
"
" We discard line2 and col2 for the first errorformat, because it's not
" useful and location only has the ability to show one line and column
" number
function! s:parse_guru_output(exit_val, output, title) abort
if a:exit_val
call go#util#EchoError(a:output)
return
endif
let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m"
let l:listtype = go#list#Type("_guru")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
endfun
function! go#guru#Scope(...) abort
if a:0
let scope = a:000
if a:0 == 1 && a:1 == '""'
let scope = []
endif
call go#config#SetGuruScope(scope)
if empty(scope)
call go#util#EchoSuccess("guru scope is cleared")
else
call go#util#EchoSuccess("guru scope changed to: ". join(a:000, ","))
endif
return
endif
let scope = go#config#GuruScope()
if empty(scope)
call go#util#EchoError("guru scope is not set")
else
call go#util#EchoSuccess("current guru scope: ". join(scope, ","))
endif
endfunction
function! go#guru#DescribeBalloon() abort
" don't even try if async isn't available.
if !go#util#has_job()
return
endif
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("GoDescribeBalloon requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
" change the active window to the window where the cursor is.
let l:winid = win_getid(winnr())
call win_gotoid(v:beval_winid)
let l:args = {
\ 'mode': 'describe',
\ 'format': 'json',
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:describe_balloon'),
\ 'disable_progress': 1,
\ 'postype': 'balloon',
\ }
call s:async_guru(args)
" make the starting window active again
call win_gotoid(l:winid)
return ''
endfunction
function! s:describe_balloon(exit_val, output, mode)
if a:exit_val != 0
return
endif
if a:output[0] !=# '{'
return
endif
if empty(a:output) || type(a:output) != type("")
return
endif
let l:result = json_decode(a:output)
if type(l:result) != type({})
call go#util#EchoError(printf('malformed output from guru: %s', a:output))
return
endif
let l:info = []
if has_key(l:result, 'desc')
if l:result['desc'] != 'identifier'
let l:info = add(l:info, l:result['desc'])
endif
endif
if has_key(l:result, 'detail')
let l:detail = l:result['detail']
" guru gives different information based on the detail mode. Let try to
" extract the most useful information
if l:detail == 'value'
if !has_key(l:result, 'value')
call go#util#EchoError('value key is missing. Please open a bug report on vim-go repo.')
return
endif
let l:val = l:result['value']
if !has_key(l:val, 'type')
call go#util#EchoError('type key is missing (value.type). Please open a bug report on vim-go repo.')
return
endif
let l:info = add(l:info, printf('type: %s', l:val['type']))
if has_key(l:val, 'value')
let l:info = add(l:info, printf('value: %s', l:val['value']))
endif
elseif l:detail == 'type'
if !has_key(l:result, 'type')
call go#util#EchoError('type key is missing. Please open a bug report on vim-go repo.')
return
endif
let l:type = l:result['type']
if !has_key(l:type, 'type')
call go#util#EchoError('type key is missing (type.type). Please open a bug report on vim-go repo.')
return
endif
let l:info = add(l:info, printf('type: %s', l:type['type']))
if has_key(l:type, 'methods')
let l:info = add(l:info, 'methods:')
for l:m in l:type.methods
let l:info = add(l:info, printf("\t%s", l:m['name']))
endfor
endif
elseif l:detail == 'package'
if !has_key(l:result, 'package')
call go#util#EchoError('package key is missing. Please open a bug report on vim-go repo.')
return
endif
let l:package = result['package']
if !has_key(l:package, 'path')
call go#util#EchoError('path key is missing (package.path). Please open a bug report on vim-go repo.')
return
endif
let l:info = add(l:info, printf('package: %s', l:package["path"]))
elseif l:detail == 'unknown'
" the description is already included in l:info, and there's no other
" information on unknowns.
else
call go#util#EchoError(printf('unknown detail mode (%s) found. Please open a bug report on vim-go repo', l:detail))
return
endif
endif
if has('balloon_eval')
call balloon_show(join(l:info, "\n"))
return
endif
call balloon_show(l:info)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,23 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function Test_GuruScope_Set() abort
silent call go#guru#Scope("example.com/foo/bar")
let actual = go#config#GuruScope()
call assert_equal(["example.com/foo/bar"], actual)
silent call go#guru#Scope('""')
silent let actual = go#config#GuruScope()
call assert_equal([], actual, "setting scope to empty string should clear")
if exists('g:go_guru_scope')
unlet g:go_guru_scope
endif
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,145 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! Test_gomodVersion_highlight() abort
try
syntax on
let l:dir = gotest#write_file('gomodtest/go.mod', [
\ 'module github.com/fatih/vim-go',
\ '',
\ '\x1frequire (',
\ '\tversion/simple v1.0.0',
\ '\tversion/simple-pre-release v1.0.0-rc',
\ '\tversion/simple-pre-release v1.0.0+meta',
\ '\tversion/simple-pre-release v1.0.0-rc+meta',
\ '\tversion/pseudo/premajor v1.0.0-20060102150405-0123456789abcdef',
\ '\tversion/pseudo/prerelease v1.0.0-prerelease.0.20060102150405-0123456789abcdef',
\ '\tversion/pseudo/prepatch v1.0.1-0.20060102150405-0123456789abcdef',
\ '\tversion/simple/incompatible v2.0.0+incompatible',
\ '\tversion/pseudo/premajor/incompatible v2.0.0-20060102150405-0123456789abcdef+incompatible',
\ '\tversion/pseudo/prerelease/incompatible v2.0.0-prerelease.0.20060102150405-0123456789abcdef+incompatible',
\ '\tversion/pseudo/prepatch/incompatible v2.0.1-0.20060102150405-0123456789abcdef+incompatible',
\ ')'])
let l:lineno = 4
let l:lineclose = line('$')
while l:lineno < l:lineclose
let l:line = getline(l:lineno)
let l:col = col([l:lineno, '$']) - 1
let l:idx = len(l:line) - 1
let l:from = stridx(l:line, ' ') + 1
while l:idx >= l:from
call cursor(l:lineno, l:col)
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
let l:errlen = len(v:errors)
call assert_equal('gomodVersion', l:synname, 'version on line ' . l:lineno)
" continue at the next line if there was an error at this column;
" there's no need to test each column once an error is detected.
if l:errlen < len(v:errors)
break
endif
let l:col -= 1
let l:idx -= 1
endwhile
let l:lineno += 1
endwhile
finally
call delete(l:dir, 'rf')
endtry
endfunc
function! Test_gomodVersion_incompatible_highlight() abort
try
syntax on
let l:dir = gotest#write_file('gomodtest/go.mod', [
\ 'module github.com/fatih/vim-go',
\ '',
\ '\x1frequire (',
\ '\tversion/invalid/premajor/incompatible v1.0.0-20060102150405-0123456789abcdef+incompatible',
\ '\tversion/invalid/prerelease/incompatible v1.0.0-prerelease.0.20060102150405-0123456789abcdef+incompatible',
\ '\tversion/invalid/prepatch/incompatible v1.0.1-0.20060102150405-0123456789abcdef+incompatible',
\ ')'])
let l:lineno = 4
let l:lineclose = line('$')
while l:lineno < l:lineclose
let l:line = getline(l:lineno)
let l:col = col([l:lineno, '$']) - 1
let l:idx = len(l:line) - 1
let l:from = stridx(l:line, '+')
while l:idx >= l:from
call cursor(l:lineno, l:col)
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
let l:errlen = len(v:errors)
call assert_notequal('gomodVersion', l:synname, 'version on line ' . l:lineno)
" continue at the next line if there was an error at this column;
" there's no need to test each column once an error is detected.
if l:errlen < len(v:errors)
break
endif
let l:col -= 1
let l:idx -= 1
endwhile
let l:lineno += 1
endwhile
finally
call delete(l:dir, 'rf')
endtry
endfunc
function! Test_numeric_literal_highlight() abort
syntax on
let tests = {
\ 'lone zero': {'group': 'goDecimalInt', 'value': '0'},
\ 'integer': {'group': 'goDecimalInt', 'value': '1234567890'},
\ 'hexadecimal': {'group': 'goHexadecimalInt', 'value': '0x0123456789abdef'},
\ 'hexadecimalErrorLeading': {'group': 'goHexadecimalError', 'value': '0xg0123456789abdef'},
\ 'hexadecimalErrorTrailing': {'group': 'goHexadecimalError', 'value': '0x0123456789abdefg'},
\ 'heXadecimal': {'group': 'goHexadecimalInt', 'value': '0X0123456789abdef'},
\ 'heXadecimalErrorLeading': {'group': 'goHexadecimalError', 'value': '0Xg0123456789abdef'},
\ 'heXadecimalErrorTrailing': {'group': 'goHexadecimalError', 'value': '0X0123456789abdefg'},
\ 'octal': {'group': 'goOctalInt', 'value': '01234567'},
\ 'octalErrorLeading': {'group': 'goOctalError', 'value': '081234567'},
\ 'octalErrorTrailing': {'group': 'goOctalError', 'value': '012345678'},
\ 'binaryInt': {'group': 'goBinaryInt', 'value': '0b0101'},
\ 'binaryErrorLeading': {'group': 'goBinaryError', 'value': '0b20101'},
\ 'binaryErrorTrailing': {'group': 'goBinaryError', 'value': '0b01012'},
\ 'BinaryInt': {'group': 'goBinaryInt', 'value': '0B0101'},
\ 'BinaryErrorLeading': {'group': 'goBinaryError', 'value': '0B20101'},
\ 'BinaryErrorTrailing': {'group': 'goBinaryError', 'value': '0B01012'},
\ }
for kv in items(tests)
let l:dir = gotest#write_file(printf('numeric/%s.go', kv[0]), [
\ 'package numeric',
\ '',
\ printf("var v = %s\x1f", kv[1].value),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
call assert_equal(kv[1].group, l:actual, kv[0])
finally
" call delete(l:dir, 'rf')
endtry
endfor
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,26 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#iferr#Generate()
let [l:out, l:err] = go#util#Exec(['iferr',
\ '-pos=' . go#util#OffsetCursor()], go#util#GetLines())
if len(l:out) == 1
return
endif
if getline('.') =~ '^\s*$'
silent delete _
silent normal! k
endif
let l:pos = getcurpos()
call append(l:pos[1], split(l:out, "\n"))
silent normal! j=2j
call setpos('.', l:pos)
silent normal! 4j
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,175 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#impl#Impl(...) abort
let recv = ""
let iface = ""
let interactive = 0
let pos = getpos('.')
if a:0 is 0
" Interactive mode if user didn't pass any arguments.
let recv = s:getReceiver()
let iface = input("vim-go: generating method stubs for interface: ")
redraw!
if empty(iface)
call go#util#EchoError('usage: interface type is not provided')
return
endif
elseif a:0 is 1
" we assume the user only passed the interface type,
" i.e: ':GoImpl io.Writer'
let recv = s:getReceiver()
let iface = a:1
elseif a:0 > 2
" user passed receiver and interface type both,
" i.e: 'GoImpl f *Foo io.Writer'
let recv = join(a:000[:-2], ' ')
let iface = a:000[-1]
else
call go#util#EchoError('usage: GoImpl {receiver} {interface}')
return
endif
" Make sure we put the generated code *after* the struct.
if getline(".") =~ "struct "
normal! $%
endif
try
let dirname = fnameescape(expand('%:p:h'))
let [result, err] = go#util#Exec(['impl', '-dir', dirname, recv, iface])
let result = substitute(result, "\n*$", "", "")
if err
call go#util#EchoError(result)
return
endif
if result is# ''
return
end
put =''
silent put =result
finally
call setpos('.', pos)
endtry
endfunction
function! s:getReceiver()
let receiveType = expand("<cword>")
if receiveType == "type"
normal! w
let receiveType = expand("<cword>")
elseif receiveType == "struct"
normal! ge
let receiveType = expand("<cword>")
endif
return printf("%s *%s", tolower(receiveType)[0], receiveType)
endfunction
if exists('*uniq')
function! s:uniq(list)
return uniq(a:list)
endfunction
else
" Note: Believe that the list is sorted
function! s:uniq(list)
let i = len(a:list) - 1
while 0 < i
if a:list[i-1] ==# a:list[i]
call remove(a:list, i)
let i -= 2
else
let i -= 1
endif
endwhile
return a:list
endfunction
endif
function! s:root_dirs() abort
let dirs = []
let root = go#util#env("goroot")
if root !=# '' && isdirectory(root)
call add(dirs, root)
endif
let paths = map(split(go#util#env("gopath"), go#util#PathListSep()), "substitute(v:val, '\\\\', '/', 'g')")
if !empty(filter(paths, 'isdirectory(v:val)'))
call extend(dirs, paths)
endif
return dirs
endfunction
function! s:go_packages(dirs, arglead) abort
let pkgs = []
for dir in a:dirs
" this may expand to multiple lines
let scr_root = expand(dir . '/src/')
for pkg in split(globpath(scr_root, a:arglead.'*'), "\n")
if isdirectory(pkg)
let pkg .= '/'
elseif pkg !~ '\.a$'
continue
endif
" without this the result can have duplicates in form of
" 'encoding/json' and '/encoding/json/'
let pkg = go#util#StripPathSep(pkg)
" remove the scr root and keep the package in tact
let pkg = substitute(pkg, scr_root, "", "")
call add(pkgs, pkg)
endfor
endfor
return pkgs
endfunction
function! s:interface_list(pkg) abort
let [contents, err] = go#util#Exec(['go', 'doc', a:pkg])
if err
return []
endif
let contents = split(contents, "\n")
call filter(contents, 'v:val =~# ''^type\s\+\h\w*\s\+interface''')
return map(contents, 'a:pkg . "." . matchstr(v:val, ''^type\s\+\zs\h\w*\ze\s\+interface'')')
endfunction
" Complete package and interface for {interface}
function! go#impl#Complete(arglead, cmdline, cursorpos) abort
let words = split(a:cmdline, '\s\+', 1)
if words[-1] ==# ''
" if no words are given, just start completing the first package we found
return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
elseif words[-1] =~# '^\(\h\w.*\.\%(\h\w*\)\=$\)\@!\S*$'
" start matching go packages. It's negate match of the below match
return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
elseif words[-1] =~# '^\h\w.*\.\%(\h\w*\)\=$'
" match the following, anything that could indicate an interface candidate
"
" io.
" io.Wr
" github.com/fatih/color.
" github.com/fatih/color.U
" github.com/fatih/color.Un
let splitted = split(words[-1], '\.', 1)
let pkg = join(splitted[:-2], '.')
let interface = splitted[-1]
return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]')))
else
return []
endif
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,47 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_impl() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ ''])
call go#impl#Impl('r', 'reader', 'io.Reader')
call gotest#assert_buffer(1, [
\ 'func (r reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_impl_get() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ 'type reader struct {}'])
call go#impl#Impl('io.Reader')
call gotest#assert_buffer(0, [
\ 'package a',
\ '',
\ 'type reader struct {}',
\ '',
\ 'func (r *reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,232 +0,0 @@
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
"
" Check out the docs for more information at /doc/vim-go.txt
"
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#import#SwitchImport(enabled, localname, path, bang) abort
let view = winsaveview()
let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '')
" Quotes are not necessary, so remove them if provided.
if path[0] == '"'
let path = strpart(path, 1)
endif
if path[len(path)-1] == '"'
let path = strpart(path, 0, len(path) - 1)
endif
" if given a trailing slash, eg. `github.com/user/pkg/`, remove it
if path[len(path)-1] == '/'
let path = strpart(path, 0, len(path) - 1)
endif
if path == ''
call s:Error('Import path not provided')
return
endif
if a:bang == "!"
let [l:out, l:err] = go#util#Exec(['go', 'get', '-u', '-v', path])
if err != 0
call s:Error("Can't find import: " . path . ":" . out)
endif
endif
let exists = go#tool#Exists(path)
if exists == -1
call s:Error("Can't find import: " . path)
return
endif
" Extract any site prefix (e.g. github.com/).
" If other imports with the same prefix are grouped separately,
" we will add this new import with them.
" Only up to and including the first slash is used.
let siteprefix = matchstr(path, "^[^/]*/")
let qpath = '"' . path . '"'
if a:localname != ''
let qlocalpath = a:localname . ' ' . qpath
else
let qlocalpath = qpath
endif
let indentstr = 0
let packageline = -1 " Position of package name statement
let appendline = -1 " Position to introduce new import
let deleteline = -1 " Position of line with existing import
let linesdelta = 0 " Lines added/removed
" Find proper place to add/remove import.
let line = 0
while line <= line('$')
let linestr = getline(line)
if linestr =~# '^package\s'
let packageline = line
let appendline = line
elseif linestr =~# '^import\s\+(\+)'
let appendline = line
let appendstr = qlocalpath
elseif linestr =~# '^import\s\+('
let appendstr = qlocalpath
let indentstr = 1
let appendline = line
let firstblank = -1
let lastprefix = ""
while line <= line("$")
let line = line + 1
let linestr = getline(line)
let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)')
if empty(m)
if siteprefix == "" && a:enabled
" must be in the first group
break
endif
" record this position, but keep looking
if firstblank < 0
let firstblank = line
endif
continue
endif
if m[1] == ')'
" if there's no match, add it to the first group
if appendline < 0 && firstblank >= 0
let appendline = firstblank
endif
break
endif
let lastprefix = matchstr(m[4], "^[^/]*/")
if a:localname != '' && m[3] != ''
let qlocalpath = printf('%-' . (len(m[3])-1) . 's %s', a:localname, qpath)
endif
let appendstr = m[2] . qlocalpath
let indentstr = 0
if m[4] == path
let appendline = -1
let deleteline = line
break
elseif m[4] < path
" don't set candidate position if we have a site prefix,
" we've passed a blank line, and this doesn't share the same
" site prefix.
if siteprefix == "" || firstblank < 0 || match(m[4], "^" . siteprefix) >= 0
let appendline = line
endif
elseif siteprefix != "" && match(m[4], "^" . siteprefix) >= 0
" first entry of site group
let appendline = line - 1
break
endif
endwhile
break
elseif linestr =~# '^import '
if appendline == packageline
let appendstr = 'import ' . qlocalpath
let appendline = line - 1
endif
let m = matchlist(linestr, '^import\(\s\+\)\(\S*\s*\)"\(.\+\)"')
if !empty(m)
if m[3] == path
let appendline = -1
let deleteline = line
break
endif
if m[3] < path
let appendline = line
endif
if a:localname != '' && m[2] != ''
let qlocalpath = printf("%s %" . len(m[2])-1 . "s", a:localname, qpath)
endif
let appendstr = 'import' . m[1] . qlocalpath
endif
elseif linestr =~# '^\(var\|const\|type\|func\)\>'
break
endif
let line = line + 1
endwhile
" Append or remove the package import, as requested.
if a:enabled
if deleteline != -1
call s:Error(qpath . ' already being imported')
elseif appendline == -1
call s:Error('No package line found')
else
if appendline == packageline
call append(appendline + 0, '')
call append(appendline + 1, 'import (')
call append(appendline + 2, ')')
let appendline += 2
let linesdelta += 3
let appendstr = qlocalpath
let indentstr = 1
call append(appendline, appendstr)
elseif getline(appendline) =~# '^import\s\+(\+)'
call setline(appendline, 'import (')
call append(appendline + 0, appendstr)
call append(appendline + 1, ')')
let linesdelta -= 1
let indentstr = 1
else
call append(appendline, appendstr)
endif
execute appendline + 1
if indentstr
execute 'normal! >>'
endif
let linesdelta += 1
endif
else
if deleteline == -1
call s:Error(qpath . ' not being imported')
else
execute deleteline . 'd'
let linesdelta -= 1
if getline(deleteline-1) =~# '^import\s\+(' && getline(deleteline) =~# '^)'
" Delete empty import block
let deleteline -= 1
execute deleteline . "d"
execute deleteline . "d"
let linesdelta -= 2
endif
if getline(deleteline) == '' && getline(deleteline - 1) == ''
" Delete spacing for removed line too.
execute deleteline . "d"
let linesdelta -= 1
endif
endif
endif
" Adjust view for any changes.
let view.lnum += linesdelta
let view.topline += linesdelta
if view.topline < 0
let view.topline = 0
endif
" Put buffer back where it was.
call winrestview(view)
endfunction
function! s:Error(s) abort
echohl Error | echo a:s | echohl None
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,72 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_indent_raw_string() abort
" The goRawString discovery requires that syntax be enabled.
syntax on
try
let l:dir= gotest#write_file('indent/indent.go', [
\ 'package main',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ "\t\x1fconst msg = `",
\ '`',
\ '\tfmt.Println(msg)',
\ '}'])
silent execute "normal o" . "not indented\<Esc>"
let l:indent = indent(line('.'))
call assert_equal(0, l:indent)
finally
call delete(l:dir, 'rf')
endtry
try
let l:dir= gotest#write_file('indent/indent.go', [
\ 'package main',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ "\t\x1fmsg := `",
\ '`',
\ '\tfmt.Println(msg)',
\ '}'])
silent execute "normal o" . "not indented\<Esc>"
let l:indent = indent(line('.'))
call assert_equal(0, l:indent)
finally
call delete(l:dir, 'rf')
endtry
try
let l:dir= gotest#write_file('indent/indent.go', [
\ 'package main',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ "\tconst msg = `",
\ "\t\x1findented",
\ '`',
\ '\tfmt.Println(msg)',
\ '}'])
silent execute "normal o" . "indented\<Esc>"
let l:indent = indent(line('.'))
call assert_equal(shiftwidth(), l:indent)
finally
call delete(l:dir, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,52 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
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#uri#Encode(s:issuebody())
let url = "https://github.com/fatih/vim-go/issues/new?body=" . l:body
call go#util#OpenBrowser(l:url)
endfunction
function! s:issuebody() abort
let lines = readfile(s:templatepath)
let rtrimpat = '[[:space:]]\+$'
let body = []
for l in lines
let body = add(body, l)
if l =~ '^<!-- :version'
redir => out
silent version
redir END
let body = extend(body, split(out, "\n")[0:2])
elseif l =~ '^<!-- go version -->'
let [out, err] = go#util#Exec(['go', 'version'])
let body = add(body, substitute(l:out, rtrimpat, '', ''))
elseif l =~ '^<!-- go env -->'
let [out, err] = go#util#Exec(['go', 'env'])
let body = add(body, substitute(l:out, rtrimpat, '', ''))
endif
endfor
let body = add(body, "#### vim-go configuration:\n<details><summary>vim-go configuration</summary><br><pre>")
for k in keys(g:)
if k =~ '^go_'
let body = add(body, 'g:' . k . ' = ' . string(get(g:, k)))
endif
endfor
let body = add(body, '</pre></details>')
return join(body, "\n")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,528 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Spawn starts an asynchronous job. See the description of go#job#Options to
" understand the args parameter.
"
" Spawn returns a job.
function! go#job#Spawn(cmd, args)
let l:options = go#job#Options(a:args)
return go#job#Start(a:cmd, l:options)
endfunction
" Options returns callbacks to be used with job_start. It is abstracted to be
" used with various go commands, such as build, test, install, etc.. This
" allows us to avoid writing the same callback over and over for some
" commands. It's fully customizable so each command can change it to its own
" logic.
"
" args is a dictionary with the these keys:
" 'bang':
" Set to 0 to jump to the first error in the error list.
" Defaults to 0.
" 'statustype':
" The status type to use when updating the status.
" See statusline.vim.
" 'for':
" The g:go_list_type_command key to use to get the error list type to use.
" Errors will not be handled when the value is '_'.
" Defaults to '_job'
" 'errorformat':
" The errorformat string to use when parsing errors. Defaults to
" &errorformat.
" See :help 'errorformat'.
" 'complete':
" A function to call after the job exits and the channel is closed. The
" function will be passed three arguments: the job, its exit code, and the
" list of messages received from the channel. The default is a no-op. A
" custom value can modify the messages before they are processed by the
" returned exit_cb and close_cb callbacks. When the function is called,
" the current window will be the window that was hosting the buffer when
" the job was started. After it returns, the current window will be
" restored to what it was before the function was called.
" The return value is a dictionary with these keys:
" 'callback':
" A function suitable to be passed as a job callback handler. See
" job-callback.
" 'exit_cb':
" A function suitable to be passed as a job exit_cb handler. See
" job-exit_cb.
" 'close_cb':
" A function suitable to be passed as a job close_cb handler. See
" job-close_cb.
" 'cwd':
" The path to the directory which contains the current buffer. The
" callbacks are configured to expect this directory is the working
" directory for the job; it should not be modified by callers.
function! go#job#Options(args)
let cbs = {}
let state = {
\ 'winid': win_getid(winnr()),
\ 'dir': getcwd(),
\ 'jobdir': expand("%:p:h"),
\ 'messages': [],
\ 'bang': 0,
\ 'for': "_job",
\ 'exited': 0,
\ 'exit_status': 0,
\ 'closed': 0,
\ 'errorformat': &errorformat,
\ 'statustype' : ''
\ }
let cbs.cwd = state.jobdir
if has_key(a:args, 'bang')
let state.bang = a:args.bang
endif
if has_key(a:args, 'for')
let state.for = a:args.for
endif
if has_key(a:args, 'statustype')
let state.statustype = a:args.statustype
endif
if has_key(a:args, 'errorformat')
let state.errorformat = a:args.errorformat
endif
function state.complete(job, exit_status, data)
if has_key(self, 'custom_complete')
let l:winid = win_getid(winnr())
" Always set the active window to the window that was active when the job
" was started. 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 starting the job.
call win_gotoid(self.winid)
call self.custom_complete(a:job, a:exit_status, a:data)
call win_gotoid(l:winid)
endif
call self.show_errors(a:job, a:exit_status, a:data)
endfunction
function state.show_status(job, exit_status) dict
if self.statustype == ''
return
endif
if go#config#EchoCommandInfo()
let prefix = '[' . self.statustype . '] '
if a:exit_status == 0
call go#util#EchoSuccess(prefix . "SUCCESS")
else
call go#util#EchoError(prefix . "FAIL")
endif
endif
let status = {
\ 'desc': 'last status',
\ 'type': self.statustype,
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
if has_key(self, 'started_at')
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
endif
call go#statusline#Update(self.jobdir, status)
endfunction
if has_key(a:args, 'complete')
let state.custom_complete = a:args.complete
endif
" explicitly bind _start to state so that within it, self will
" always refer to state. See :help Partial for more information.
"
" _start is intended only for internal use and should not be referenced
" outside of this file.
let cbs._start = function('s:start', [''], state)
" explicitly bind callback to state so that within it, self will
" always refer to state. See :help Partial for more information.
let cbs.callback = function('s:callback', [], state)
" explicitly bind exit_cb to state so that within it, self will always refer
" to state. See :help Partial for more information.
let cbs.exit_cb = function('s:exit_cb', [], state)
" explicitly bind close_cb to state so that within it, self will
" always refer to state. See :help Partial for more information.
let cbs.close_cb = function('s:close_cb', [], state)
function state.show_errors(job, exit_status, data)
if self.for == '_'
return
endif
let l:winid = win_getid(winnr())
" Always set the active window to the window that was active when the job
" was started. 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 starting the job.
call win_gotoid(self.winid)
let l:listtype = go#list#Type(self.for)
if a:exit_status == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
return
endif
let l:listtype = go#list#Type(self.for)
if len(a:data) == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
return
endif
let out = join(self.messages, "\n")
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
try
" parse the errors relative to self.jobdir
execute l:cd fnameescape(self.jobdir)
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
let errors = go#list#Get(l:listtype)
finally
execute l:cd fnameescape(self.dir)
endtry
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError([self.dir] + self.messages)
call win_gotoid(l:winid)
return
endif
" only open the error window if user was still in the window from which
" the job was started.
if self.winid == l:winid
call go#list#Window(l:listtype, len(errors))
if self.bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction
return cbs
endfunction
function! s:start(args) dict
if go#config#EchoCommandInfo() && self.statustype != ""
let prefix = '[' . self.statustype . '] '
call go#util#EchoSuccess(prefix . "dispatched")
endif
if self.statustype != ''
let status = {
\ 'desc': 'current status',
\ 'type': self.statustype,
\ 'state': "started",
\ }
call go#statusline#Update(self.jobdir, status)
endif
let self.started_at = reltime()
endfunction
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
function! s:exit_cb(job, exitval) dict
let self.exit_status = a:exitval
let self.exited = 1
call self.show_status(a:job, a:exitval)
if self.closed || has('nvim')
call self.complete(a:job, self.exit_status, self.messages)
endif
endfunction
function! s:close_cb(ch) dict
let self.closed = 1
if self.exited
let job = ch_getjob(a:ch)
call self.complete(job, self.exit_status, self.messages)
endif
endfunction
" go#job#Start runs a job. The options are expected to be the options
" suitable for Vim8 jobs. When called from Neovim, Vim8 options will be
" transformed to their Neovim equivalents.
function! go#job#Start(cmd, options)
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:options = copy(a:options)
if has('nvim')
let l:options = s:neooptions(l:options)
endif
" Verify that the working directory for the job actually exists. Return
" early if the directory does not exist. This helps avoid errors when
" working with plugins that use virtual files that don't actually exist on
" the file system.
let l:filedir = expand("%:p:h")
if has_key(l:options, 'cwd') && !isdirectory(l:options.cwd)
return
elseif !isdirectory(l:filedir)
return
endif
let l:manualcd = 0
if !has_key(l:options, 'cwd')
" pre start
let l:manualcd = 1
let dir = getcwd()
execute l:cd fnameescape(filedir)
endif
if has_key(l:options, '_start')
call l:options._start()
" remove _start to play nicely with vim (when vim encounters an unexpected
" job option it reports an "E475: invalid argument" error).
unlet l:options._start
endif
" noblock was added in 8.1.350; remove it if it's not supported.
if has_key(l:options, 'noblock') && (has('nvim') || !has("patch-8.1.350"))
call remove(l:options, 'noblock')
endif
if go#util#HasDebug('shell-commands')
call go#util#EchoInfo('job command: ' . string(a:cmd))
endif
if has('nvim')
let l:input = []
if has_key(a:options, 'in_io') && a:options.in_io ==# 'file' && !empty(a:options.in_name)
let l:input = readfile(a:options.in_name, "b")
endif
let job = jobstart(a:cmd, l:options)
if len(l:input) > 0
call chansend(job, l:input)
" close stdin to signal that no more bytes will be sent.
call chanclose(job, 'stdin')
endif
else
let l:cmd = a:cmd
if go#util#IsWin()
let l:cmd = join(map(copy(a:cmd), function('s:winjobarg')), " ")
endif
let job = job_start(l:cmd, l:options)
endif
if l:manualcd
" post start
execute l:cd fnameescape(l:dir)
endif
return job
endfunction
" s:neooptions returns a dictionary of job options suitable for use by Neovim
" based on a dictionary of job options suitable for Vim8.
function! s:neooptions(options)
let l:options = {}
let l:options['stdout_buf'] = ''
let l:options['stderr_buf'] = ''
let l:err_mode = get(a:options, 'err_mode', get(a:options, 'mode', ''))
let l:out_mode = get(a:options, 'out_mode', get(a:options, 'mode', ''))
for key in keys(a:options)
if key == 'cwd'
let l:options['cwd'] = a:options['cwd']
continue
endif
if key == 'callback'
let l:options['callback'] = a:options['callback']
if !has_key(a:options, 'out_cb')
let l:options['on_stdout'] = function('s:callback2on_stdout', [l:out_mode], l:options)
endif
if !has_key(a:options, 'err_cb')
let l:options['on_stderr'] = function('s:callback2on_stderr', [l:err_mode], l:options)
endif
continue
endif
if key == 'out_cb'
let l:options['out_cb'] = a:options['out_cb']
let l:options['on_stdout'] = function('s:on_stdout', [l:out_mode], l:options)
continue
endif
if key == 'err_cb'
let l:options['err_cb'] = a:options['err_cb']
let l:options['on_stderr'] = function('s:on_stderr', [l:err_mode], l:options)
continue
endif
if key == 'exit_cb'
let l:options['exit_cb'] = a:options['exit_cb']
let l:options['on_exit'] = function('s:on_exit', [], l:options)
continue
endif
if key == 'close_cb'
continue
endif
if key == 'stoponexit'
if a:options['stoponexit'] == ''
let l:options['detach'] = 1
endif
continue
endif
endfor
return l:options
endfunction
function! s:callback2on_stdout(mode, ch, data, event) dict
let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.callback)
endfunction
function! s:callback2on_stderr(mode, ch, data, event) dict
let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.callback)
endfunction
function! s:on_stdout(mode, ch, data, event) dict
let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.out_cb)
endfunction
function! s:on_stderr(mode, ch, data, event) dict
let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.err_cb )
endfunction
function! s:on_exit(jobid, exitval, event) dict
call self.exit_cb(a:jobid, a:exitval)
endfunction
function! go#job#Stop(job) abort
if has('nvim')
call jobstop(a:job)
return
endif
call job_stop(a:job)
call go#job#Wait(a:job)
return
endfunction
function! go#job#Wait(job) abort
if has('nvim')
call jobwait([a:job])
return
endif
while job_status(a:job) is# 'run'
sleep 50m
endwhile
endfunction
function! s:winjobarg(idx, val) abort
if empty(a:val)
return '""'
endif
return a:val
endfunction
function! s:neocb(mode, ch, buf, data, callback)
" dealing with the channel lines of Neovim is awful. The docs (:help
" channel-lines) say:
" stream event handlers may receive partial (incomplete) lines. For a
" given invocation of on_stdout etc, `a:data` is not guaranteed to end
" with a newline.
" - `abcdefg` may arrive as `['abc']`, `['defg']`.
" - `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`,
" `['','efg']`, or even `['ab']`, `['c','efg']`.
"
" Thankfully, though, this is explained a bit better in an issue:
" https://github.com/neovim/neovim/issues/3555. Specifically in these two
" comments:
" * https://github.com/neovim/neovim/issues/3555#issuecomment-152290804
" * https://github.com/neovim/neovim/issues/3555#issuecomment-152588749
"
" The key is
" Every item in the list passed to job control callbacks represents a
" string after a newline(Except the first, of course). If the program
" outputs: "hello\nworld" the corresponding list is ["hello", "world"].
" If the program outputs "hello\nworld\n", the corresponding list is
" ["hello", "world", ""]. In other words, you can always determine if
" the last line received is complete or not.
" and
" for every list you receive in a callback, all items except the first
" represent newlines.
let l:buf = ''
" 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.
if a:buf == ''
return ''
endif
let l:data = [a:buf]
else
let l:data = copy(a:data)
let l:data[0] = a:buf . l:data[0]
" The last element may be a partial line; save it for next time.
if a:mode != 'raw'
let l:buf = l:data[-1]
let l:data = l:data[:-2]
endif
endif
let l:i = 0
let l:last = len(l:data) - 1
while l:i <= l:last
let l:msg = l:data[l:i]
if a:mode == 'raw' && l:i < l:last
let l:msg = l:msg . "\n"
endif
call a:callback(a:ch, l:msg)
let l:i += 1
endwhile
return l:buf
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,53 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_JobDirWithSpaces()
if !go#util#has_job()
return
endif
try
let l:filename = 'job/dir has spaces/main.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . fnameescape(l:tmp . '/src/job/dir has spaces')
" set the compiler type so that the errorformat option will be set
" correctly.
compiler go
let expected = [{'lnum': 4, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'undefined: notafunc'}]
" clear the quickfix lists
call setqflist([], 'r')
" go build discards any results when it compiles multiple packages. So we
" pass the `errors` package just as a placeholder with the current folder
" (indicated with '.').
let l:cmd = ['go', 'build', '.', 'errors']
let l:complete = go#promise#New(function('s:complete'), 10000, '')
call go#job#Spawn(l:cmd, {
\ 'for': 'GoBuild',
\ 'complete': l:complete.wrapper,
\ 'statustype': 'build'
\})
let l:out = l:complete.await()
let actual = getqflist()
call gotest#assert_quickfix(actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! s:complete(job, exit_code, messages)
return a:messages
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,64 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#keyify#Keyify()
" Needs: https://github.com/dominikh/go-tools/pull/272
"\ '-tags', go#config#BuildTags(),
let l:cmd = ['keyify',
\ '-json',
\ printf('%s:#%s', fnamemodify(expand('%'), ':p:gs?\\?/?'), go#util#OffsetCursor())]
let [l:out, l:err] = go#util#Exec(l:cmd)
if l:err
call go#util#EchoError("non-zero exit code: " . l:out)
return
endif
silent! let result = json_decode(l:out)
" We want to output the error message in case the result isn't a JSON
if type(result) != type({})
call go#util#EchoError(s:chomp(l:out))
return
endif
" Because keyify returns the byte before the region we want, we goto the
" byte after that
execute "goto" result.start + 1
let start = getpos('.')
execute "goto" result.end
let end = getpos('.')
let vis_start = getpos("'<")
let vis_end = getpos("'>")
" Replace contents between start and end with `replacement`
call setpos("'<", start)
call setpos("'>", end)
let select = 'gv'
" Make sure the visual mode is 'v', to avoid some bugs
normal! gv
if mode() !=# 'v'
let select .= 'v'
endif
silent! execute "normal!" select."\"=result.replacement\<cr>p"
" Replacement text isn't aligned, so it needs fix
normal! '<v'>=
call setpos("'<", vis_start)
call setpos("'>", vis_end)
endfunction
function! s:chomp(string)
return substitute(a:string, '\n\+$', '', '')
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,279 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#lint#Gometa(bang, autosave, ...) abort
if a:0 == 0
let goargs = [expand('%:p:h')]
else
let goargs = a:000
endif
let l:metalinter = go#config#MetalinterCommand()
if l:metalinter == 'golangci-lint'
let cmd = s:metalintercmd(l:metalinter)
if empty(cmd)
return
endif
" linters
let linters = a:autosave ? go#config#MetalinterAutosaveEnabled() : go#config#MetalinterEnabled()
for linter in linters
let cmd += ["--enable=".linter]
endfor
else
" the user wants something else, let us use it.
let cmd = split(go#config#MetalinterCommand(), " ")
endif
if a:autosave
" redraw so that any messages that were displayed while writing the file
" will be cleared
redraw
if l:metalinter == "golangci-lint"
let goargs[0] = expand('%:p:h')
endif
endif
" Call metalinter asynchronously.
let deadline = go#config#MetalinterDeadline()
if deadline != ''
let cmd += ["--deadline=" . deadline]
endif
let cmd += goargs
" Golangci-lint can output the following:
" <file>:<line>:<column>: <message> (<linter>)
" This can be defined by the following errorformat:
let errformat = "%f:%l:%c:\ %m"
if go#util#has_job()
call s:lint_job({'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat}, a:bang, a:autosave)
return
endif
let [l:out, l:err] = go#util#Exec(cmd)
if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
else
let l:listtype = go#list#Type("GoMetaLinter")
endif
if l:err == 0
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[metalinter] PASS')
else
let l:winid = win_getid(winnr())
" Parse and populate our location list
let l:messages = split(out, "\n")
if a:autosave
call s:metalinterautosavecomplete(fnamemodify(expand('%:p'), ":."), 0, 1, l:messages)
endif
call go#list#ParseFormat(l:listtype, errformat, l:messages, 'GoMetaLinter')
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if a:autosave || a:bang
call win_gotoid(l:winid)
return
endif
call go#list#JumpToFirst(l:listtype)
endif
endfunction
" Golint calls 'golint' on the current directory. Any warnings are populated in
" the location list
function! go#lint#Golint(bang, ...) abort
if a:0 == 0
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), expand('%:p:h')])
else
let [l:out, l:err] = go#util#Exec([go#config#GolintBin()] + a:000)
endif
if empty(l:out)
call go#util#EchoSuccess('[lint] PASS')
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 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
" the location list
function! go#lint#Vet(bang, ...) abort
call go#cmd#autowrite()
if go#config#EchoCommandInfo()
call go#util#EchoProgress('calling vet...')
endif
if a:0 == 0
let [l:out, l:err] = go#util#Exec(['go', 'vet', go#package#ImportPath()])
else
let [l:out, l:err] = go#util#Exec(['go', 'vet'] + a:000 + [go#package#ImportPath()])
endif
let l:listtype = go#list#Type("GoVet")
if l:err != 0
let l:winid = win_getid(winnr())
let l:errorformat = "%-Gexit status %\\d%\\+," . &errorformat
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
else
call win_gotoid(l:winid)
endif
else
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[vet] PASS')
endif
endfunction
" ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in
" the location list
function! go#lint#Errcheck(bang, ...) abort
if a:0 == 0
let l:import_path = go#package#ImportPath()
if import_path == -1
call go#util#EchoError('package is not inside GOPATH src')
return
endif
else
let l:import_path = join(a:000, ' ')
endif
call go#util#EchoProgress('[errcheck] analysing ...')
redraw
let [l:out, l:err] = go#util#Exec([go#config#ErrcheckBin(), '-abspath', l:import_path])
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
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
let l:errors = go#list#Get(l:listtype)
if empty(l:errors)
call go#util#EchoError(l:out)
return
endif
if !empty(errors)
call go#list#Populate(l:listtype, errors, 'Errcheck')
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
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[errcheck] PASS')
endif
endfunction
function! go#lint#ToggleMetaLinterAutoSave() abort
if go#config#MetalinterAutosave()
call go#config#SetMetalinterAutosave(0)
call go#util#EchoProgress("auto metalinter disabled")
return
end
call go#config#SetMetalinterAutosave(1)
call go#util#EchoProgress("auto metalinter enabled")
endfunction
function! s:lint_job(args, bang, autosave)
let l:opts = {
\ 'statustype': a:args.statustype,
\ 'errorformat': a:args.errformat,
\ 'for': "GoMetaLinter",
\ 'bang': a:bang,
\ }
if a:autosave
let l:opts.for = "GoMetaLinterAutoSave"
" s:metalinterautosavecomplete is really only needed for golangci-lint
let l:opts.complete = funcref('s:metalinterautosavecomplete', [expand('%:p:t')])
endif
" autowrite is not enabled for jobs
call go#cmd#autowrite()
call go#job#Spawn(a:args.cmd, l:opts)
endfunction
function! s:metalintercmd(metalinter)
let l:cmd = []
let bin_path = go#path#CheckBinPath(a:metalinter)
if !empty(bin_path)
if a:metalinter == "golangci-lint"
let l:cmd = s:golangcilintcmd(bin_path)
endif
endif
return cmd
endfunction
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
function! s:metalinterautosavecomplete(filepath, job, exit_code, messages)
if len(a:messages) == 0
return
endif
let l:idx = len(a:messages) - 1
while l:idx >= 0
" Go 1.13 changed how go vet output is formatted by prepending a leading
" 'vet :', so account for that, too. This function is really needed for
" gometalinter at all, so the check for Go 1.13's go vet output shouldn't
" be neeeded, but s:lint_job hooks this up even when the
" g:go_metalinter_command is golangci-lint.
if a:messages[l:idx] !~# '^' . a:filepath . ':' && a:messages[l:idx] !~# '^vet: \.[\\/]' . a:filepath . ':'
call remove(a:messages, l:idx)
endif
let l:idx -= 1
endwhile
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,206 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_GometaGolangciLint() abort
call s:gometa('golangci-lint')
endfunc
func! s:gometa(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
if a:metalinter == 'golangci-lint'
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+2, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingFooDoc` should have comment or be unexported (golint)'}
\ ]
endif
" clear the quickfix lists
call setqflist([], 'r')
let g:go_metalinter_enabled = ['golint']
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_enabled
endtry
endfunc
func! Test_GometaAutoSaveGolangciLint() abort
call s:gometaautosave('golangci-lint')
endfunc
func! s:gometaautosave(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ ]
if a:metalinter == 'golangci-lint'
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingDoc` should have comment or be unexported (golint)'}
\ ]
endif
let winnr = winnr()
" clear the location lists
call setloclist(l:winnr, [], 'r')
let g:go_metalinter_autosave_enabled = ['golint']
call go#lint#Gometa(0, 1)
let actual = getloclist(l:winnr)
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(l:winnr)
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_autosave_enabled
endtry
endfunc
func! Test_Vet() abort
let l:tmp = gotest#load_fixture('lint/src/vet/vet.go')
try
let expected = [
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '',
\ 'text': 'Printf format %d has arg str of wrong type string'}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Vet_compilererror() abort
let l:tmp = gotest#load_fixture('lint/src/vet/compilererror/compilererror.go')
try
let expected = [
\ {'lnum': 6, 'bufnr': bufnr('%'), 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': "missing ',' before newline in argument list (and 1 more errors)"}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Lint_GOPATH() abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
compiler go
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
\ {'lnum': 5, 'bufnr': 6, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Golint(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
call call(RestoreGOPATH, [])
endfunc
func! Test_Lint_NullModule() abort
silent exe 'e ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint/src/lint/lint.go'
compiler go
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
\ {'lnum': 5, 'bufnr': 6, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Golint(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,173 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Window opens the list with the given height up to 10 lines maximum.
" Otherwise g:go_loclist_height is used.
"
" If no or zero height is given it closes the window by default.
" To prevent this, set g:go_list_autoclose = 0
function! go#list#Window(listtype, ...) abort
" we don't use lwindow to close the location list as we need also the
" ability to resize the window. So, we are going to use lopen and lclose
" for a better user experience. If the number of errors in a current
" location list increases/decreases, cwindow will not resize when a new
" updated height is passed. lopen in the other hand resizes the screen.
if !a:0 || a:1 == 0
call go#list#Close(a:listtype)
return
endif
let height = go#config#ListHeight()
if height == 0
" prevent creating a large location height for a large set of numbers
if a:1 > 10
let height = 10
else
let height = a:1
endif
endif
if a:listtype == "locationlist"
exe 'lopen ' . height
else
exe 'copen ' . height
endif
endfunction
" Get returns the current items from the list
function! go#list#Get(listtype) abort
if a:listtype == "locationlist"
return getloclist(0)
else
return getqflist()
endif
endfunction
" Populate populate the list with the given items
function! go#list#Populate(listtype, items, title) abort
if a:listtype == "locationlist"
call setloclist(0, a:items, 'r')
call setloclist(0, [], 'a', {'title': a:title})
else
call setqflist(a:items, 'r')
call setqflist([], 'a', {'title': a:title})
endif
endfunction
" Parse parses the given items based on the specified errorformat and
" populates the list.
function! go#list#ParseFormat(listtype, errformat, items, title) abort
" backup users errorformat, will be restored once we are finished
let old_errorformat = &errorformat
" parse and populate the location list
let &errorformat = a:errformat
try
call go#list#Parse(a:listtype, a:items, a:title)
finally
"restore back
let &errorformat = old_errorformat
endtry
endfunction
" Parse parses the given items based on the global errorformat and
" populates the list.
function! go#list#Parse(listtype, items, title) abort
if a:listtype == "locationlist"
lgetexpr a:items
call setloclist(0, [], 'a', {'title': a:title})
else
cgetexpr a:items
call setqflist([], 'a', {'title': a:title})
endif
endfunction
" JumpToFirst jumps to the first item in the location list
function! go#list#JumpToFirst(listtype) abort
if a:listtype == "locationlist"
ll 1
else
cc 1
endif
endfunction
" Clean cleans and closes the location list
function! go#list#Clean(listtype) abort
if a:listtype == "locationlist"
lex []
else
cex []
endif
call go#list#Close(a:listtype)
endfunction
" Close closes the location list
function! go#list#Close(listtype) abort
let autoclose_window = go#config#ListAutoclose()
if !autoclose_window
return
endif
if a:listtype == "locationlist"
lclose
else
cclose
endif
endfunction
function! s:listtype(listtype) abort
let listtype = go#config#ListType()
if empty(listtype)
return a:listtype
endif
return listtype
endfunction
" s:default_list_type_commands is the defaults that will be used for each of
" the supported commands (see documentation for g:go_list_type_commands). When
" defining a default, quickfix should be used if the command operates on
" multiple files, while locationlist should be used if the command operates on a
" single file or buffer. Keys that begin with an underscore are not supported
" in g:go_list_type_commands.
let s:default_list_type_commands = {
\ "GoBuild": "quickfix",
\ "GoDebug": "quickfix",
\ "GoErrCheck": "quickfix",
\ "GoFmt": "locationlist",
\ "GoGenerate": "quickfix",
\ "GoInstall": "quickfix",
\ "GoLint": "quickfix",
\ "GoMetaLinter": "quickfix",
\ "GoMetaLinterAutoSave": "locationlist",
\ "GoModFmt": "locationlist",
\ "GoModifyTags": "locationlist",
\ "GoRename": "quickfix",
\ "GoRun": "quickfix",
\ "GoTest": "quickfix",
\ "GoVet": "quickfix",
\ "_guru": "locationlist",
\ "_term": "locationlist",
\ "_job": "locationlist",
\ }
function! go#list#Type(for) abort
let l:listtype = s:listtype(get(s:default_list_type_commands, a:for))
if l:listtype == "0"
call go#util#EchoError(printf(
\ "unknown list type command value found ('%s'). Please open a bug report in the vim-go repo.",
\ a:for))
let l:listtype = "quickfix"
endif
return get(go#config#ListTypeCommands(), a:for, l:listtype)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

File diff suppressed because it is too large Load Diff

View File

@ -1,63 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:Text = 1
let s:Method = 2
let s:Function = 3
let s:Constructor = 4
let s:Field = 5
let s:Variable = 6
let s:Class = 7
let s:Interface = 8
let s:Module = 9
let s:Property = 10
let s:Unit = 11
let s:Value = 12
let s:Enum = 13
let s:Keyword = 14
let s:Snippet = 15
let s:Color = 16
let s:File = 17
let s:Reference = 18
let s:Folder = 19
let s:EnumMember = 20
let s:Constant = 21
let s:Struct = 22
let s:Event = 23
let s:Operator = 24
let s:TypeParameter = 25
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
return 'v'
elseif a:kind == s:Field || a:kind == s:Property
return 'm'
elseif a:kind == s:Class || a:kind == s:Interface || a:kind == s:Struct
return 't'
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
" vim: sw=2 ts=2 et

View File

@ -1,58 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" go#lsp#lsp#Position returns the LSP text position. If no arguments are
" provided, the cursor position is assumed. Otherwise, there should be two
" arguments: the line and the column.
function! go#lsp#lsp#Position(...)
if a:0 < 2
let [l:line, l:col] = getpos('.')[1:2]
else
let l:line = a:1
let l:col = a:2
endif
let l:content = getline(l:line)
" LSP uses 0-based lines.
return [l:line - 1, s:character(l:line, l:col-1)]
endfunction
function! s:strlen(str) abort
let l:runes = split(a:str, '\zs')
return len(l:runes) + len(filter(l:runes, 'char2nr(v:val)>=0x10000'))
endfunction
function! s:character(line, col) abort
return s:strlen(getline(a:line)[:col([a:line, a:col - 1])])
endfunction
" go#lsp#PositionOf returns len(content[0:units]) where units is utf-16 code
" units. This is mostly useful for converting LSP text position to vim
" position.
function! go#lsp#lsp#PositionOf(content, units) abort
if a:units == 0
return 1
endif
let l:remaining = a:units
let l:str = ""
for l:rune in split(a:content, '\zs')
if l:remaining < 0
break
endif
let l:remaining -= 1
if char2nr(l:rune) >= 0x10000
let l:remaining -= 1
endif
let l:str = l:str . l:rune
endfor
return len(l:str)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,32 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
function! Test_PositionOf_Simple()
let l:actual = go#lsp#lsp#PositionOf("just ascii", 3)
call assert_equal(4, l:actual)
endfunc
function! Test_PositionOf_MultiByte()
" ⌘ is U+2318, which encodes to three bytes in utf-8 and 1 code unit in
" utf-16.
let l:actual = go#lsp#lsp#PositionOf("⌘⌘ foo", 3)
call assert_equal(8, l:actual)
endfunc
function! Test_PositionOf_MultipleCodeUnit()
" 𐐀 is U+10400, which encodes to 4 bytes in utf-8 and 2 code units in
" utf-16.
let l:actual = go#lsp#lsp#PositionOf("𐐀 bar", 3)
call assert_equal(6, l:actual)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,221 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#lsp#message#Initialize(wd) abort
return {
\ 'notification': 0,
\ 'method': 'initialize',
\ 'params': {
\ 'processId': getpid(),
\ 'rootUri': go#path#ToURI(a:wd),
\ 'capabilities': {
\ 'workspace': {
\ 'workspaceFolders': v:true,
\ 'didChangeConfiguration': {
\ 'dynamicRegistration': v:true,
\ },
\ 'configuration': v:true,
\ },
\ 'textDocument': {
\ 'hover': {
\ 'contentFormat': ['plaintext'],
\ },
\ }
\ },
\ 'workspaceFolders': [s:workspaceFolder(0, a:wd)],
\ }
\ }
endfunction
function! go#lsp#message#Initialized() abort
return {
\ 'notification': 1,
\ 'method': 'initialized',
\ 'params': {},
\ }
endfunction
function! go#lsp#message#Shutdown() abort
return {
\ 'notification': 0,
\ 'method': 'shutdown',
\ }
endfunction
function! go#lsp#message#Exit() abort
return {
\ 'notification': 1,
\ 'method': 'exit',
\ }
endfunction
function! go#lsp#message#WorkspaceFoldersResult(dirs) abort
return map(copy(a:dirs), function('s:workspaceFolder', []))
endfunction
function! go#lsp#message#Definition(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/definition',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col)
\ }
\ }
endfunction
function! go#lsp#message#TypeDefinition(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/typeDefinition',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col)
\ }
\ }
endfunction
function! go#lsp#message#DidOpen(file, content) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didOpen',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file),
\ 'languageId': 'go',
\ 'text': a:content,
\ }
\ }
\ }
endfunction
function! go#lsp#message#DidChange(file, content) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didChange',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file),
\ },
\ 'contentChanges': [
\ {
\ 'text': a:content,
\ }
\ ]
\ }
\ }
endfunction
function! go#lsp#message#DidClose(file) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didClose',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file),
\ }
\ }
\ }
endfunction
function! go#lsp#message#Completion(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/completion',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col),
\ }
\ }
endfunction
function! go#lsp#message#References(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/references',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col),
\ 'context': {
\ 'includeDeclaration': v:true,
\ },
\ }
\ }
endfunction
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! go#lsp#message#ChangeWorkspaceFolders(add, remove) abort
let l:addDirs = map(copy(a:add), function('s:workspaceFolder', []))
let l:removeDirs = map(copy(a:add), function('s:workspaceFolder', []))
return {
\ 'notification': 1,
\ 'method': 'workspace/didChangeWorkspaceFolders',
\ 'params': {
\ 'event': {
\ 'removed': l:removeDirs,
\ 'added': l:addDirs,
\ },
\ }
\ }
endfunction
function! go#lsp#message#ConfigurationResult(items) abort
let l:result = []
" results must be in the same order as the items
for l:item in a:items
let l:config = {
\ 'buildFlags': [],
\ 'hoverKind': 'NoDocumentation',
\ 'deepCompletion': go#config#GoplsDeepCompletion() ? v:true : v:false,
\ 'fuzzyMatching': go#config#GoplsFuzzyMatching() ? v:true : v:false,
\ 'completeUnimported': go#config#GoplsCompleteUnimported() ? v:true : v:false,
\ 'usePlaceholders': go#config#GoplsUsePlaceholders() ? v:true : v:false,
\ }
let l:buildtags = go#config#BuildTags()
if buildtags isnot ''
let l:config.buildFlags = extend(l:config.buildFlags, ['-tags', go#config#BuildTags()])
endif
let l:result = add(l:result, l:config)
endfor
return l:result
endfunction
function s:workspaceFolder(key, val) abort
return {'uri': go#path#ToURI(a:val), 'name': a:val}
endfunction
function! s:position(line, col) abort
return {'line': a:line, 'character': a:col}
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,49 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
function! Test_GetSimpleTextPosition()
call s:getinfo('lsp text position should align with cursor position after ascii only', 'ascii')
endfunction
function! Test_GetMultiByteTextPosition()
call s:getinfo('lsp text position should align with cursor position after two place of interest symbols ⌘⌘', 'multi-byte')
endfunction
function! Test_GetMultipleCodeUnitTextPosition()
call s:getinfo('lsp text position should align with cursor position after Deseret Capital Letter Long I 𐐀', 'multi-code-units')
endfunction
function! s:getinfo(str, name)
if !go#util#has_job()
return
endif
try
let g:go_info_mode = 'gopls'
let l:tmp = gotest#write_file(a:name . '/position/position.go', [
\ 'package position',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" .'"' . a:str . '", id)',
\ '}',
\ ] )
let l:expected = 'var id string'
let l:actual = go#lsp#GetInfo()
call assert_equal(l:expected, l:actual)
finally
call delete(l:tmp, 'rf')
unlet g:go_info_mode
endtry
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,147 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
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#Exec(['go', 'version']), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
if len(tokens) > 0
let s:go_major_version = str2nr(tokens[1])
else
let s:go_major_version = ""
endif
endif
if !empty(s:go_major_version) && s:go_major_version < "11"
call go#util#EchoError("Go v1.11 is required to format go.mod file")
return
endif
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
" Save cursor position and many other things.
let l:curw = winsaveview()
" Write current unsaved buffer to a temp file
let l:tmpname = tempname() . '.mod'
call writefile(go#util#GetLines(), l:tmpname)
if go#util#IsWin()
let l:tmpname = tr(l:tmpname, '\', '/')
endif
let current_col = col('.')
let l:args = ['go', 'mod', 'edit', '--fmt', l:tmpname]
let [l:out, l:err] = go#util#Exec(l:args)
let diff_offset = len(readfile(l:tmpname)) - line('$')
if l:err == 0
call go#mod#update_file(l:tmpname, fname)
else
let errors = s:parse_errors(fname, l:out)
call s:show_errors(errors)
endif
" We didn't use the temp file, so clean up
call delete(l:tmpname)
" Restore our cursor/windows positions.
call winrestview(l:curw)
" be smart and jump to the line the new statement was added/removed
call cursor(line('.') + diff_offset, current_col)
" Syntax highlighting breaks less often.
syntax sync fromstart
endfunction
" update_file updates the target file with the given formatted source
function! go#mod#update_file(source, target)
" remove undo point caused via BufWritePre
try | silent undojoin | catch | endtry
let old_fileformat = &fileformat
if exists("*getfperm")
" save file permissions
let original_fperm = getfperm(a:target)
endif
call rename(a:source, a:target)
" restore file permissions
if exists("*setfperm") && original_fperm != ''
call setfperm(a:target , original_fperm)
endif
" reload buffer to reflect latest changes
silent edit!
let &fileformat = old_fileformat
let &syntax = &syntax
let l:listtype = go#list#Type("GoModFmt")
" clean up previous list
if l:listtype == "quickfix"
let l:list_title = getqflist({'title': 1})
else
let l:list_title = getloclist(0, {'title': 1})
endif
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype)
endif
endfunction
" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
let splitted = split(a:content, '\n')
" list of errors to be put into location list
let errors = []
for line in splitted
let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\s*\(.*\)')
if !empty(tokens)
call add(errors,{
\"filename": a:filename,
\"lnum": tokens[2],
\"text": tokens[3],
\ })
endif
endfor
return errors
endfunction
" show_errors opens a location list and shows the given errors. If the given
" errors is empty, it closes the the location list
function! s:show_errors(errors) abort
let l:listtype = go#list#Type("GoModFmt")
if !empty(a:errors)
call go#list#Populate(l:listtype, a:errors, 'Format')
call go#util#EchoError("GoModFmt returned error")
endif
" this closes the window if there are no errors or it opens
" it if there is any
call go#list#Window(l:listtype, len(a:errors))
endfunction
function! go#mod#ToggleModFmtAutoSave() abort
if go#config#ModFmtAutosave()
call go#config#SetModFmtAutosave(0)
call go#util#EchoProgress("auto mod fmt disabled")
return
end
call go#config#SetModFmtAutosave(1)
call go#util#EchoProgress("auto mod fmt enabled")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,309 +0,0 @@
" Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file.
"
" This file provides a utility function that performs auto-completion of
" package names, for use by other commands.
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:goos = $GOOS
let s:goarch = $GOARCH
if len(s:goos) == 0
if exists('g:golang_goos')
let s:goos = g:golang_goos
elseif has('win32') || has('win64')
let s:goos = 'windows'
elseif has('macunix')
let s:goos = 'darwin'
else
let s:goos = '*'
endif
endif
if len(s:goarch) == 0
if exists('g:golang_goarch')
let s:goarch = g:golang_goarch
else
let s:goarch = '*'
endif
endif
function! s:paths() abort
let dirs = []
if !exists("s:goroot")
if executable('go')
let s:goroot = go#util#env("goroot")
if go#util#ShellError() != 0
call go#util#EchoError('`go env GOROOT` failed')
endif
else
let s:goroot = $GOROOT
endif
endif
if len(s:goroot) != 0 && isdirectory(s:goroot)
let dirs += [s:goroot]
endif
let workspaces = split(go#path#Default(), go#util#PathListSep())
if workspaces != []
let dirs += workspaces
endif
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
if empty(l:root)
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. It
" returns -1 if the import path cannot be determined.
function! go#package#ImportPath() abort
let l:dir = expand("%:p:h")
if has_key(s:import_paths, dir)
return s:import_paths[l:dir]
endif
let l:importpath = go#package#FromPath(l:dir)
if type(l:importpath) == type(0)
return -1
endif
let s:import_paths[l:dir] = l:importpath
return l:importpath
endfunction
" 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, not in a module, and not below the current working
" directory. A relative path is returned when in a null module at or below the
" current working directory..
function! go#package#FromPath(arg) abort
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:dir = getcwd()
let l:path = fnamemodify(a:arg, ':p')
if !isdirectory(l:path)
let l:path = fnamemodify(l:path, ':h')
endif
execute l:cd fnameescape(l:path)
try
if glob("*.go") == ""
" There's no Go code in this directory. We might be in a module directory
" which doesn't have any code at this level. To avoid `go list` making a
" bunch of HTTP requests to fetch dependencies, short-circuit `go list`
" and return -1 immediately.
if !empty(s:module())
return -1
endif
endif
let [l:out, l:err] = go#util#Exec(['go', 'list'])
if l:err != 0
return -1
endif
let l:importpath = split(l:out, '\n')[0]
finally
execute l:cd fnameescape(l:dir)
endtry
" go list returns '_CURRENTDIRECTORY' if the directory is in a null module
" (i.e. neither in GOPATH nor in a module). Return a relative import path
" if possible or an error if that is the case.
if l:importpath[0] ==# '_'
let l:relativeimportpath = fnamemodify(l:importpath[1:], ':.')
if go#util#IsWin()
let l:relativeimportpath = substitute(l:relativeimportpath, '\\', '/', 'g')
endif
if l:relativeimportpath == l:importpath[1:]
return '.'
endif
if l:relativeimportpath[0] == '/'
return -2
endif
let l:importpath= printf('./%s', l:relativeimportpath)
endif
return l:importpath
endfunction
function! go#package#CompleteMembers(package, member) abort
let [l:content, l:err] = go#util#Exec(['go', 'doc', a:package])
if l:err || !len(content)
return []
endif
let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'")
try
let mx1 = '^\s\+\(\S+\)\s\+=\s\+.*'
let mx2 = '^\%(const\|var\|type\|func\) \([A-Z][^ (]\+\).*'
let candidates = map(filter(copy(lines), 'v:val =~ mx1'),
\ 'substitute(v:val, mx1, "\\1", "")')
\ + map(filter(copy(lines), 'v:val =~ mx2'),
\ 'substitute(v:val, mx2, "\\1", "")')
return filter(candidates, '!stridx(v:val, a:member)')
catch
return []
endtry
endfunction
function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
let words = split(a:CmdLine, '\s\+', 1)
" do not complete package members for these commands
let neglect_commands = ["GoImportAs", "GoGuruScope"]
if len(words) > 2 && index(neglect_commands, words[0]) == -1
" Complete package members
return go#package#CompleteMembers(words[1], words[2])
endif
let dirs = s:paths()
let module = s:module()
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")
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 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 candidate = go#util#StripPathSep(candidate)
let ret[candidate] = candidate
endfor
endfor
endfor
return sort(keys(ret))
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,58 +0,0 @@
" 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

@ -1,248 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" initial_go_path is used to store the initial GOPATH that was set when Vim
" was started. It's used with :GoPathClear to restore the GOPATH when the user
" changed it explicitly via :GoPath. Initially it's empty. It's being set when
" :GoPath is used
let s:initial_go_path = ""
" GoPath sets or echos the current GOPATH. If no arguments are passed it
" echoes the current GOPATH, if an argument is passed it replaces the current
" GOPATH with it. If two double quotes are passed (the empty string in go),
" it'll clear the GOPATH and will restore to the initial GOPATH.
function! go#path#GoPath(...) abort
" no argument, show GOPATH
if len(a:000) == 0
echo go#path#Default()
return
endif
" we have an argument, replace GOPATH
" clears the current manually set GOPATH and restores it to the
" initial GOPATH, which was set when Vim was started.
if len(a:000) == 1 && a:1 == '""'
if !empty(s:initial_go_path)
let $GOPATH = s:initial_go_path
let s:initial_go_path = ""
endif
call go#util#EchoInfo("GOPATH restored to ". $GOPATH)
return
endif
call go#util#EchoInfo("GOPATH changed to ". a:1)
let s:initial_go_path = $GOPATH
let $GOPATH = a:1
endfunction
" Default returns the default GOPATH. If GOPATH is not set, it uses the
" default GOPATH set starting with Go 1.8. This GOPATH can be retrieved via
" 'go env GOPATH'
function! go#path#Default() abort
if $GOPATH == ""
" use default GOPATH via go env
return go#util#env("gopath")
endif
return $GOPATH
endfunction
" s:HasPath checks whether the given path exists in GOPATH environment variable
" or not
function! s:HasPath(path) abort
let go_paths = split(go#path#Default(), go#util#PathListSep())
let last_char = strlen(a:path) - 1
" check cases of '/foo/bar/' and '/foo/bar'
if a:path[last_char] == go#util#PathSep()
let withSep = a:path
let noSep = strpart(a:path, 0, last_char)
else
let withSep = a:path . go#util#PathSep()
let noSep = a:path
endif
let hasA = index(go_paths, withSep) != -1
let hasB = index(go_paths, noSep) != -1
return hasA || hasB
endfunction
" Detect returns the current GOPATH. If a package manager is used, such as
" Godeps, GB, it will modify the GOPATH so those directories take precedence
" over the current GOPATH. It also detects diretories whose are outside
" GOPATH.
function! go#path#Detect() abort
let gopath = go#path#Default()
let current_dir = fnameescape(expand('%:p:h'))
" TODO(arslan): this should be changed so folders or files should be
" fetched from a customizable list. The user should define any new package
" management tool by it's own.
" src folders outside $GOPATH
let src_roots = finddir("src", current_dir .";", -1)
" for cases like GOPATH/src/foo/src/bar, pick up GOPATH/src instead of
" GOPATH/src/foo/src
let src_root = ""
if len(src_roots) > 0
let src_root = src_roots[-1]
endif
if !empty(src_root)
let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep()
" gb vendor plugin
" (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor)
let gb_vendor_root = src_path . "vendor" . go#util#PathSep()
if isdirectory(gb_vendor_root) && !s:HasPath(gb_vendor_root)
let gopath = gb_vendor_root . go#util#PathListSep() . gopath
endif
if !s:HasPath(src_path)
let gopath = src_path . go#util#PathListSep() . gopath
endif
endif
" Godeps
let godeps_root = finddir("Godeps", current_dir .";")
if !empty(godeps_root)
let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep())
if !s:HasPath(godeps_path)
let gopath = godeps_path . go#util#PathListSep() . gopath
endif
endif
" Fix up the case where initial $GOPATH is empty,
" and we end up with a trailing :
let gopath = substitute(gopath, ":$", "", "")
return gopath
endfunction
" BinPath returns the binary path of installed go tools.
function! go#path#BinPath() abort
let bin_path = go#config#BinPath()
if bin_path != ""
return bin_path
endif
" check if our global custom path is set, if not check if $GOBIN is set so
" we can use it, otherwise use default GOPATH
if $GOBIN != ""
let bin_path = $GOBIN
else
let go_paths = split(go#path#Default(), go#util#PathListSep())
if len(go_paths) == 0
return "" "nothing found
endif
let bin_path = expand(go_paths[0] . "/bin/")
endif
return bin_path
endfunction
" CheckBinPath checks whether the given binary exists or not and returns the
" path of the binary, respecting the go_bin_path and go_search_bin_path_first
" settings. It returns an empty string if the binary doesn't exist.
function! go#path#CheckBinPath(binpath) abort
" remove whitespaces if user applied something like 'goimports '
let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '')
" save original path
let old_path = $PATH
" check if we have an appropriate bin_path
let go_bin_path = go#path#BinPath()
if !empty(go_bin_path)
" append our GOBIN and GOPATH paths and be sure they can be found there...
" let us search in our GOBIN and GOPATH paths
" respect the ordering specified by go_search_bin_path_first
if go#config#SearchBinPathFirst()
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
else
let $PATH = $PATH . go#util#PathListSep() . go_bin_path
endif
endif
" if it's in PATH just return it
if executable(binpath)
if exists('*exepath')
let binpath = exepath(binpath)
endif
let $PATH = old_path
if go#util#IsUsingCygwinShell() == 1
return s:CygwinPath(binpath)
endif
return binpath
endif
" just get the basename
let basename = fnamemodify(binpath, ":t")
if !executable(basename)
call go#util#EchoError(printf("could not find '%s'. Run :GoInstallBinaries to fix it", basename))
" restore back!
let $PATH = old_path
return ""
endif
let $PATH = old_path
if go#util#IsUsingCygwinShell() == 1
return s:CygwinPath(a:binpath)
endif
return go_bin_path . go#util#PathSep() . basename
endfunction
function! s:CygwinPath(path)
return substitute(a:path, '\\', '/', "g")
endfunction
" go#path#ToURI converts path to a file URI. path should be an absolute path.
" 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 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:absolute ? 'file://' : '') . l:prefix . go#uri#EncodePath(l:path),
\ '\\',
\ '/',
\ 'g',
\)
endfunction
function! go#path#FromURI(uri) abort
let l:i = len('file://')
let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri
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 go#util#IsWin() && l:path =~# '^/[a-zA-Z]:'
let l:path = substitute(l:path[1:], '/', '\\', 'g')
endif
return l:path
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,76 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#play#Share(count, line1, line2) abort
if !executable('curl')
call go#util#EchoError('cannot share: curl cannot be found')
return
endif
let content = join(getline(a:line1, a:line2), "\n")
let share_file = tempname()
call writefile(split(content, "\n"), share_file, "b")
let l:cmd = ['curl', '-s', '-X', 'POST', 'https://play.golang.org/share',
\ '--data-binary', '@' . l:share_file]
let [l:snippet_id, l:err] = go#util#Exec(l:cmd)
" we can remove the temp file because it's now posted.
call delete(share_file)
if l:err != 0
call go#util#EchoError(['A error has occurred. Run this command to see what the problem is:', go#util#Shelljoin(l:cmd)])
return
endif
let url = "http://play.golang.org/p/".snippet_id
" copy to clipboard
if has('unix') && !has('xterm_clipboard') && !has('clipboard')
let @" = url
else
let @+ = url
endif
if go#config#PlayOpenBrowser()
call go#util#OpenBrowser(url)
endif
call go#util#EchoInfo('snippet uploaded: ' . url)
endfunction
function! s:get_visual_content() abort
let save_regcont = @"
let save_regtype = getregtype('"')
silent! normal! gvy
let content = @"
call setreg('"', save_regcont, save_regtype)
return content
endfunction
" modified version of
" http://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript
" another function that returns the content of visual selection, it's not used
" but might be useful in the future
function! s:get_visual_selection() abort
let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2]
" check if the the visual mode is used before
if lnum1 == 0 || lnum2 == 0 || col1 == 0 || col2 == 0
return
endif
let lines = getline(lnum1, lnum2)
let lines[-1] = lines[-1][: col2 - (&selection == 'inclusive' ? 1 : 2)]
let lines[0] = lines[0][col1 - 1:]
return join(lines, "\n")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,50 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
" New returns a promise. A promise's primary purpose is to make async jobs
" synchronous by awaiting fn.
"
" A promise is a dictionary with two keys:
" 'wrapper':
" A function that wraps fn. It can be used in place of fn.
" 'await':
" A function that waits for wrapper to be called and returns the value
" returned by fn. Returns default if timeout expires.
function! go#promise#New(fn, timeout, default) abort
let l:state = {}
" explicitly bind to state so that within l:promise's methods, self will
" always refer to state. See :help Partial for more information.
return {
\ 'wrapper': function('s:wrapper', [a:fn], l:state),
\ 'await': function('s:await', [a:timeout, a:default], l:state),
\ }
endfunction
function! s:wrapper(fn, ...) dict
let self.retval = call(a:fn, a:000)
return self.retval
endfunction
function! s:await(timeout, default) dict
let l:timer = timer_start(a:timeout, function('s:setretval', [a:default], self))
while !has_key(self, 'retval')
sleep 50m
endwhile
call timer_stop(l:timer)
return self.retval
endfunction
function! s:setretval(val, timer) dict
let self.retval = a:val
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,41 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_PromiseNew() abort
let l:sut = go#promise#New(function('s:work', []), 100, -1)
call assert_true(has_key(l:sut, 'wrapper'))
call assert_true(has_key(l:sut, 'await'))
endfunc
func! Test_PromiseAwait() abort
let l:expected = 1
let l:default = -1
let l:sut = go#promise#New(function('s:work', [l:expected]), 100, l:default)
call timer_start(10, l:sut.wrapper)
let l:actual = call(l:sut.await, [])
call assert_equal(l:expected, l:actual)
endfunc
func! Test_PromiseAwait_Timeout() abort
let l:desired = 1
let l:expected = -1
let l:sut = go#promise#New(function('s:work', [l:desired]), 10, l:expected)
call timer_start(100, l:sut.wrapper)
let l:actual = call(l:sut.await, [])
call assert_equal(l:expected, l:actual)
endfunc
func! s:work(val, timer)
return a:val
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,140 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#rename#Rename(bang, ...) abort
let to_identifier = ""
if a:0 == 0
let ask = printf("vim-go: rename '%s' to: ", expand("<cword>"))
let prefill = go#config#GorenamePrefill()
if prefill != ''
let to_identifier = input(ask, eval(prefill))
else
let to_identifier = input(ask)
endif
redraw!
if empty(to_identifier)
return
endif
else
let to_identifier = a:1
endif
let l:bin = go#config#GorenameCommand()
" return with a warning if the bin doesn't exist
let bin_path = go#path#CheckBinPath(l:bin)
if empty(bin_path)
return
endif
let fname = expand('%:p')
let pos = go#util#OffsetCursor()
let offset = printf('%s:#%d', fname, pos)
let args = []
if l:bin == 'gorename'
let l:args = extend(l:args, ['-tags', go#config#BuildTags(), '-offset', offset, '-to', to_identifier])
elseif l:bin == 'gopls'
" TODO(bc): use -tags when gopls supports it
let l:args = extend(l:args, ['rename', '-w', l:offset, to_identifier])
else
call go#util#EchoWarning('unexpected rename command')
endif
let l:cmd = extend([l:bin], l:args)
if go#util#has_job()
call s:rename_job({
\ 'cmd': cmd,
\ 'bang': a:bang,
\})
return
endif
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
call s:parse_errors(l:err, a:bang, split(l:out, '\n'))
endfunction
function s:rename_job(args)
let l:job_opts = {
\ 'bang': a:args.bang,
\ 'for': 'GoRename',
\ 'statustype': 'gorename',
\ }
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let l:cbs = go#job#Options(l:job_opts)
" wrap l:cbs.exit_cb in s:exit_cb.
let l:cbs.exit_cb = funcref('s:exit_cb', [l:cbs.exit_cb])
call go#job#Start(a:args.cmd, l:cbs)
endfunction
function! s:reload_changed() abort
" reload all files to reflect the new changes. We explicitly call
" checktime to trigger a reload of all files. See
" http://www.mail-archive.com/vim@vim.org/msg05900.html for more info
" about the autoread bug
let current_autoread = &autoread
set autoread
silent! checktime
let &autoread = current_autoread
endfunction
" s:exit_cb reloads any changed buffers and then calls next.
function! s:exit_cb(next, job, exitval) abort
call s:reload_changed()
call call(a:next, [a:job, a:exitval])
endfunction
function s:parse_errors(exit_val, bang, out)
" reload all files to reflect the new changes. We explicitly call
" checktime to trigger a reload of all files. See
" http://www.mail-archive.com/vim@vim.org/msg05900.html for more info
" about the autoread bug
let current_autoread = &autoread
set autoread
silent! checktime
let &autoread = current_autoread
let l:listtype = go#list#Type("GoRename")
if a:exit_val != 0
let errors = go#util#ParseErrors(a:out)
call go#list#Populate(l:listtype, errors, 'Rename')
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
elseif empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(a:out)
endif
return
endif
" strip out newline on the end that gorename puts. If we don't remove, it
" will trigger the 'Hit ENTER to continue' prompt
call go#list#Clean(l:listtype)
call go#util#EchoSuccess(a:out[0])
" refresh the buffer so we can see the new content
silent execute ":e"
endfunction
" Commandline completion: original, unexported camelCase, and exported
" CamelCase.
function! go#rename#Complete(lead, cmdline, cursor)
let l:word = expand('<cword>')
return filter(uniq(sort(
\ [l:word, go#util#camelcase(l:word), go#util#pascalcase(l:word)])),
\ 'strpart(v:val, 0, len(a:lead)) == a:lead')
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,124 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Statusline
""""""""""""""""""""""""""""""""
" s:statuses is a global reference to all statuses. It stores the statuses per
" import paths (map[string]status), where each status is unique per its
" type. Current status dict is in form:
" {
" 'desc' : 'Job description',
" 'state' : 'Job state, such as success, failure, etc..',
" 'type' : 'Job type, such as build, test, etc..'
" 'created_at' : 'Time it was created as seconds since 1st Jan 1970'
" }
let s:statuses = {}
" timer_id for cleaner
let s:timer_id = 0
" last_status stores the last generated text per status
let s:last_status = ""
" Show returns the current status of the job for 20 seconds (configurable). It
" displays it in form of 'desc: [type|state]' if there is any state available,
" if not it returns an empty string. This function should be plugged directly
" into the statusline.
function! go#statusline#Show() abort
" lazy initialization of the cleaner
if !s:timer_id
let interval = go#config#StatuslineDuration()
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
endif
" nothing to show
if empty(s:statuses)
return ''
endif
let status_dir = expand('%:p:h')
if !has_key(s:statuses, status_dir)
return ''
endif
let status = s:statuses[status_dir]
if !has_key(status, 'desc') || !has_key(status, 'state') || !has_key(status, 'type')
return ''
endif
let status_text = printf("[%s|%s]", status.type, status.state)
if empty(status_text)
return ''
endif
" only update highlight if status has changed.
if status_text != s:last_status
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass" || status.state =~ 'initialized'
hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22 guibg=#5fd700 guifg=#005f00
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling" || status.state =~ 'initializing'
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88 guibg=#ff8700 guifg=#870000
elseif status.state =~ "failed"
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52 guibg=#ff0000 guifg=#5f0000
endif
endif
let s:last_status = status_text
return status_text
endfunction
" Update updates (adds) the statusline for the given status_dir with the
" given status dict. It overrides any previously set status.
function! go#statusline#Update(status_dir, status) abort
let a:status.created_at = reltime()
let s:statuses[a:status_dir] = a:status
" force to update the statusline, otherwise the user needs to move the
" cursor
exe 'let &ro = &ro'
" before we stop the timer, check if we have any previous jobs to be cleaned
" up. Otherwise every job will reset the timer when this function is called
" and thus old jobs will never be cleaned
call s:clear()
" also reset the timer, so the user has time to see it in the statusline.
" Setting the timer_id to 0 will cause a new timer to be created the next
" time the go#statusline#Show() is called.
call timer_stop(s:timer_id)
let s:timer_id = 0
endfunction
" Clear clears all currently stored statusline data. The timer_id argument is
" just a placeholder so we can pass it to a timer_start() function if needed.
function! go#statusline#Clear(timer_id) abort
call s:clear()
endfunction
function! s:clear()
for [status_dir, status] in items(s:statuses)
let elapsed_time = reltimestr(reltime(status.created_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
if str2nr(elapsed_time) > 10
call remove(s:statuses, status_dir)
endif
endfor
if len(s:statuses) == 0
let s:statuses = {}
endif
" force to update the statusline, otherwise the user needs to move the
" cursor
exe 'let &ro = &ro'
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,219 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" mapped to :GoAddTags
function! go#tags#Add(start, end, count, ...) abort
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
let offset = 0
if a:count == -1
let offset = go#util#OffsetCursor()
endif
let test_mode = 0
call call("go#tags#run", [a:start, a:end, offset, "add", fname, test_mode] + a:000)
endfunction
" mapped to :GoRemoveTags
function! go#tags#Remove(start, end, count, ...) abort
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
let offset = 0
if a:count == -1
let offset = go#util#OffsetCursor()
endif
let test_mode = 0
call call("go#tags#run", [a:start, a:end, offset, "remove", fname, test_mode] + a:000)
endfunction
" run runs gomodifytag. This is an internal test so we can test it
function! go#tags#run(start, end, offset, mode, fname, test_mode, ...) abort
" do not split this into multiple lines, somehow tests fail in that case
let args = {'mode': a:mode,'start': a:start,'end': a:end,'offset': a:offset,'fname': a:fname,'cmd_args': a:000}
if &modified
let args["modified"] = 1
endif
let l:result = s:create_cmd(args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return -1
endif
if &modified
let filename = expand("%:p:gs!\\!/!")
let content = join(go#util#GetLines(), "\n")
let in = filename . "\n" . strlen(content) . "\n" . content
let [l:out, l:err] = go#util#Exec(l:result.cmd, in)
else
let [l:out, l:err] = go#util#Exec(l:result.cmd)
endif
if l:err != 0
call go#util#EchoError(out)
return
endif
if a:test_mode
exe 'edit ' . a:fname
endif
call s:write_out(out)
if a:test_mode
exe 'write! ' . a:fname
endif
endfunc
" write_out writes back the given output to the current buffer
func s:write_out(out) abort
" not a json output
if a:out[0] !=# '{'
return
endif
" nothing to do
if empty(a:out) || type(a:out) != type("")
return
endif
let result = json_decode(a:out)
if type(result) != type({})
call go#util#EchoError(printf("malformed output from gomodifytags: %s", a:out))
return
endif
let lines = result['lines']
let start_line = result['start']
let end_line = result['end']
let index = 0
for line in range(start_line, end_line)
call setline(line, lines[index])
let index += 1
endfor
if has_key(result, 'errors')
let l:winnr = winnr()
let l:listtype = go#list#Type("GoModifyTags")
call go#list#ParseFormat(l:listtype, "%f:%l:%c:%m", result['errors'], "gomodifytags")
call go#list#Window(l:listtype, len(result['errors']))
"prevent jumping to quickfix list
exe l:winnr . "wincmd w"
endif
endfunc
" create_cmd returns a dict that contains the command to execute gomodifytags
func s:create_cmd(args) abort
if !exists("*json_decode")
return {'err': "requires 'json_decode'. Update your Vim/Neovim version."}
endif
let bin_path = go#path#CheckBinPath('gomodifytags')
if empty(bin_path)
return {'err': "gomodifytags does not exist"}
endif
let l:start = a:args.start
let l:end = a:args.end
let l:offset = a:args.offset
let l:mode = a:args.mode
let l:cmd_args = a:args.cmd_args
let l:modifytags_transform = go#config#AddtagsTransform()
" start constructing the command
let cmd = [bin_path]
call extend(cmd, ["-format", "json"])
call extend(cmd, ["-file", a:args.fname])
call extend(cmd, ["-transform", l:modifytags_transform])
if has_key(a:args, "modified")
call add(cmd, "-modified")
endif
if l:offset != 0
call extend(cmd, ["-offset", l:offset])
else
let range = printf("%d,%d", l:start, l:end)
call extend(cmd, ["-line", range])
endif
if l:mode == "add"
let l:tags = []
let l:options = []
if !empty(l:cmd_args)
for item in l:cmd_args
let splitted = split(item, ",")
" tag only
if len(splitted) == 1
call add(l:tags, splitted[0])
endif
" options only
if len(splitted) == 2
call add(l:tags, splitted[0])
call add(l:options, printf("%s=%s", splitted[0], splitted[1]))
endif
endfor
endif
" 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, ",")])
endif
elseif l:mode == "remove"
if empty(l:cmd_args)
call add(cmd, "-clear-tags")
else
let l:tags = []
let l:options = []
for item in l:cmd_args
let splitted = split(item, ",")
" tag only
if len(splitted) == 1
call add(l:tags, splitted[0])
endif
" options only
if len(splitted) == 2
call add(l:options, printf("%s=%s", splitted[0], splitted[1]))
endif
endfor
" construct tags
if !empty(l:tags)
call extend(cmd, ["-remove-tags", join(l:tags, ",")])
endif
" construct options
if !empty(l:options)
call extend(cmd, ["-remove-options", join(l:options, ",")])
endif
endif
else
return {'err': printf("unknown mode: %s", l:mode)}
endif
return {'cmd': cmd}
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,52 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
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)
call gotest#assert_fixture('tags/add_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
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')
silent call go#tags#run(0, 0, 40, "remove", bufname(''), 1)
call gotest#assert_fixture('tags/remove_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim:ts=2:sts=2:sw=2:et

View File

@ -1,61 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:current_file = expand("<sfile>")
function! go#template#create() abort
let l:go_template_use_pkg = go#config#TemplateUsePkg()
let l:root_dir = fnamemodify(s:current_file, ':h:h:h')
let l:package_name = go#tool#PackageName()
" if we can't figure out any package name (i.e. no Go files in the directory)
" from the directory create the template or use the directory as the name.
if l:package_name == -1
if l:go_template_use_pkg == 1
let l:path = fnamemodify(expand('%:p:h'), ':t')
let l:content = printf("package %s", l:path)
call append(0, l:content)
else
let l:filename = expand('%:t')
if l:filename =~ "_test.go$"
let l:template_file = go#config#TemplateTestFile()
else
let l:template_file = go#config#TemplateFile()
endif
" If template_file is an absolute path, use it as-is. This is to support
" overrides pointing to templates outside of the vim-go plugin dir
if fnamemodify(l:template_file, ':p') != l:template_file
let l:template_file = go#util#Join(l:root_dir, "templates", l:template_file)
endif
silent exe 'keepalt 0r ' . fnameescape(l:template_file)
endif
else
let l:content = printf("package %s", l:package_name)
call append(0, l:content)
endif
" checking that the last line is empty shouldn't be necessary, but for some
" reason the last line isn't the expected empty line when run via tests.
if getline('$') is ''
$delete _
endif
endfunction
function! go#template#ToggleAutoCreate() abort
if go#config#TemplateAutocreate()
call go#config#SetTemplateAutocreate(0)
call go#util#EchoProgress("auto template create disabled")
return
end
call go#config#SetTemplateAutocreate(1)
call go#util#EchoProgress("auto template create enabled")
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,62 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_TemplateCreate() abort
try
let l:tmp = gotest#write_file('foo/empty.txt', [''])
edit foo/bar.go
call gotest#assert_buffer(1, [
\ 'func main() {',
\ '\tfmt.Println("vim-go")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
try
let l:tmp = gotest#write_file('foo/empty.txt', [''])
edit foo/bar_test.go
call gotest#assert_buffer(1, [
\ 'func TestHelloWorld(t *testing.T) {',
\ '\t// t.Fatal("not implemented")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_TemplateCreate_UsePkg() abort
try
let l:tmp = gotest#write_file('foo/empty.txt', [''])
let g:go_template_use_pkg = 1
edit foo/bar.go
call gotest#assert_buffer(0, ['package foo'])
finally
unlet g:go_template_use_pkg
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_TemplateCreate_PackageExists() abort
try
let l:tmp = gotest#write_file('quux/quux.go', ['package foo'])
edit quux/bar.go
call gotest#assert_buffer(0, ['package foo'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,177 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
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, errorformat) abort
return go#term#newmode(a:bang, a:cmd, a:errorformat, go#config#TermMode())
endfunction
" 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 l:state = {
\ 'cmd': a:cmd,
\ 'bang' : a:bang,
\ 'winid': win_getid(winnr()),
\ 'stdout': [],
\ 'stdout_buf': '',
\ 'errorformat': a:errorformat,
\ }
" execute go build in the files directory
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
execute l:cd . fnameescape(expand("%:p:h"))
execute l:mode . ' __go_term__'
setlocal filetype=goterm
setlocal bufhidden=delete
setlocal winfixheight
setlocal noswapfile
setlocal nobuflisted
" explicitly bind callbacks to state so that within them, self will always
" refer to state. See :help Partial for more information.
"
" 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 l:job = {
\ 'on_stdout': function('s:on_stdout', [], state),
\ 'on_exit' : function('s:on_exit', [], state),
\ }
let l:state.id = termopen(a:cmd, l:job)
let l:state.termwinid = win_getid(winnr())
execute l:cd . fnameescape(l:dir)
" resize new term if needed.
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 l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
exe 'vertical resize ' . l:width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . l:height
endif
" we also need to resize the pty, so there you go...
call jobresize(l:state.id, l:width, l:height)
call win_gotoid(l:state.winid)
return l:state.id
endfunction
function! s:on_stdout(job_id, data, event) dict abort
" 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)
" change to directory where test were run. if we do not do this
" the quickfix items will have the incorrect paths.
" see: https://github.com/fatih/vim-go/issues/2400
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
execute l:cd . fnameescape(expand("%:p:h"))
let l:listtype = go#list#Type("_term")
if a:exit_status == 0
call go#list#Clean(l:listtype)
execute l:cd l:dir
call win_gotoid(l:winid)
return
endif
call win_gotoid(self.winid)
let l:title = self.cmd
if type(l:title) == v:t_list
let l:title = join(self.cmd)
endif
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
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")
execute l:cd l:dir
call win_gotoid(l:winid)
return
endif
" close terminal; we don't need it anymore
if go#config#TermCloseOnExit()
call win_gotoid(self.termwinid)
close!
endif
if self.bang
execute l:cd l:dir
call win_gotoid(l:winid)
return
endif
call win_gotoid(self.winid)
call go#list#JumpToFirst(l:listtype)
" change back to original working directory
execute l:cd l:dir
endfunction
function! go#term#ToggleCloseOnExit() abort
if go#config#TermCloseOnExit()
call go#config#SetTermCloseOnExit(0)
call go#util#EchoProgress("term close on exit disabled")
return
endif
call go#config#SetTermCloseOnExit(1)
call go#util#EchoProgress("term close on exit enabled")
return
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,60 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_GoTermNewMode()
if !has('nvim')
return
endif
try
let l:filename = 'term/term.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/term'
let expected = expand('%:p')
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set nosplitright
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)
finally
sleep 50m
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_GoTermNewMode_SplitRight()
if !has('nvim')
return
endif
try
let l:filename = 'term/term.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/term'
let expected = expand('%:p')
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set splitright
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)
finally
sleep 50m
call delete(l:tmp, 'rf')
set nosplitright
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,5 +0,0 @@
package main
func main() {
notafunc()
}

View File

@ -1,9 +0,0 @@
package complete
type T struct {
V string
}
func Example(s string) {
Example("")
}

View File

@ -1,5 +0,0 @@
package config
func Example() {
foo()
}

View File

@ -1,8 +0,0 @@
// +build constrained
package config
// foo is constrained and this comment exists to make the line numbers different than foo.go
func foo() {
println("foo")
}

View File

@ -1,7 +0,0 @@
// +build !constrained
package config
func foo() {
println("foo")
}

View File

@ -1,3 +0,0 @@
module config
go 1.13

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +0,0 @@
package main
import (
"fmt"
)
func Foo(log *logging.TestLogger) {
log.Debug("vim-go")
}
func main() {
fmt.Println("vim-go")
}

View File

@ -1,15 +0,0 @@
package main
import (
"fmt"
logging "gh.com/gi/foo-logging"
)
func Foo(log *logging.TestLogger) {
log.Debug("vim-go")
}
func main() {
fmt.Println("vim-go")
}

View File

@ -1,12 +0,0 @@
package logging
import "fmt"
type TestLogger struct {
Value string
}
func (l *TestLogger) Debug(msg string) {
fmt.Println(msg)
fmt.Println(l.Value)
}

View File

@ -1,6 +0,0 @@
package main
func main() {
notafunc()
println("vim-go")
}

View File

@ -1,7 +0,0 @@
package foo
import "fmt"
func MissingFooDoc() {
fmt.Println("missing doc")
}

View File

@ -1,7 +0,0 @@
package lint
import "fmt"
func MissingDoc() {
fmt.Println("missing doc")
}

View File

@ -1,7 +0,0 @@
package lint
import "fmt"
func AlsoMissingDoc() {
fmt.Println("missing doc")
}

View File

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

View File

@ -1,8 +0,0 @@
package main
import "fmt"
func main() {
str := "hello world!"
fmt.Printf("%d\n", str)
}

View File

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

View File

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

View File

@ -1,5 +0,0 @@
package main
func main() {
println("hello, world")
}

View File

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

View File

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

View File

@ -1,7 +0,0 @@
package mock
import "testing"
func Fail(t *testing.T) {
t.Fatal("another package badness")
}

View File

@ -1,59 +0,0 @@
package play
import (
"sync"
"testing"
"play/mock"
)
func TestTopSubHelper(t *testing.T) {
t.Run("sub", func(t *testing.T) {
t.Log("log message")
t.Error("sub badness")
})
t.Error("badness")
helper(t)
}
func TestMultiline(t *testing.T) {
t.Error("this is an error\nand a second line, too")
t.Error("\nthis is another error")
}
func TestSub(t *testing.T) {
t.Run("indented", func(t *testing.T) {
t.Error("this is a sub-test error\nand a second line, too")
})
}
func TestOK(t *testing.T) {
t.Run("log", func(t *testing.T) {
t.Log("goodness")
})
}
// TestMocked tests behavior similar to what users may experience when using
// github.com/golang/mock/gomock.
func TestMocked(t *testing.T) {
mock.Fail(t)
}
func TestPanic(t *testing.T) {
panic("worst ever")
}
func TestConcurrentPanic(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
panic("concurrent fail")
wg.Done()
}()
wg.Wait()
}
func helper(t *testing.T) {
t.Helper()
t.Fatal("helper badness")
}

View File

@ -1,11 +0,0 @@
package main
import "testing"
func TestHelloWorld(t *testing.T) {
t.Error("so long")
t.Run("sub", func(t *testing.T) {
t.Error("thanks for all the fish")
})
}

View File

@ -1,11 +0,0 @@
package main
import (
"io/ioutil"
"testing"
)
func TestSomething(t *testing.T) {
r := struct{}{}
ioutil.ReadAll(r)
}

View File

@ -1,47 +0,0 @@
// Run a few parallel tests, all in parallel, using multiple techniques for
// causing the test to take a while so that the stacktraces resulting from a
// test timeout will contain several goroutines to avoid giving a false sense
// of confidence or creating error formats that don't account for the more
// complex scenarios that can occur with timeouts.
package main
import (
"testing"
"time"
)
func TestSleep(t *testing.T) {
t.Parallel()
time.Sleep(15 * time.Second)
t.Log("expected panic if run with timeout < 15s")
}
func TestRunning(t *testing.T) {
t.Parallel()
c := time.After(15 * time.Second)
Loop:
for {
select {
case <-c:
break Loop
default:
}
}
t.Log("expected panic if run with timeout < 15s")
}
func TestRunningAlso(t *testing.T) {
t.Parallel()
c := time.After(15 * time.Second)
Loop:
for {
select {
case <-c:
break Loop
default:
}
}
t.Log("expected panic if run with timeout < 15s")
}

View File

@ -1,7 +0,0 @@
package main
import "fmt"
func main() {
fmt.Errorf("%v")
}

View File

@ -1,329 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" Test runs `go test` in the current directory. If compile is true, it'll
" compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appended to the final `go test` command.
function! go#test#Test(bang, compile, ...) abort
let args = ["test", '-tags', go#config#BuildTags()]
" don't run the test, only compile it. Useful to capture and fix errors.
if a:compile
let testfile = tempname() . ".vim-go.test"
call extend(args, ["-c", "-o", testfile])
endif
if a:0
let goargs = a:000
" do not expand for coverage mode as we're passing the arg ourself
if a:1 != '-coverprofile'
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
endif
call extend(args, goargs, 1)
else
" only add this if no custom flags are passed
let timeout = go#config#TestTimeout()
call add(args, printf("-timeout=%s", timeout))
endif
if go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args, s:errorformat())
return
endif
if go#util#has_job()
" use vim's job functionality to call it asynchronously
let job_options = {
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ 'statustype': 'test',
\ 'errorformat': s:errorformat(),
\ }
if a:compile
let job_options.statustype = 'compile ' . job_options.statustype
endif
call s:test_job(['go'] + args, job_options)
return
endif
if go#config#EchoCommandInfo()
if a:compile
call go#util#EchoProgress("compiling tests ...")
else
call go#util#EchoProgress("testing...")
endif
endif
call go#cmd#autowrite()
redraw
let l:cmd = ['go'] + l:args
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
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)
" 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)
if a:compile
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
endif
endif
execute cd . fnameescape(dir)
endfunction
" Testfunc runs a single test that surrounds the current cursor position.
" Arguments are passed to the `go test` command.
function! go#test#Func(bang, ...) abort
" search flags legend (used only)
" 'b' search backward instead of forward
" 'c' accept a match at the cursor position
" 'n' do Not move the cursor
" 'W' don't wrap around the end of the file
"
" for the full list
" :help search
let test = search('func \(Test\|Example\)', "bcnW")
if test == 0
echo "vim-go: [test] no test found immediate to cursor"
return
end
let line = getline(test)
let name = split(split(line, " ")[1], "(")[0]
let args = [a:bang, 0, "-run", name . "$"]
if a:0
call extend(args, a:000)
else
" only add this if no custom flags are passed
let timeout = go#config#TestTimeout()
call add(args, printf("-timeout=%s", timeout))
endif
call call('go#test#Test', args)
endfunction
function! s:test_job(cmd, args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()
call go#job#Spawn(a:cmd, a:args)
endfunction
let s:efm = ""
let s:go_test_show_name = 0
function! s:errorformat() abort
" NOTE(arslan): once we get JSON output everything will be easier :).
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
" https://github.com/golang/go/issues/2981.
let goroot = go#util#goroot()
let show_name = go#config#TestShowName()
if s:efm != "" && s:go_test_show_name == show_name
return s:efm
endif
let s:go_test_show_name = show_name
" each level of test indents the test output 4 spaces. Capturing groups
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
" (e.g. \%(\)).
let indent = '%\\%( %\\)'
" ignore `go test -v` output for starting tests
let format = "%-G=== RUN %.%#"
" ignore `go test -v` output for passing tests
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, followed by the duration of the test in parentheses.
"
" e.g.:
" '--- FAIL: TestSomething (0.00s)'
if show_name
let format .= ",%G" . indent . "%#--- FAIL: %m (%.%#)"
else
let format .= ",%-G" . indent . "%#--- FAIL: %.%#"
endif
" Go 1.10 test output {{{1
" Matches test output lines.
"
" All test output lines start with the test indentation and a tab, followed
" by the filename, a colon, the line number, another colon, a space, and the
" message. e.g.:
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: %m"
" also match lines that don't have a message (i.e. the message begins with a
" newline or is the empty string):
" e.g.:
" t.Errorf("\ngot %v; want %v", actual, expected)
" t.Error("")
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: "
" Match the 2nd and later lines of multi-line output. These lines are
" indented the number of spaces for the level of nesting of the test,
" followed by two tabs, followed by the message.
"
" Treat these lines as if they are stand-alone lines of output by using %G.
" It would also be valid to treat these lines as if they were the
" continuation of a multi-line error by using %C instead of %G, but that
" would also require that all test errors using a %A or %E modifier to
" indicate that they're multiple lines of output, but in that case the lines
" get concatenated in the quickfix list, which is not what users typically
" want when writing a newline into their test output.
let format .= ",%G" . indent . "%#%\\t%\\{2}%m"
" }}}1
" Go 1.11 test output {{{1
" Match test output lines similarly to Go 1.10 test output lines, but they
" use an indent level where the Go 1.10 test output uses tabs, so they'll
" always have at least one level indentation...
let format .= ",%A" . indent . "%\\+%f:%l: %m"
let format .= ",%A" . indent . "%\\+%f:%l: "
let format .= ",%G" . indent . "%\\{2\\,}%m"
" }}}1
" set the format for panics.
" handle panics from test timeouts
let format .= ",%+Gpanic: test timed out after %.%\\+"
" handle non-timeout panics
" In addition to 'panic', check for 'fatal error' to support older versions
" of Go that used 'fatal error'.
"
" Panics come in two flavors. When the goroutine running the tests panics,
" `go test` recovers and tries to exit more cleanly. In that case, the panic
" message is suffixed with ' [recovered]'. If the panic occurs in a
" different goroutine, it will not be suffixed with ' [recovered]'.
let format .= ",%+Afatal error: %.%# [recovered]"
let format .= ",%+Apanic: %.%# [recovered]"
let format .= ",%+Afatal error: %.%#"
let format .= ",%+Apanic: %.%#"
" Match address lines in stacktraces produced by panic.
"
" Address lines in the stack trace have leading tabs, followed by the path
" to the file. The file path is followed by a colon and then the line number
" within the file where the panic occurred. After that there's a space and
" hexadecimal number.
"
" e.g.:
" '\t/usr/local/go/src/time.go:1313 +0x5d'
" panicaddress and readyaddress are identical except for
" panicaddress sets the filename and line number.
let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" stdlib address is identical to readyaddress, except it matches files
" inside GOROOT.
let stdlibaddress = "%\\t" . goroot . "%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" Match and ignore the running goroutine line.
let format .= ",%-Cgoroutine %\\d%\\+ [running]:"
" Match address lines that refer to stdlib, but consider them informational
" only. This is to catch the lines after the first address line in the
" running goroutine of a panic stack trace. Ideally, this wouldn't be
" necessary, but when a panic happens in the goroutine running a test, it's
" recovered and another panic is created, so the stack trace actually has
" the line that caused the original panic a couple of addresses down the
" stack.
let format .= ",%-C" . stdlibaddress
" Match address lines in the first matching goroutine. This means the panic
" message will only be shown as the error message in the first address of
" the running goroutine's stack.
let format .= ",%Z" . panicaddress
" Match and ignore errors from runtime.goparkunlock(). These started
" appearing in stack traces from Go 1.12 test timeouts.
let format .= ",%-Gruntime.goparkunlock(%.%#"
let format .= ",%-G%\\t" . goroot . "%\\f%\\+:%\\d%\\+"
" Match and ignore panic address without being part of a multi-line message.
" This is to catch those lines that come after the top most non-standard
" library line in stack traces.
let format .= ",%-G" . readyaddress
" Match and ignore exit status lines (produced when go test panics) whether
" part of a multi-line message or not, because these lines sometimes come
" before and sometimes after panic stacktraces.
let format .= ",%-Cexit status %[0-9]%\\+"
"let format .= ",exit status %[0-9]%\\+"
" Match and ignore failure lines whether part of a multi-line message
" or not, because these lines sometimes come before and sometimes after
" panic stacktraces.
let format .= ",%-CFAIL%\\t%.%#"
"let format .= ",FAIL%\\t%.%#"
" match compiler errors.
" These are very smilar to errors from <=go1.10 test output, but lack
" leading tabs for the first line of an error, and subsequent lines only
" have one tab instead of two.
let format .= ",%A%f:%l:%c: %m"
let format .= ",%A%f:%l: %m"
" It would be nice if this weren't necessary, but panic lines from tests are
" prefixed with a single leading tab, making them very similar to 2nd and
" later lines of a multi-line compiler error. Swallow it so that it doesn't
" cause a quickfix entry since the next %G entry can add a quickfix entry
" for 2nd and later lines of a multi-line compiler error.
let format .= ",%-C%\\tpanic: %.%#"
let format .= ",%G%\\t%m"
" Match and ignore everything else in multi-line messages.
let format .= ",%-C%.%#"
" Match and ignore everything else not in a multi-line message:
let format .= ",%-G%.%#"
let s:efm = format
return s:efm
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,143 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_GoTest() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected)
endfunc
func! Test_GoTestConcurrentPanic()
let expected = [
\ {'lnum': 50, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'}
\ ]
call s:test('play/play_test.go', expected, "-run", "TestConcurrentPanic")
endfunc
func! Test_GoTestVerbose() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 32, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected, "-v")
endfunc
func! Test_GoTestCompilerError() abort
let expected = [
\ {'lnum': 6, 'bufnr': 6, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'syntax error: unexpected newline, expecting comma or )'}
\ ]
call s:test('compilerror/compilerror_test.go', expected)
endfunc
func! Test_GoTestTimeout() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: test timed out after 500ms'}
\ ]
let g:go_test_timeout="500ms"
call s:test('timeout/timeout_test.go', expected)
unlet g:go_test_timeout
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': 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': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ ]
let g:go_test_show_name=1
call s:test('showname/showname_test.go', expected)
unlet g:go_test_show_name
endfunc
func! Test_GoTestVet() abort
let expected = [
\ {'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': 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
" clear the quickfix lists
call setqflist([], 'r')
let args = [1,0]
if a:0
let args += a:000
endif
" run the tests
silent call call(function('go#test#Test'), args)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
for item in actual
let item.text = s:normalize_durations(item.text)
endfor
for item in a:expected
let item.text = s:normalize_durations(item.text)
endfor
call gotest#assert_quickfix(actual, a:expected)
endfunc
func! s:normalize_durations(str) abort
return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g')
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,284 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" ( ) motions
" { } motions
" s for sentence
" p for paragraph
" < >
" t for tag
function! go#textobj#Comment(mode) abort
let l:fname = expand('%:p')
try
if &modified
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let l:fname = l:tmpname
endif
let l:cmd = ['motion',
\ '-format', 'json',
\ '-file', l:fname,
\ '-offset', go#util#OffsetCursor(),
\ '-mode', 'comment',
\ ]
let [l:out, l:err] = go#util#Exec(l:cmd)
if l:err
call go#util#EchoError(l:out)
return
endif
finally
if exists("l:tmpname")
call delete(l:tmpname)
endif
endtry
let l:result = json_decode(l:out)
if type(l:result) != 4 || !has_key(l:result, 'comment')
return
endif
let l:info = l:result.comment
call cursor(l:info.startLine, l:info.startCol)
" Adjust cursor to exclude start comment markers. Try to be a little bit
" clever when using multi-line '/*' markers.
if a:mode is# 'i'
" trim whitespace so matching below works correctly
let l:line = substitute(getline('.'), '^\s*\(.\{-}\)\s*$', '\1', '')
" //text
if l:line[:2] is# '// '
call cursor(l:info.startLine, l:info.startCol+3)
" // text
elseif l:line[:1] is# '//'
call cursor(l:info.startLine, l:info.startCol+2)
" /*
" text
elseif l:line =~# '^/\* *$'
call cursor(l:info.startLine+1, 0)
" /*
" * text
if getline('.')[:2] is# ' * '
call cursor(l:info.startLine+1, 4)
" /*
" *text
elseif getline('.')[:1] is# ' *'
call cursor(l:info.startLine+1, 3)
endif
" /* text
elseif l:line[:2] is# '/* '
call cursor(l:info.startLine, l:info.startCol+3)
" /*text
elseif l:line[:1] is# '/*'
call cursor(l:info.startLine, l:info.startCol+2)
endif
endif
normal! v
" Exclude trailing newline.
if a:mode is# 'i'
let l:info.endCol -= 1
endif
call cursor(l:info.endLine, l:info.endCol)
" Exclude trailing '*/'.
if a:mode is# 'i'
let l:line = getline('.')
" text
" */
if l:line =~# '^ *\*/$'
call cursor(l:info.endLine - 1, len(getline(l:info.endLine - 1)))
" text */
elseif l:line[-3:] is# ' */'
call cursor(l:info.endLine, l:info.endCol - 3)
" text*/
elseif l:line[-2:] is# '*/'
call cursor(l:info.endLine, l:info.endCol - 2)
endif
endif
endfunction
" Select a function in visual mode.
function! go#textobj#Function(mode) abort
let l:fname = expand("%:p")
if &modified
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let l:fname = l:tmpname
endif
let l:cmd = ['motion',
\ '-format', 'vim',
\ '-file', l:fname,
\ '-offset', go#util#OffsetCursor(),
\ '-mode', 'enclosing',
\ ]
if go#config#TextobjIncludeFunctionDoc()
let l:cmd += ['-parse-comments']
endif
let [l:out, l:err] = go#util#Exec(l:cmd)
if l:err
call go#util#EchoError(out)
return
endif
" if exists, delete it as we don't need it anymore
if exists("l:tmpname")
call delete(l:tmpname)
endif
" convert our string dict representation into native Vim dictionary type
let result = eval(out)
if type(result) != 4 || !has_key(result, 'fn')
return
endif
let info = result.fn
if a:mode == 'a'
" anonymous functions doesn't have associated doc. Also check if the user
" want's to include doc comments for function declarations
if has_key(info, 'doc') && go#config#TextobjIncludeFunctionDoc()
call cursor(info.doc.line, info.doc.col)
elseif info['sig']['name'] == '' && go#config#TextobjIncludeVariable()
" one liner anonymous functions
if info.lbrace.line == info.rbrace.line
" jump to first nonblack char, to get the correct column
call cursor(info.lbrace.line, 0 )
normal! ^
call cursor(info.func.line, col("."))
else
call cursor(info.func.line, info.rbrace.col)
endif
else
call cursor(info.func.line, info.func.col)
endif
normal! v
call cursor(info.rbrace.line, info.rbrace.col)
return
endif
" rest is inner mode, a:mode == 'i'
" if the function is a one liner we need to select only that portion
if info.lbrace.line == info.rbrace.line
call cursor(info.lbrace.line, info.lbrace.col+1)
normal! v
call cursor(info.rbrace.line, info.rbrace.col-1)
return
endif
call cursor(info.lbrace.line+1, 1)
normal! V
call cursor(info.rbrace.line-1, 1)
endfunction
" Get the location of the previous or next function.
function! go#textobj#FunctionLocation(direction, cnt) abort
let l:fname = expand("%:p")
if &modified
" Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let l:fname = l:tmpname
endif
let l:cmd = ['motion',
\ '-format', 'vim',
\ '-file', l:fname,
\ '-offset', go#util#OffsetCursor(),
\ '-shift', a:cnt,
\ '-mode', a:direction,
\ ]
if go#config#TextobjIncludeFunctionDoc()
let l:cmd += ['-parse-comments']
endif
let [l:out, l:err] = go#util#Exec(l:cmd)
if l:err
call go#util#EchoError(out)
return
endif
" if exists, delete it as we don't need it anymore
if exists("l:tmpname")
call delete(l:tmpname)
endif
let l:result = json_decode(out)
if type(l:result) != 4 || !has_key(l:result, 'fn')
return 0
endif
return l:result
endfunction
function! go#textobj#FunctionJump(mode, direction) abort
" get count of the motion. This should be done before all the normal
" expressions below as those reset this value(because they have zero
" count!). We abstract -1 because the index starts from 0 in motion.
let l:cnt = v:count1 - 1
" set context mark so we can jump back with '' or ``
normal! m'
" select already previously selected visual content and continue from there.
" If it's the first time starts with the visual mode. This is needed so
" after selecting something in visual mode, every consecutive motion
" continues.
if a:mode == 'v'
normal! gv
endif
let l:result = go#textobj#FunctionLocation(a:direction, l:cnt)
if l:result is 0
return
endif
" we reached the end and there are no functions. The usual [[ or ]] jumps to
" the top or bottom, we'll do the same.
if type(result) == 4 && has_key(result, 'err') && result.err == "no functions found"
if a:direction == 'next'
keepjumps normal! G
else " 'prev'
keepjumps normal! gg
endif
return
endif
let info = result.fn
" if we select something ,select all function
if a:mode == 'v' && a:direction == 'next'
keepjumps call cursor(info.rbrace.line, 1)
return
endif
if a:mode == 'v' && a:direction == 'prev'
if has_key(info, 'doc') && go#config#TextobjIncludeFunctionDoc()
keepjumps call cursor(info.doc.line, 1)
else
keepjumps call cursor(info.func.line, 1)
endif
return
endif
keepjumps call cursor(info.func.line, 1)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,147 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" From "go list -h".
function! go#tool#ValidFiles(...)
let l:list = ["GoFiles", "CgoFiles", "IgnoredGoFiles", "CFiles", "CXXFiles",
\ "MFiles", "HFiles", "FFiles", "SFiles", "SwigFiles", "SwigCXXFiles",
\ "SysoFiles", "TestGoFiles", "XTestGoFiles"]
" Used as completion
if len(a:000) > 0
let l:list = filter(l:list, 'strpart(v:val, 0, len(a:1)) == a:1')
endif
return l:list
endfunction
function! go#tool#Files(...) abort
if len(a:000) > 0
let source_files = a:000
else
let source_files = ['GoFiles']
endif
let combined = ''
for sf in source_files
" Strip dot in case people used ":GoFiles .GoFiles".
let sf = substitute(sf, '^\.', '', '')
" Make sure the passed options are valid.
if index(go#tool#ValidFiles(), sf) == -1
echoerr "unknown source file variable: " . sf
endif
if go#util#IsWin()
let combined .= '{{range $f := .' . sf . '}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}'
else
let combined .= "{{range $f := ." . sf . "}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}"
endif
endfor
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:combined])
return split(l:out, '\n')
endfunction
function! go#tool#Deps() abort
if go#util#IsWin()
let format = '{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}'
else
let format = "{{range $f := .Deps}}{{$f}}\n{{end}}"
endif
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
return split(l:out, '\n')
endfunction
function! go#tool#Imports() abort
let imports = {}
if go#util#IsWin()
let format = '{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}'
else
let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}"
endif
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
if l:err != 0
echo out
return imports
endif
for package_path in split(out, '\n')
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}', l:package_path])
if l:err != 0
echo out
return imports
endif
let package_name = substitute(l:out, '\n$', '', '')
let imports[package_name] = package_path
endfor
return imports
endfunction
function! go#tool#Info(showstatus) abort
let l:mode = go#config#InfoMode()
if l:mode == 'gocode'
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, gopls]')
endif
endfunction
function! go#tool#PackageName() abort
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}'])
if l:err != 0
return -1
endif
return split(out, '\n')[0]
endfunction
" Exists checks whether the given importpath exists or not. It returns 0 if
" the importpath exists under GOPATH.
function! go#tool#Exists(importpath) abort
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', a:importpath])
if l:err != 0
return -1
endif
return 0
endfunction
function! go#tool#DescribeBalloon()
let l:fname = fnamemodify(bufname(v:beval_bufnr), ':p')
let l:winid = win_getid()
call win_gotoid(bufwinid(v:beval_bufnr))
let [l:line, l:col] = go#lsp#lsp#Position(v:beval_lnum, v:beval_col)
call go#lsp#Hover(l:fname, l:line, l:col, funcref('s:balloon', []))
call win_gotoid(l:winid)
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
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,31 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_ExecuteInDir() abort
let l:tmp = gotest#write_file('a/a.go', ['package a'])
try
let l:out = go#util#ExecInDir(['pwd'])
call assert_equal([l:tmp . "/src/a\n", 0], l:out)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_ExecuteInDir_nodir() abort
let l:tmp = go#util#tempdir("executeindir")
exe ':e ' . l:tmp . '/new-dir/a'
try
let l:out = go#util#ExecInDir(['pwd'])
call assert_equal(['', 1], l:out)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,122 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
let s:buf_nr = -1
"OpenWindow opens a new scratch window and put's the content into the window
function! go#ui#OpenWindow(title, content, filetype) abort
" Ensure there's only one return window in this session/tabpage
call go#util#Windo("unlet! w:vim_go_return_window")
" Mark the window we're leaving as such
let w:vim_go_return_window = 1
" reuse existing buffer window if it exists otherwise create a new one
if !bufexists(s:buf_nr)
execute 'botright new'
file `="[" . a:title . "]"`
let s:buf_nr = bufnr('%')
elseif bufwinnr(s:buf_nr) == -1
execute 'botright new'
execute s:buf_nr . 'buffer'
elseif bufwinnr(s:buf_nr) != bufwinnr('%')
execute bufwinnr(s:buf_nr) . 'wincmd w'
endif
" Resize window to content length
exe 'resize' . len(a:content)
execute "setlocal filetype=".a:filetype
" some sane default values for a readonly buffer
setlocal bufhidden=delete
setlocal buftype=nofile
setlocal noswapfile
setlocal nobuflisted
setlocal winfixheight
setlocal cursorline " make it easy to distinguish
setlocal nonumber
setlocal norelativenumber
setlocal showbreak=""
" we need this to purge the buffer content
setlocal modifiable
"delete everything first from the buffer
%delete _
" add the content
call append(0, a:content)
" delete last line that comes from the append call
$delete _
" set it back to non modifiable
setlocal nomodifiable
" Remove the '... [New File]' message line from the command line
echon
endfunction
function! go#ui#GetReturnWindow() abort
for l:wn in range(1, winnr("$"))
if !empty(getwinvar(l:wn, "vim_go_return_window"))
return l:wn
endif
endfor
endfunction
" CloseWindow closes the current window
function! go#ui#CloseWindow() abort
" Close any window associated with the ui buffer, if it's there
if bufexists(s:buf_nr)
let ui_window_number = bufwinnr(s:buf_nr)
if ui_window_number != -1
execute ui_window_number . 'close'
endif
endif
"return to original window, if it's there
let l:rw = go#ui#GetReturnWindow()
if !empty(l:rw)
execute l:rw . 'wincmd w'
unlet! w:vim_go_return_window
endif
endfunction
" OpenDefinition parses the current line and jumps to it by openening a new
" tab
function! go#ui#OpenDefinition(filter) abort
let curline = getline('.')
" don't touch our first line or any blank line
if curline =~ a:filter || curline =~ "^$"
" suppress information about calling this function
echo ""
return
endif
" format: 'interface file:lnum:coln'
let mx = '^\(^\S*\)\s*\(.\{-}\):\(\d\+\):\(\d\+\)'
" parse it now into the list
let tokens = matchlist(curline, mx)
" convert to: 'file:lnum:coln'
let expr = tokens[2] . ":" . tokens[3] . ":" . tokens[4]
" jump to it in a new tab, we use explicit lgetexpr so we can later change
" the behaviour via settings (like opening in vsplit instead of tab)
lgetexpr expr
tab split
ll 1
" center the word
norm! zz
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,38 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#uri#Encode(value) abort
return s:encode(a:value, '[^A-Za-z0-9_.~-]')
endfunction
function! go#uri#EncodePath(value) abort
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)
return substitute(
\ a:value,
\ a:unreserved,
\ '\="%".printf(''%02X'', char2nr(submatch(0)))',
\ 'g'
\)
endfunction
function! go#uri#Decode(value) abort
return substitute(
\ a:value,
\ '%\(\x\x\)',
\ '\=nr2char(''0X'' . submatch(1))',
\ 'g'
\)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View File

@ -1,596 +0,0 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
" PathSep returns the appropriate OS specific path separator.
function! go#util#PathSep() abort
if go#util#IsWin()
return '\'
endif
return '/'
endfunction
" PathListSep returns the appropriate OS specific path list separator.
function! go#util#PathListSep() abort
if go#util#IsWin()
return ";"
endif
return ":"
endfunction
" LineEnding returns the correct line ending, based on the current fileformat
function! go#util#LineEnding() abort
if &fileformat == 'dos'
return "\r\n"
elseif &fileformat == 'mac'
return "\r"
endif
return "\n"
endfunction
" Join joins any number of path elements into a single path, adding a
" Separator if necessary and returns the result
function! go#util#Join(...) abort
return join(a:000, go#util#PathSep())
endfunction
" IsWin returns 1 if current OS is Windows or 0 otherwise
" Note that has('win32') is always 1 when has('win64') is 1, so has('win32') is enough.
function! go#util#IsWin() abort
return has('win32')
endfunction
" IsMac returns 1 if current OS is macOS or 0 otherwise.
function! go#util#IsMac() abort
return has('mac') ||
\ has('macunix') ||
\ has('gui_macvim') ||
\ go#util#Exec(['uname'])[0] =~? '^darwin'
endfunction
" Checks if using:
" 1) Windows system,
" 2) And has cygpath executable,
" 3) And uses *sh* as 'shell'
function! go#util#IsUsingCygwinShell()
return go#util#IsWin() && executable('cygpath') && &shell =~ '.*sh.*'
endfunction
" Check if Vim jobs API is supported.
"
" The (optional) first parameter can be added to indicate the 'cwd' or 'env'
" parameters will be used, which wasn't added until a later version.
function! go#util#has_job(...) abort
return has('job') || has('nvim')
endfunction
let s:env_cache = {}
" env returns the go environment variable for the given key. Where key can be
" GOARCH, GOOS, GOROOT, etc... It caches the result and returns the cached
" version.
function! go#util#env(key) abort
let l:key = tolower(a:key)
if has_key(s:env_cache, l:key)
return s:env_cache[l:key]
endif
if executable('go')
let l:var = call('go#util#'.l:key, [])
if go#util#ShellError() != 0
call go#util#EchoError(printf("'go env %s' failed", toupper(l:key)))
return ''
endif
else
let l:var = eval("$".toupper(a:key))
endif
let s:env_cache[l:key] = l:var
return l:var
endfunction
" goarch returns 'go env GOARCH'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goarch")'
function! go#util#goarch() abort
return substitute(s:exec(['go', 'env', 'GOARCH'])[0], '\n', '', 'g')
endfunction
" goos returns 'go env GOOS'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goos")'
function! go#util#goos() abort
return substitute(s:exec(['go', 'env', 'GOOS'])[0], '\n', '', 'g')
endfunction
" goroot returns 'go env GOROOT'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goroot")'
function! go#util#goroot() abort
return substitute(s:exec(['go', 'env', 'GOROOT'])[0], '\n', '', 'g')
endfunction
" gopath returns 'go env GOPATH'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("gopath")'
function! go#util#gopath() abort
return substitute(s:exec(['go', 'env', 'GOPATH'])[0], '\n', '', 'g')
endfunction
" gomod returns 'go env GOMOD'. gomod changes depending on the folder. Don't
" use go#util#env as it caches the value.
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,
" so that we always use a standard POSIX-compatible Bourne shell (and not e.g.
" csh, fish, etc.) See #988 and #1276.
function! s:system(cmd, ...) abort
" Preserve original shell, shellredir and shellcmdflag values
let l:shell = &shell
let l:shellredir = &shellredir
let l:shellcmdflag = &shellcmdflag
if !go#util#IsWin() && executable('/bin/sh')
set shell=/bin/sh shellredir=>%s\ 2>&1 shellcmdflag=-c
endif
try
return call('system', [a:cmd] + a:000)
finally
" Restore original values
let &shell = l:shell
let &shellredir = l:shellredir
let &shellcmdflag = l:shellcmdflag
endtry
endfunction
" System runs a shell command "str". Every arguments after "str" is passed to
" stdin.
function! go#util#System(str, ...) abort
return call('s:system', [a:str] + a:000)
endfunction
" Exec runs a shell command "cmd", which must be a list, one argument per item.
" Every list entry will be automatically shell-escaped
" Every other argument is passed to stdin.
function! go#util#Exec(cmd, ...) abort
if len(a:cmd) == 0
call go#util#EchoError("go#util#Exec() called with empty a:cmd")
return ['', 1]
endif
let l:bin = a:cmd[0]
" Lookup the full path, respecting settings such as 'go_bin_path'. On errors,
" CheckBinPath will show a warning for us.
let l:bin = go#path#CheckBinPath(l:bin)
if empty(l:bin)
return ['', 1]
endif
" Finally execute the command using the full, resolved path. Do not pass the
" unmodified command as the correct program might not exist in $PATH.
return call('s:exec', [[l:bin] + a:cmd[1:]] + a:000)
endfunction
function! go#util#ExecInDir(cmd, ...) abort
if !isdirectory(expand("%:p:h"))
return ['', 1]
endif
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd . fnameescape(expand("%:p:h"))
let [l:out, l:err] = call('go#util#Exec', [a:cmd] + a:000)
finally
execute cd . fnameescape(l:dir)
endtry
return [l:out, l:err]
endfunction
function! s:exec(cmd, ...) abort
let l:bin = a:cmd[0]
let l:cmd = go#util#Shelljoin([l:bin] + a:cmd[1:])
if go#util#HasDebug('shell-commands')
call go#util#EchoInfo('shell command: ' . l:cmd)
endif
let l:out = call('s:system', [l:cmd] + a:000)
return [l:out, go#util#ShellError()]
endfunction
function! go#util#ShellError() abort
return v:shell_error
endfunction
" StripPath strips the path's last character if it's a path separator.
" example: '/foo/bar/' -> '/foo/bar'
function! go#util#StripPathSep(path) abort
let last_char = strlen(a:path) - 1
if a:path[last_char] == go#util#PathSep()
return strpart(a:path, 0, last_char)
endif
return a:path
endfunction
" StripTrailingSlash strips the trailing slash from the given path list.
" example: ['/foo/bar/'] -> ['/foo/bar']
function! go#util#StripTrailingSlash(paths) abort
return map(copy(a:paths), 'go#util#StripPathSep(v:val)')
endfunction
" Shelljoin returns a shell-safe string representation of arglist. The
" {special} argument of shellescape() may optionally be passed.
function! go#util#Shelljoin(arglist, ...) abort
try
let ssl_save = &shellslash
set noshellslash
if a:0
return join(map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')'), ' ')
endif
return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ')
finally
let &shellslash = ssl_save
endtry
endfunction
fu! go#util#Shellescape(arg)
try
let ssl_save = &shellslash
set noshellslash
return shellescape(a:arg)
finally
let &shellslash = ssl_save
endtry
endf
" Shelllist returns a shell-safe representation of the items in the given
" arglist. The {special} argument of shellescape() may optionally be passed.
function! go#util#Shelllist(arglist, ...) abort
try
let ssl_save = &shellslash
set noshellslash
if a:0
return map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')')
endif
return map(copy(a:arglist), 'shellescape(v:val)')
finally
let &shellslash = ssl_save
endtry
endfunction
" Returns the byte offset for line and column
function! go#util#Offset(line, col) abort
if &encoding != 'utf-8'
let sep = go#util#LineEnding()
let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep)
let buf .= a:col == 1 ? '' : getline('.')[:a:col-2]
return len(iconv(buf, &encoding, 'utf-8'))
endif
return line2byte(a:line) + (a:col-2)
endfunction
"
" Returns the byte offset for the cursor
function! go#util#OffsetCursor() abort
return go#util#Offset(line('.'), col('.'))
endfunction
" Windo is like the built-in :windo, only it returns to the window the command
" was issued from
function! go#util#Windo(command) abort
let s:currentWindow = winnr()
try
execute "windo " . a:command
finally
execute s:currentWindow. "wincmd w"
unlet s:currentWindow
endtry
endfunction
" snippetcase converts the given word to given preferred snippet setting type
" case.
function! go#util#snippetcase(word) abort
let l:snippet_case = go#config#AddtagsTransform()
if l:snippet_case == "snakecase"
return go#util#snakecase(a:word)
elseif l:snippet_case == "camelcase"
return go#util#camelcase(a:word)
else
return a:word " do nothing
endif
endfunction
" snakecase converts a string to snake case. i.e: FooBar -> foo_bar
" Copied from tpope/vim-abolish
function! go#util#snakecase(word) abort
let word = substitute(a:word, '::', '/', 'g')
let word = substitute(word, '\(\u\+\)\(\u\l\)', '\1_\2', 'g')
let word = substitute(word, '\(\l\|\d\)\(\u\)', '\1_\2', 'g')
let word = substitute(word, '[.-]', '_', 'g')
let word = tolower(word)
return word
endfunction
" camelcase converts a string to camel case. e.g. FooBar or foo_bar will become
" fooBar.
" Copied from tpope/vim-abolish.
function! go#util#camelcase(word) abort
let word = substitute(a:word, '-', '_', 'g')
if word !~# '_' && word =~# '\l'
return substitute(word, '^.', '\l&', '')
else
return substitute(word, '\C\(_\)\=\(.\)', '\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))','g')
endif
endfunction
" pascalcase converts a string to 'PascalCase'. e.g. fooBar or foo_bar will
" become FooBar.
function! go#util#pascalcase(word) abort
let word = go#util#camelcase(a:word)
return toupper(word[0]) . word[1:]
endfunction
" Echo a message to the screen and highlight it with the group in a:hi.
"
" The message can be a list or string; every line with be :echomsg'd separately.
function! s:echo(msg, hi)
let l:msg = []
if type(a:msg) != type([])
let l:msg = split(a:msg, "\n")
else
let l:msg = a:msg
endif
" Tabs display as ^I or <09>, so manually expand them.
let l:msg = map(l:msg, 'substitute(v:val, "\t", " ", "")')
exe 'echohl ' . a:hi
for line in l:msg
echom "vim-go: " . line
endfor
echohl None
endfunction
function! go#util#EchoSuccess(msg)
call s:echo(a:msg, 'Function')
endfunction
function! go#util#EchoError(msg)
call s:echo(a:msg, 'ErrorMsg')
endfunction
function! go#util#EchoWarning(msg)
call s:echo(a:msg, 'WarningMsg')
endfunction
function! go#util#EchoProgress(msg)
redraw
call s:echo(a:msg, 'Identifier')
endfunction
function! go#util#EchoInfo(msg)
call s:echo(a:msg, 'Debug')
endfunction
" Get all lines in the buffer as a a list.
function! go#util#GetLines()
let buf = getline(1, '$')
if &encoding != 'utf-8'
let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")')
endif
if &l:fileformat == 'dos'
" XXX: line2byte() depend on 'fileformat' option.
" so if fileformat is 'dos', 'buf' must include '\r'.
let buf = map(buf, 'v:val."\r"')
endif
return buf
endfunction
" Convert the current buffer to the "archive" format of
" golang.org/x/tools/go/buildutil:
" https://godoc.org/golang.org/x/tools/go/buildutil#ParseOverlayArchive
"
" > The archive consists of a series of files. Each file consists of a name, a
" > decimal file size and the file contents, separated by newlinews. No newline
" > follows after the file contents.
function! go#util#archive()
let l:buffer = join(go#util#GetLines(), "\n")
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
endfunction
" Make a named temporary directory which starts with "prefix".
"
" Unfortunately Vim's tempname() is not portable enough across various systems;
" see: https://github.com/mattn/vim-go/pull/3#discussion_r138084911
function! go#util#tempdir(prefix) abort
" See :help tempfile
if go#util#IsWin()
let l:dirs = [$TMP, $TEMP, 'c:\tmp', 'c:\temp']
else
let l:dirs = [$TMPDIR, '/tmp', './', $HOME]
endif
let l:dir = ''
for l:d in dirs
if !empty(l:d) && filewritable(l:d) == 2
let l:dir = l:d
break
endif
endfor
if l:dir == ''
call go#util#EchoError('Unable to find directory to store temporary directory in')
return
endif
" Not great randomness, but "good enough" for our purpose here.
let l:rnd = sha256(printf('%s%s', reltimestr(reltime()), fnamemodify(bufname(''), ":p")))
let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd)
call mkdir(l:tmp, 'p', 0700)
return l:tmp
endfunction
" Report if the user enabled a debug flag in g:go_debug.
function! go#util#HasDebug(flag)
return index(go#config#Debug(), a:flag) >= 0
endfunction
function! go#util#OpenBrowser(url) abort
let l:cmd = go#config#PlayBrowserCommand()
if len(l:cmd) == 0
redraw
echohl WarningMsg
echo "It seems that you don't have general web browser. Open URL below."
echohl None
echo a:url
return
endif
" if setting starts with a !.
if l:cmd =~ '^!'
let l:cmd = substitute(l:cmd, '%URL%', '\=escape(shellescape(a:url), "#")', 'g')
silent! exec l:cmd
elseif cmd =~ '^:[A-Z]'
let l:cmd = substitute(l:cmd, '%URL%', '\=escape(a:url,"#")', 'g')
exec l:cmd
else
let l:cmd = substitute(l:cmd, '%URL%', '\=shellescape(a:url)', 'g')
call go#util#System(l:cmd)
endif
endfunction
function! go#util#ParseErrors(lines) abort
let errors = []
for line in a:lines
let fatalerrors = matchlist(line, '^\(fatal error:.*\)$')
let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)')
if !empty(fatalerrors)
call add(errors, {"text": fatalerrors[1]})
elseif !empty(tokens)
" strip endlines of form ^M
let out = substitute(tokens[3], '\r$', '', '')
call add(errors, {
\ "filename" : fnamemodify(tokens[1], ':p'),
\ "lnum" : tokens[2],
\ "text" : out,
\ })
elseif !empty(errors)
" Preserve indented lines.
" This comes up especially with multi-line test output.
if match(line, '^\s') >= 0
call add(errors, {"text": substitute(line, '\r$', '', '')})
endif
endif
endfor
return errors
endfunction
function! go#util#ShowInfo(info)
if empty(a:info)
return
endif
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
return function('s:unset', [a:name], l:state)
endif
return function('go#util#SetEnv', [a:name, l:oldvalue], l:state)
endfunction
function! go#util#ClearGroupFromMatches(group) abort
if !exists("*matchaddpos")
return 0
endif
let l:cleared = 0
let m = getmatches()
for item in m
if item['group'] == a:group
call matchdelete(item['id'])
let l:cleared = 1
endif
endfor
return l:cleared
endfunction
function! s:unset(name) abort
try
" unlet $VAR was introducted in Vim 8.0.1832, which is newer than the
" minimal version that vim-go supports. Set the environment variable to
" the empty string in that case. It's not perfect, but it will work fine
" for most things, and is really the best alternative that's available.
if !has('patch-8.0.1832')
call go#util#SetEnv(a:name, '')
return
endif
call execute('unlet $' . a:name)
catch
call go#util#EchoError(printf('could not unset $%s: %s', a:name, v:exception))
endtry
endfunction
function! s:noop(...) abort dict
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et