mirror of
https://github.com/amix/vimrc
synced 2025-07-07 00:15:00 +08:00
Removed vim-go
This commit is contained in:
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
notafunc()
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
package complete
|
||||
|
||||
type T struct {
|
||||
V string
|
||||
}
|
||||
|
||||
func Example(s string) {
|
||||
Example("")
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package config
|
||||
|
||||
func Example() {
|
||||
foo()
|
||||
}
|
@ -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")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// +build !constrained
|
||||
|
||||
package config
|
||||
|
||||
func foo() {
|
||||
println("foo")
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module config
|
||||
|
||||
go 1.13
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go"
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Foo(log *logging.TestLogger) {
|
||||
log.Debug("vim-go")
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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)
|
||||
}
|
@ -1 +0,0 @@
|
||||
../imports/
|
@ -1,6 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
notafunc()
|
||||
println("vim-go")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package foo
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingFooDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func AlsoMissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go"
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "hello world!"
|
||||
fmt.Printf("%d\n", str)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
module package
|
||||
|
||||
go 1.12
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go")
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello, world")
|
||||
}
|
@ -1 +0,0 @@
|
||||
/pkg
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("vim-go"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleHelloWorld() {
|
||||
fmt.Println("Hello, World")
|
||||
// Output: What's shakin
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package mock
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fail(t *testing.T) {
|
||||
t.Fatal("another package badness")
|
||||
}
|
@ -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")
|
||||
}
|
@ -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")
|
||||
})
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSomething(t *testing.T) {
|
||||
r := struct{}{}
|
||||
ioutil.ReadAll(r)
|
||||
}
|
@ -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")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Errorf("%v")
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Reference in New Issue
Block a user