Skip to content

Commit 7649c3e

Browse files
committed
lsp: restart when gopls is updated
* Restart gopls when gopls is updated * Update scripts/runtest.vim to sleep after each test to give any gopls event handler a chance to run in order to avoid errors that would otherwise be caused by gopls having exited before messages were sent to it. * add sleeps to term_test.vim's tests to avoid errors that happen otherwise.
1 parent 007b69c commit 7649c3e

File tree

5 files changed

+81
-13
lines changed

5 files changed

+81
-13
lines changed

autoload/go/lsp.vim

+47-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ scriptencoding utf-8
77
let s:lspfactory = {}
88

99
function! s:lspfactory.get() dict abort
10-
if !has_key(self, 'current') || empty(self.current) || !has_key(self.current, 'job') || empty(self.current.job)
10+
if empty(get(self, 'current', {})) || empty(get(self.current, 'job', {}))
1111
let self.current = s:newlsp()
1212
endif
1313

@@ -138,6 +138,10 @@ function! s:newlsp() abort
138138
return
139139
endif
140140

141+
if get(self, 'exited', 0)
142+
return
143+
endif
144+
141145
let l:msg = self.newResponse(a:req.id, l:resp)
142146
call self.write(l:msg)
143147
endfunction
@@ -305,13 +309,31 @@ function! s:newlsp() abort
305309
call ch_sendraw(self.job, l:data)
306310
endfunction
307311

308-
function! l:lsp.exit_cb(job, exit_status) dict abort
312+
function! l:lsp.exit_cb(job, exit_status) dict
313+
let self.exited = 1
314+
if !get(self, 'restarting', 0)
315+
return
316+
endif
317+
318+
let l:queue = self.queue
319+
320+
let l:workspaces = self.workspaceDirectories
321+
309322
call s:lspfactory.reset()
323+
let l:lsp = s:lspfactory.get()
324+
325+
" restore workspaces
326+
call call('go#lsp#AddWorkspaceDirectory', l:workspaces)
327+
" * send DidOpen messages for all buffers that have b:did_lsp_open set
328+
" TODO(bc): check modifiable and filetype, too?
329+
bufdo if get(b:, 'go_lsp_did_open', 0) | if &modified | call go#lsp#DidOpen(expand('%:p')) | else | call go#lsp#DidChange(expand('%:p')) | endif | endif
330+
let l:lsp.queue = extend(l:lsp.queue, l:queue)
331+
return
310332
endfunction
311-
" explicitly bind close_cb to state so that within it, self will always refer
312333

313334
function! l:lsp.close_cb(ch) dict abort
314-
" TODO(bc): does anything need to be done here?
335+
" TODO(bc): remove the buffer variables that indicate that gopls has been
336+
" informed that the file is open
315337
endfunction
316338

317339
function! l:lsp.err_cb(ch, msg) dict abort
@@ -767,6 +789,27 @@ function! go#lsp#DebugBrowser() abort
767789
call go#util#OpenBrowser(printf('http://localhost:%d', l:port))
768790
endfunction
769791

792+
function! go#lsp#Restart() abort
793+
if !go#util#has_job() || len(s:lspfactory) == 0 || !has_key(s:lspfactory, 'current')
794+
return
795+
endif
796+
797+
let l:lsp = s:lspfactory.get()
798+
799+
let l:lsp.restarting = 1
800+
801+
let l:state = s:newHandlerState('exit')
802+
803+
let l:msg = go#lsp#message#Shutdown()
804+
let l:state.handleResult = funcref('s:noop')
805+
let l:retval = l:lsp.sendMessage(l:msg, l:state)
806+
807+
let l:msg = go#lsp#message#Exit()
808+
let l:retval = l:lsp.sendMessage(l:msg, l:state)
809+
810+
return l:retval
811+
endfunction
812+
770813
function! s:debug(event, data) abort
771814
if !go#util#HasDebug('lsp')
772815
return

autoload/go/lsp/message.vim

+14
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ function! go#lsp#message#Initialized() abort
3636
\ }
3737
endfunction
3838

39+
function! go#lsp#message#Shutdown() abort
40+
return {
41+
\ 'notification': 0,
42+
\ 'method': 'shutdown',
43+
\ }
44+
endfunction
45+
46+
function! go#lsp#message#Exit() abort
47+
return {
48+
\ 'notification': 1,
49+
\ 'method': 'exit',
50+
\ }
51+
endfunction
52+
3953
function! go#lsp#message#WorkspaceFoldersResult(dirs) abort
4054
return map(copy(a:dirs), function('s:workspaceFolder', []))
4155
endfunction

