mirror of
https://github.com/amix/vimrc
synced 2025-07-05 07:25:00 +08:00
Update plugins using update_plugins.py
This commit is contained in:
@ -79,7 +79,12 @@ endfunction
|
||||
" go#complete#GoInfo returns the description of the identifier under the
|
||||
" cursor.
|
||||
function! go#complete#GetInfo() abort
|
||||
return s:sync_info(0)
|
||||
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
|
||||
|
@ -2,23 +2,33 @@
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
func! Test_GetInfo()
|
||||
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)
|
||||
|
||||
call cursor(8, 3)
|
||||
|
||||
let g:go_info_mode = 'gocode'
|
||||
let expected = 'func Example(s string)'
|
||||
let actual = go#complete#GetInfo()
|
||||
call assert_equal(expected, actual)
|
||||
|
||||
let g:go_info_mode = 'guru'
|
||||
call go#config#InfoMode()
|
||||
let actual = go#complete#GetInfo()
|
||||
call assert_equal(expected, actual)
|
||||
|
||||
unlet g:go_info_mode
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
@ -14,10 +14,6 @@ function! go#config#VersionWarning() abort
|
||||
return get(g:, 'go_version_warning', 1)
|
||||
endfunction
|
||||
|
||||
function! go#config#NullModuleWarning() abort
|
||||
return get(g:, 'go_null_module_warning', 1)
|
||||
endfunction
|
||||
|
||||
function! go#config#BuildTags() abort
|
||||
return get(g:, 'go_build_tags', '')
|
||||
endfunction
|
||||
@ -118,7 +114,7 @@ function! go#config#ListAutoclose() abort
|
||||
endfunction
|
||||
|
||||
function! go#config#InfoMode() abort
|
||||
return get(g:, 'go_info_mode', 'gocode')
|
||||
return get(g:, 'go_info_mode', 'gopls')
|
||||
endfunction
|
||||
|
||||
function! go#config#GuruScope() abort
|
||||
@ -178,12 +174,15 @@ function! go#config#DocUrl() abort
|
||||
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', 'guru')
|
||||
return get(g:, 'go_def_mode', 'gopls')
|
||||
endfunction
|
||||
|
||||
function! go#config#DeclsIncludes() abort
|
||||
@ -272,10 +271,6 @@ function! go#config#MetalinterEnabled() abort
|
||||
return get(g:, "go_metalinter_enabled", default_enabled)
|
||||
endfunction
|
||||
|
||||
function! go#config#MetalinterDisabled() abort
|
||||
return get(g:, "go_metalinter_disabled", [])
|
||||
endfunction
|
||||
|
||||
function! go#config#GolintBin() abort
|
||||
return get(g:, "go_golint_bin", "golint")
|
||||
endfunction
|
||||
|
@ -595,10 +595,23 @@ function! go#debug#Start(is_test, ...) abort
|
||||
|
||||
" append the package when it's given.
|
||||
if len(a:000) > 0
|
||||
let l:pkgname = go#package#FromPath(a:1)
|
||||
if l:pkgname is -1
|
||||
call go#util#EchoError('could not determine package name')
|
||||
return
|
||||
let l:pkgname = a:1
|
||||
if l:pkgname[0] == '.'
|
||||
let l:pkgabspath = fnamemodify(l:pkgname, ':p')
|
||||
|
||||
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||
let l:dir = getcwd()
|
||||
execute l:cd fnameescape(expand('%:p:h'))
|
||||
|
||||
try
|
||||
let l:pkgname = go#package#FromPath(l:pkgabspath)
|
||||
if type(l:pkgname) == type(0)
|
||||
call go#util#EchoError('could not determine package name')
|
||||
return
|
||||
endif
|
||||
finally
|
||||
execute l:cd fnameescape(l:dir)
|
||||
endtry
|
||||
endif
|
||||
|
||||
let l:cmd += [l:pkgname]
|
||||
|
@ -10,6 +10,10 @@ 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
|
||||
@ -52,14 +56,22 @@ function! Test_GoDebugStart_Errors() abort
|
||||
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'))
|
||||
|
@ -50,8 +50,16 @@ func! Test_Jump_leaves_lists() abort
|
||||
|
||||
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 start = reltime()
|
||||
while bufnr('%') == l:bufnr && reltimefloat(reltime(start)) < 10
|
||||
sleep 100m
|
||||
|
@ -76,6 +76,18 @@ function! go#doc#Open(newmode, mode, ...) abort
|
||||
endfunction
|
||||
|
||||
function! s:GodocView(newposition, position, content) abort
|
||||
" popup window
|
||||
if go#config#DocPopupWindow() && has("patch-8.1.1513")
|
||||
call popup_clear()
|
||||
|
||||
call popup_atcursor(split(a:content, '\n'), {
|
||||
\ 'padding': [1, 1, 1, 1],
|
||||
\ 'borderchars': ['-','|','-','|','+','+','+','+'],
|
||||
\ "border": [1, 1, 1, 1],
|
||||
\ })
|
||||
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)
|
||||
|
@ -22,10 +22,6 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
for linter in linters
|
||||
let cmd += ["--enable=".linter]
|
||||
endfor
|
||||
|
||||
for linter in go#config#MetalinterDisabled()
|
||||
let cmd += ["--disable=".linter]
|
||||
endfor
|
||||
else
|
||||
" the user wants something else, let us use it.
|
||||
let cmd = split(go#config#MetalinterCommand(), " ")
|
||||
@ -44,7 +40,7 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
endif
|
||||
let cmd += include
|
||||
elseif l:metalinter == "golangci-lint"
|
||||
let goargs[0] = expand('%:p')
|
||||
let goargs[0] = expand('%:p:h')
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -88,7 +84,13 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
else
|
||||
let l:winid = win_getid(winnr())
|
||||
" Parse and populate our location list
|
||||
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
|
||||
|
||||
let 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))
|
||||
@ -101,10 +103,11 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Golint calls 'golint'.
|
||||
" 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#ExecInDir([go#config#GolintBin(), '.'])
|
||||
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
|
||||
@ -140,7 +143,7 @@ function! go#lint#Vet(bang, ...) abort
|
||||
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', 'tool', 'vet'] + a:000)
|
||||
let [l:out, l:err] = go#util#ExecInDir(['go', 'tool', 'vet'] + a:000)
|
||||
endif
|
||||
|
||||
let l:listtype = go#list#Type("GoVet")
|
||||
@ -229,6 +232,7 @@ function! s:lint_job(args, bang, autosave)
|
||||
|
||||
if a:autosave
|
||||
let l:opts.for = "GoMetaLinterAutoSave"
|
||||
let l:opts.complete = funcref('s:metalinterautosavecomplete', [expand('%:p:t')])
|
||||
endif
|
||||
|
||||
" autowrite is not enabled for jobs
|
||||
@ -278,6 +282,21 @@ function! s:golangcilintcmd(bin_path)
|
||||
return cmd
|
||||
endfunction
|
||||
|
||||
function! s:metalinterautosavecomplete(filepath, job, exit_code, messages)
|
||||
if len(a:messages) == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:file = expand('%:p:t')
|
||||
let l:idx = len(a:messages) - 1
|
||||
while l:idx >= 0
|
||||
if a:messages[l:idx] !~# '^' . 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
|
||||
|
@ -3,7 +3,7 @@ let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
func! Test_Gometa() abort
|
||||
call s:gometa('gometaliner')
|
||||
call s:gometa('gometalinter')
|
||||
endfunc
|
||||
|
||||
func! Test_GometaGolangciLint() abort
|
||||
@ -11,14 +11,19 @@ func! Test_GometaGolangciLint() abort
|
||||
endfunc
|
||||
|
||||
func! s:gometa(metalinter) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
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_comand = a:metalinter
|
||||
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('%')+1, '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')
|
||||
@ -36,48 +41,11 @@ func! s:gometa(metalinter) abort
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
finally
|
||||
call call(RestoreGOPATH, [])
|
||||
unlet g:go_metalinter_enabled
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_GometaWithDisabled() abort
|
||||
call s:gometawithdisabled('gometalinter')
|
||||
endfunc
|
||||
|
||||
func! Test_GometaWithDisabledGolangciLint() abort
|
||||
call s:gometawithdisabled('golangci-lint')
|
||||
endfunc
|
||||
|
||||
func! s:gometawithdisabled(metalinter) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
try
|
||||
let g:go_metalinter_comand = 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)'}
|
||||
\ ]
|
||||
|
||||
" clear the quickfix lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
let g:go_metalinter_disabled = ['vet']
|
||||
|
||||
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
|
||||
unlet g:go_metalinter_disabled
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_GometaAutoSave() abort
|
||||
call s:gometaautosave('gometalinter')
|
||||
endfunc
|
||||
@ -87,14 +55,19 @@ func! Test_GometaAutoSaveGolangciLint() abort
|
||||
endfunc
|
||||
|
||||
func! s:gometaautosave(metalinter) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
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_comand = a:metalinter
|
||||
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()
|
||||
|
||||
@ -114,12 +87,13 @@ func! s:gometaautosave(metalinter) abort
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
finally
|
||||
call call(RestoreGOPATH, [])
|
||||
unlet g:go_metalinter_autosave_enabled
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_Vet() abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
|
||||
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
|
||||
compiler go
|
||||
|
||||
@ -142,6 +116,63 @@ func! Test_Vet() abort
|
||||
let actual = getqflist()
|
||||
endwhile
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
call call(RestoreGOPATH, [])
|
||||
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
|
||||
|
||||
|
@ -26,7 +26,6 @@ function! s:newlsp() abort
|
||||
if has('nvim')
|
||||
set shortmess-=F
|
||||
endif
|
||||
" TODO(bc): start the server in the background using a shell that waits for the right output before returning.
|
||||
call go#util#EchoWarning('Features that rely on gopls will not work without either Vim 8.0.0087 or newer with +job or Neovim')
|
||||
" Sleep one second to make sure people see the message. Otherwise it is
|
||||
" often immediately overwritten by an async message.
|
||||
@ -55,6 +54,7 @@ function! s:newlsp() abort
|
||||
\ 'last_request_id': 0,
|
||||
\ 'buf': '',
|
||||
\ 'handlers': {},
|
||||
\ 'workspaceDirectories': [],
|
||||
\ }
|
||||
|
||||
function! l:lsp.readMessage(data) dict abort
|
||||
@ -83,19 +83,17 @@ function! s:newlsp() abort
|
||||
endif
|
||||
|
||||
" get the start of the rest
|
||||
let l:rest_start_idx = l:body_start_idx + str2nr(l:length_match[1])
|
||||
let l:next_start_idx = l:body_start_idx + str2nr(l:length_match[1])
|
||||
|
||||
if len(l:rest) < l:rest_start_idx
|
||||
if len(l:rest) < l:next_start_idx
|
||||
" incomplete response body
|
||||
break
|
||||
endif
|
||||
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "<-\n" . l:rest[:l:rest_start_idx - 1])
|
||||
endif
|
||||
call s:debug('received', l:rest[:l:next_start_idx - 1])
|
||||
|
||||
let l:body = l:rest[l:body_start_idx : l:rest_start_idx - 1]
|
||||
let l:rest = l:rest[l:rest_start_idx :]
|
||||
let l:body = l:rest[l:body_start_idx : l:next_start_idx - 1]
|
||||
let l:rest = l:rest[l:next_start_idx :]
|
||||
|
||||
try
|
||||
" add the json body to the list.
|
||||
@ -113,53 +111,77 @@ function! s:newlsp() abort
|
||||
function! l:lsp.handleMessage(ch, data) dict abort
|
||||
let self.buf .= a:data
|
||||
|
||||
let [self.buf, l:responses] = self.readMessage(self.buf)
|
||||
let [self.buf, l:messages] = self.readMessage(self.buf)
|
||||
|
||||
" TODO(bc): handle notifications (e.g. window/showMessage).
|
||||
|
||||
for l:response in l:responses
|
||||
if has_key(l:response, 'id') && has_key(self.handlers, l:response.id)
|
||||
try
|
||||
let l:handler = self.handlers[l:response.id]
|
||||
|
||||
let l:winid = win_getid(winnr())
|
||||
" Always set the active window to the window that was active when
|
||||
" the request was sent. Among other things, this makes sure that
|
||||
" the correct window's location list will be populated when the
|
||||
" list type is 'location' and the user has moved windows since
|
||||
" sending the reques.
|
||||
call win_gotoid(l:handler.winid)
|
||||
|
||||
if has_key(l:response, 'error')
|
||||
call l:handler.requestComplete(0)
|
||||
if has_key(l:handler, 'error')
|
||||
call call(l:handler.error, [l:response.error.message])
|
||||
else
|
||||
call go#util#EchoError(l:response.error.message)
|
||||
endif
|
||||
call win_gotoid(l:winid)
|
||||
return
|
||||
endif
|
||||
call l:handler.requestComplete(1)
|
||||
|
||||
let l:winidBeforeHandler = l:handler.winid
|
||||
call call(l:handler.handleResult, [l:response.result])
|
||||
|
||||
" change the window back to the window that was active when
|
||||
" starting to handle the response _only_ if the handler didn't
|
||||
" update the winid, so that handlers can set the winid if needed
|
||||
" (e.g. :GoDef).
|
||||
if l:handler.winid == l:winidBeforeHandler
|
||||
call win_gotoid(l:winid)
|
||||
endif
|
||||
finally
|
||||
call remove(self.handlers, l:response.id)
|
||||
endtry
|
||||
for l:message in l:messages
|
||||
if has_key(l:message, 'method')
|
||||
if has_key(l:message, 'id')
|
||||
call self.handleRequest(l:message)
|
||||
else
|
||||
call self.handleNotification(l:message)
|
||||
endif
|
||||
elseif has_key(l:message, 'result') || has_key(l:message, 'error')
|
||||
call self.handleResponse(l:message)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleRequest(req) dict abort
|
||||
if a:req.method == 'workspace/workspaceFolders'
|
||||
let l:resp = go#lsp#message#workspaceFolders(self.workspaceDirectories)
|
||||
endif
|
||||
|
||||
let l:msg = self.newResponse(l:resp)
|
||||
call self.write(l:msg)
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleNotification(req) dict abort
|
||||
" TODO(bc): handle notifications (e.g. window/showMessage).
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleResponse(resp) dict abort
|
||||
if has_key(a:resp, 'id') && has_key(self.handlers, a:resp.id)
|
||||
try
|
||||
let l:handler = self.handlers[a:resp.id]
|
||||
|
||||
let l:winid = win_getid(winnr())
|
||||
" Always set the active window to the window that was active when
|
||||
" the request was sent. Among other things, this makes sure that
|
||||
" the correct window's location list will be populated when the
|
||||
" list type is 'location' and the user has moved windows since
|
||||
" sending the request.
|
||||
call win_gotoid(l:handler.winid)
|
||||
|
||||
if has_key(a:resp, 'error')
|
||||
call l:handler.requestComplete(0)
|
||||
if has_key(l:handler, 'error')
|
||||
call call(l:handler.error, [a:resp.error.message])
|
||||
else
|
||||
call go#util#EchoError(a:resp.error.message)
|
||||
endif
|
||||
call win_gotoid(l:winid)
|
||||
return
|
||||
endif
|
||||
call l:handler.requestComplete(1)
|
||||
|
||||
let l:winidBeforeHandler = l:handler.winid
|
||||
call call(l:handler.handleResult, [a:resp.result])
|
||||
|
||||
" change the window back to the window that was active when
|
||||
" starting to handle the message _only_ if the handler didn't
|
||||
" update the winid, so that handlers can set the winid if needed
|
||||
" (e.g. :GoDef).
|
||||
if l:handler.winid == l:winidBeforeHandler
|
||||
call win_gotoid(l:winid)
|
||||
endif
|
||||
finally
|
||||
call remove(self.handlers, a:resp.id)
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleInitializeResult(result) dict abort
|
||||
call go#util#EchoProgress("initialized gopls")
|
||||
let self.ready = 1
|
||||
" TODO(bc): send initialized message to the server?
|
||||
|
||||
@ -173,7 +195,15 @@ function! s:newlsp() abort
|
||||
endfunction
|
||||
|
||||
function! l:lsp.sendMessage(data, handler) dict abort
|
||||
" block while waiting on any in flight initializations to avoid race
|
||||
" conditions initializing gopls.
|
||||
while get(self, 'checkingmodule', 0)
|
||||
sleep 50 m
|
||||
redraw
|
||||
endwhile
|
||||
|
||||
if !self.last_request_id
|
||||
call go#util#EchoProgress("initializing gopls")
|
||||
" TODO(bc): run a server per module and one per GOPATH? (may need to
|
||||
" keep track of servers by rootUri).
|
||||
let l:wd = go#util#ModuleRoot()
|
||||
@ -186,25 +216,7 @@ function! s:newlsp() abort
|
||||
let l:wd = getcwd()
|
||||
endif
|
||||
|
||||
" do not attempt to send a message to gopls when using neither GOPATH
|
||||
" mode nor module mode.
|
||||
if go#package#FromPath(l:wd) == -2
|
||||
if go#config#NullModuleWarning() && (!has_key(self, 'warned') || !self.warned)
|
||||
let l:oldshortmess=&shortmess
|
||||
if has('nvim')
|
||||
set shortmess-=F
|
||||
endif
|
||||
call go#util#EchoWarning('Features that rely on gopls will not work correctly outside of GOPATH or a module.')
|
||||
let self.warned = 1
|
||||
" Sleep one second to make sure people see the message. Otherwise it is
|
||||
" often immediately overwritten by an async message.
|
||||
sleep 1
|
||||
let &shortmess=l:oldshortmess
|
||||
endif
|
||||
|
||||
return -1
|
||||
endif
|
||||
|
||||
let self.workspaceDirectories = add(self.workspaceDirectories, l:wd)
|
||||
let l:msg = self.newMessage(go#lsp#message#Initialize(l:wd))
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
@ -235,7 +247,7 @@ function! s:newlsp() abort
|
||||
let l:msg = {
|
||||
\ 'method': a:data.method,
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ }
|
||||
\ }
|
||||
|
||||
if !a:data.notification
|
||||
let self.last_request_id += 1
|
||||
@ -249,13 +261,19 @@ function! s:newlsp() abort
|
||||
return l:msg
|
||||
endfunction
|
||||
|
||||
function l:lsp.newResponse(id, result) dict abort
|
||||
let l:msg = {
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ 'id': a:id,
|
||||
\ 'result': a:result,
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! l:lsp.write(msg) dict abort
|
||||
let l:body = json_encode(a:msg)
|
||||
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "->\n" . l:data)
|
||||
endif
|
||||
call s:debug('sent', l:data)
|
||||
|
||||
if has('nvim')
|
||||
call chansend(self.job, l:data)
|
||||
@ -275,9 +293,7 @@ function! s:newlsp() abort
|
||||
endfunction
|
||||
|
||||
function! l:lsp.err_cb(ch, msg) dict abort
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "<-stderr\n" . a:msg)
|
||||
endif
|
||||
call s:debug('stderr', a:msg)
|
||||
endfunction
|
||||
|
||||
" explicitly bind callbacks to l:lsp so that within it, self will always refer
|
||||
@ -303,7 +319,6 @@ function! s:newlsp() abort
|
||||
" start in.
|
||||
let l:lsp.job = go#job#Start([l:bin_path], l:opts)
|
||||
|
||||
" TODO(bc): send the initialize message now?
|
||||
return l:lsp
|
||||
endfunction
|
||||
|
||||
@ -363,16 +378,17 @@ function! s:requestComplete(ok) abort dict
|
||||
endfunction
|
||||
|
||||
function! s:start() abort dict
|
||||
if self.statustype != ''
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': self.statustype,
|
||||
\ 'state': "started",
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(self.jobdir, status)
|
||||
endif
|
||||
let self.started_at = reltime()
|
||||
if self.statustype == ''
|
||||
return
|
||||
endif
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': self.statustype,
|
||||
\ 'state': "started",
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(self.jobdir, status)
|
||||
endfunction
|
||||
|
||||
" go#lsp#Definition calls gopls to get the definition of the identifier at
|
||||
@ -506,7 +522,17 @@ function! s:completionHandler(next, msg) abort dict
|
||||
if has_key(l:item, 'detail')
|
||||
let l:match.info = l:item.detail
|
||||
if go#lsp#completionitemkind#IsFunction(l:item.kind) || go#lsp#completionitemkind#IsMethod(l:item.kind)
|
||||
let l:match.info = printf('func %s %s', l:item.label, l:item.detail)
|
||||
let l:match.info = printf('%s %s', l:item.label, l:item.detail)
|
||||
|
||||
" The detail provided by gopls hasn't always provided the the full
|
||||
" signature including the return value. The label used to be the
|
||||
" function signature and the detail was the return value. Handle
|
||||
" that case for backward compatibility. This can be removed in the
|
||||
" future once it's likely that the majority of users are on a recent
|
||||
" version of gopls.
|
||||
if l:item.detail !~ '^func'
|
||||
let l:match.info = printf('func %s %s', l:item.label, l:item.detail)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -561,13 +587,32 @@ function! go#lsp#Info(showstatus)
|
||||
let l:state = s:newHandlerState('')
|
||||
endif
|
||||
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [a:showstatus], l:state)
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [function('s:info', [1], l:state), a:showstatus], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:infoDefinitionHandler(showstatus, msg) abort dict
|
||||
function! go#lsp#GetInfo()
|
||||
let l:fname = expand('%:p')
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
|
||||
call go#lsp#DidChange(l:fname)
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
|
||||
let l:info = go#promise#New(function('s:info', [0], l:state), 10000, '')
|
||||
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [l:info.wrapper, 0], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:info.await()
|
||||
endfunction
|
||||
|
||||
function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
|
||||
" gopls returns a []Location; just take the first one.
|
||||
let l:msg = a:msg[0]
|
||||
|
||||
@ -584,22 +629,101 @@ function! s:infoDefinitionHandler(showstatus, msg) abort dict
|
||||
let l:state = s:newHandlerState('')
|
||||
endif
|
||||
|
||||
let l:state.handleResult = funcref('s:hoverHandler', [function('s:info', [], l:state)], l:state)
|
||||
let l:state.handleResult = funcref('s:hoverHandler', [a:next], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:info(content) abort dict
|
||||
function! s:info(show, content) abort dict
|
||||
let l:content = s:infoFromHoverContent(a:content)
|
||||
|
||||
if a:show
|
||||
call go#util#ShowInfo(l:content)
|
||||
endif
|
||||
|
||||
return l:content
|
||||
endfunction
|
||||
|
||||
function! s:infoFromHoverContent(content) abort
|
||||
if len(a:content) < 1
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:content = a:content[0]
|
||||
|
||||
" strip godoc summary
|
||||
let l:content = substitute(l:content, '^[^\n]\+\n', '', '')
|
||||
" Hover content with godoc summary will have the godoc summary in the first
|
||||
" line, and the second line will not have leading whitespace. When there is
|
||||
" leading whitespace on the second line, then the hover content is for a
|
||||
" struct or interface without godoc.
|
||||
let l:lines = split(l:content, '\n')
|
||||
if len(l:lines) > 1 && (l:lines[1] !~# '^\s')
|
||||
let l:content = substitute(l:content, '^[^\n]\+\n', '', '')
|
||||
endif
|
||||
|
||||
" strip off the method set and fields of structs and interfaces.
|
||||
if l:content =~# '^type [^ ]\+ \(struct\|interface\)'
|
||||
if l:content =~# '^\(type \)\?[^ ]\+ \(struct\|interface\)'
|
||||
let l:content = substitute(l:content, '{.*', '', '')
|
||||
endif
|
||||
call go#util#ShowInfo(l:content)
|
||||
|
||||
return l:content
|
||||
endfunction
|
||||
|
||||
function! go#lsp#AddWorkspace(...) abort
|
||||
if a:0 == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:workspaces = []
|
||||
for l:dir in a:000
|
||||
let l:dir = fnamemodify(l:dir, ':p')
|
||||
if !isdirectory(l:dir)
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:workspaces = add(l:workspaces, l:dir)
|
||||
endfor
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
let l:lsp.workspaceDirectories = extend(l:lsp.workspaceDirectories, l:workspaces)
|
||||
let l:msg = go#lsp#message#AddWorkspaces(l:workspaces)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:debug(event, data) abort
|
||||
if !go#util#HasDebug('lsp')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:winid = win_getid()
|
||||
|
||||
let l:name = '__GOLSP_LOG__'
|
||||
let l:log_winid = bufwinid(l:name)
|
||||
if l:log_winid == -1
|
||||
silent keepalt botright 10new
|
||||
silent file `='__GOLSP_LOG__'`
|
||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||
setlocal filetype=golsplog
|
||||
else
|
||||
call win_gotoid(l:log_winid)
|
||||
endif
|
||||
|
||||
try
|
||||
setlocal modifiable
|
||||
if getline(1) == ''
|
||||
call setline('$', printf('%s: %s', a:event, a:data))
|
||||
else
|
||||
call append('$', printf('%s: %s', a:event, a:data))
|
||||
endif
|
||||
normal! G
|
||||
setlocal nomodifiable
|
||||
finally
|
||||
call win_gotoid(l:winid)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
@ -10,7 +10,9 @@ function! go#lsp#message#Initialize(wd) abort
|
||||
\ 'processId': getpid(),
|
||||
\ 'rootUri': go#path#ToURI(a:wd),
|
||||
\ 'capabilities': {
|
||||
\ 'workspace': {},
|
||||
\ 'workspace': {
|
||||
\ 'workspaceFolders': v:true,
|
||||
\ },
|
||||
\ 'textDocument': {
|
||||
\ 'hover': {
|
||||
\ 'contentFormat': ['plaintext'],
|
||||
@ -21,6 +23,14 @@ function! go#lsp#message#Initialize(wd) abort
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#workspaceFolders(dirs) abort
|
||||
return map(copy(a:dirs), function('s:workspaceFolderToURI', []))
|
||||
endfunction
|
||||
|
||||
function s:workspaceFolderToURI(key, val) abort
|
||||
return go#path#ToURI(a:val)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Definition(file, line, col) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
@ -116,6 +126,25 @@ function! go#lsp#message#Hover(file, line, col) abort
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#AddWorkspaces(dirs) abort
|
||||
let l:dirs = map(copy(a:dirs), function('s:workspaceFodlerToAddURI', []))
|
||||
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'workspace/didChangeWorkspaceFolders',
|
||||
\ 'params': {
|
||||
\ 'event': {
|
||||
\ 'added': l:dirs,
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
|
||||
endfunction
|
||||
|
||||
function s:workspaceFolderToAddURI(key, val) abort
|
||||
return {'uri': go#path#ToURI(a:val), 'name': a:val}
|
||||
endfunction
|
||||
|
||||
function! s:position(line, col) abort
|
||||
return {'line': a:line - 1, 'character': a:col-1}
|
||||
endfunction
|
||||
|
@ -8,10 +8,14 @@ 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\+\)\? ')
|
||||
let s:go_major_version = str2nr(tokens[1])
|
||||
if len(tokens) > 0
|
||||
let s:go_major_version = str2nr(tokens[1])
|
||||
else
|
||||
let s:go_major_version = ""
|
||||
endif
|
||||
endif
|
||||
|
||||
if s:go_major_version < "11"
|
||||
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
|
||||
|
@ -82,6 +82,10 @@ function! s:vendordirs() abort
|
||||
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}}'])
|
||||
@ -111,35 +115,29 @@ function! s:vendordirs() abort
|
||||
endfunction
|
||||
|
||||
let s:import_paths = {}
|
||||
" ImportPath returns the import path of the package for current buffer.
|
||||
" 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 dir = expand("%:p:h")
|
||||
let l:dir = expand("%:p:h")
|
||||
if has_key(s:import_paths, dir)
|
||||
return s:import_paths[dir]
|
||||
return s:import_paths[l:dir]
|
||||
endif
|
||||
|
||||
let [l:out, l:err] = go#util#ExecInDir(['go', 'list'])
|
||||
if l:err != 0
|
||||
let l:importpath = go#package#FromPath(l:dir)
|
||||
if type(l:importpath) == type(0)
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:importpath = split(out, '\n')[0]
|
||||
|
||||
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
|
||||
" Check it and retun an error if that is the case
|
||||
if l:importpath[0] ==# '_'
|
||||
return -1
|
||||
endif
|
||||
|
||||
let s:import_paths[dir] = l:importpath
|
||||
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 and not in a module.
|
||||
" 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()
|
||||
@ -150,25 +148,44 @@ function! go#package#FromPath(arg) abort
|
||||
endif
|
||||
|
||||
execute l:cd fnameescape(l:path)
|
||||
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.
|
||||
if !empty(s:module())
|
||||
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
|
||||
endif
|
||||
let [l:out, l:err] = go#util#Exec(['go', 'list'])
|
||||
execute l:cd fnameescape(l:dir)
|
||||
if l:err != 0
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:importpath = split(l:out, '\n')[0]
|
||||
let l:importpath = split(l:out, '\n')[0]
|
||||
finally
|
||||
execute l:cd fnameescape(l:dir)
|
||||
endtry
|
||||
|
||||
" go list returns '_CURRENTDIRECTORY' if the directory is neither in GOPATH
|
||||
" nor in a module. Check it and retun an error if that is the case
|
||||
" 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] ==# '_'
|
||||
return -2
|
||||
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
|
||||
|
50
sources_non_forked/vim-go/autoload/go/promise.vim
Normal file
50
sources_non_forked/vim-go/autoload/go/promise.vim
Normal file
@ -0,0 +1,50 @@
|
||||
" 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
|
41
sources_non_forked/vim-go/autoload/go/promise_test.vim
Normal file
41
sources_non_forked/vim-go/autoload/go/promise_test.vim
Normal file
@ -0,0 +1,41 @@
|
||||
" 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
|
Reference in New Issue
Block a user