autoload/go/term_test.vim

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func! Test_GoTermNewMode()
2222
call assert_equal(actual, l:expected)
2323

2424
finally
25+
sleep 50m
2526
call delete(l:tmp, 'rf')
2627
endtry
2728
endfunc
@@ -46,6 +47,7 @@ func! Test_GoTermNewMode_SplitRight()
4647
call assert_equal(actual, l:expected)
4748

4849
finally
50+
sleep 50m
4951
call delete(l:tmp, 'rf')
5052
set nosplitright
5153
endtry

plugin/go.vim

+12-8
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ let s:packages = {
5656
\ 'gogetdoc': ['github.com/zmb3/gogetdoc'],
5757
\ 'goimports': ['golang.org/x/tools/cmd/goimports'],
5858
\ 'golint': ['golang.org/x/lint/golint'],
59-
\ 'gopls': ['golang.org/x/tools/gopls@latest'],
59+
\ 'gopls': ['golang.org/x/tools/gopls@latest', {}, {'after': function('go#lsp#Restart', [])}],
6060
\ 'gometalinter': ['github.com/alecthomas/gometalinter'],
6161
\ 'golangci-lint': ['github.com/golangci/golangci-lint/cmd/golangci-lint'],
6262
\ 'gomodifytags': ['github.com/fatih/gomodifytags'],
@@ -136,29 +136,29 @@ function! s:GoInstallBinaries(updateBinaries, ...)
136136
let l:platform = 'windows'
137137
endif
138138

139-
for [binary, pkg] in items(l:packages)
140-
let l:importPath = pkg[0]
139+
for [l:binary, l:pkg] in items(l:packages)
140+
let l:importPath = l:pkg[0]
141141

142142
" TODO(bc): how to support this with modules? Do we have to clone and then
143143
" install manually? Probably not. I suspect that we can just use GOPATH
144144
" mode and then do the legacy method.
145-
let bin_setting_name = "go_" . binary . "_bin"
145+
let bin_setting_name = "go_" . l:binary . "_bin"
146146

147147
if exists("g:{bin_setting_name}")
148148
let bin = g:{bin_setting_name}
149149
else
150150
if go#util#IsWin()
151-
let bin = binary . '.exe'
151+
let bin = l:binary . '.exe'
152152
else
153-
let bin = binary
153+
let bin = l:binary
154154
endif
155155
endif
156156

157157
if !executable(bin) || a:updateBinaries == 1
158158
if a:updateBinaries == 1
159-
echo "vim-go: Updating " . binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
159+
echo "vim-go: Updating " . l:binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
160160
else
161-
echo "vim-go: ". binary ." not found. Installing ". importPath . " to folder " . go_bin_path
161+
echo "vim-go: ". l:binary ." not found. Installing ". importPath . " to folder " . go_bin_path
162162
endif
163163

164164
if l:importPath =~ "@"
@@ -208,9 +208,13 @@ function! s:GoInstallBinaries(updateBinaries, ...)
208208
echom "Error installing " . l:importPath . ": " . l:out
209209
endif
210210

211+
211212
call call(Restore_modules, [])
212213
endif
213214

215+
if len(l:pkg) > 2
216+
call call(get(l:pkg[2], 'after', function('s:noop', [])), [])
217+
endif
214218
endif
215219
endfor
216220

scripts/runtest.vim

+6-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ for s:test in sort(s:tests)
6464
endif
6565
try
6666
exe 'call ' . s:test
67+
" sleep to give events a chance to be processed. This is especially
68+
" important for the LSP code to have a chance to run before Vim exits, in
69+
" order to avoid errors trying to write to the gopls channels since Vim
70+
" would otherwise stop gopls before the event handlers were run and result
71+
" in 'stream closed' errors when the events were run _after_ gopls exited.
72+
sleep 50m
6773
catch
6874
let v:errors += [v:exception]
6975
endtry
@@ -76,7 +82,6 @@ for s:test in sort(s:tests)
7682
let s:elapsed_time = substitute(reltimestr(reltime(s:started)), '^\s*\(.\{-}\)\s*$', '\1', '')
7783
let s:done += 1
7884

79-
8085
if len(v:errors) > 0
8186
let s:fail += 1
8287
call add(s:logs, printf("--- FAIL %s (%ss)", s:test[:-3], s:elapsed_time))

0 commit comments

Comments
 (0)