Skip to content

Commit 898ebbd

Browse files
authored
Merge pull request #2779 from bhcleek/lsp/highlight/after-save
lsp: keep diagnostics highlighting on format
2 parents f5d34f4 + 7a97d3a commit 898ebbd

File tree

10 files changed

+197
-47
lines changed

10 files changed

+197
-47
lines changed

autoload/go/config_test.vim

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func! Test_GoplsEnabled_Clear() abort
9797

9898
finally
9999
unlet g:go_gopls_enabled
100+
call delete(l:tmp, 'rf')
100101
endtry
101102
endfunc
102103
" restore Vi compatibility settings

autoload/go/fmt.vim

+6-8
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ function! go#fmt#Format(withGoimport) abort
6464

6565
let current_col = col('.')
6666
let [l:out, l:err] = go#fmt#run(l:bin_name, l:tmpname, expand('%'))
67-
let diff_offset = len(readfile(l:tmpname)) - line('$')
67+
let line_offset = len(readfile(l:tmpname)) - line('$')
68+
let l:orig_line = getline('.')
6869

6970
if l:err == 0
7071
call go#fmt#update_file(l:tmpname, expand('%'))
@@ -92,8 +93,10 @@ function! go#fmt#Format(withGoimport) abort
9293
call winrestview(l:curw)
9394
endif
9495

95-
" be smart and jump to the line the new statement was added/removed
96-
call cursor(line('.') + diff_offset, current_col)
96+
" be smart and jump to the line the new statement was added/removed and
97+
" adjust the column within the line
98+
let l:lineno = line('.') + line_offset
99+
call cursor(l:lineno, current_col + (len(getline(l:lineno)) - len(l:orig_line)))
97100

98101
" Syntax highlighting breaks less often.
99102
syntax sync fromstart
@@ -117,11 +120,6 @@ function! go#fmt#update_file(source, target)
117120
call setfperm(a:target , original_fperm)
118121
endif
119122

120-
" reset diagnostic matches before reloading so that any locations that have
121-
" been invalidated by formatting won't cause errors when the buffer is
122-
" reloaded from disk.
123-
let b:go_diagnostic_matches = {'errors': [], 'warnings': []}
124-
125123
" reload buffer to reflect latest changes
126124
silent edit!
127125

autoload/go/guru.vim

+5-1
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,11 @@ function! s:same_ids_highlight(exit_val, output, mode) abort
484484
" is redisplayed: e.g. :edit, :GoRename, etc.
485485
augroup vim-go-sameids
486486
autocmd! * <buffer>
487-
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
487+
if has('textprop')
488+
autocmd BufReadPost <buffer> nested call go#guru#SameIds(0)
489+
else
490+
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
491+
endif
488492
augroup end
489493
endif
490494
endfunction

autoload/go/highlight_test.vim

+147
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,153 @@ function! s:numericHighlightGroupInSliceSlicing(testname, from, to)
270270
endtry
271271
endfunction
272272

273+
function! Test_diagnostic_after_fmt() abort
274+
let g:go_fmt_command = 'gofmt'
275+
try
276+
call s:diagnostic_after_write( [
277+
\ 'package main',
278+
\ 'import "fmt"',
279+
\ '',
280+
\ 'func main() {',
281+
\ '',
282+
\ "\tfmt.Println(\x1fhello)",
283+
\ '}',
284+
\ ], [])
285+
finally
286+
unlet g:go_fmt_command
287+
endtry
288+
endfunction
289+
290+
function! Test_diagnostic_after_fmt_change() abort
291+
" craft a file that will be changed when its written (gofmt will change it).
292+
let g:go_fmt_command = 'gofmt'
293+
try
294+
call s:diagnostic_after_write( [
295+
\ 'package main',
296+
\ 'import "fmt"',
297+
\ '',
298+
\ 'func main() {',
299+
\ '',
300+
\ "fmt.Println(\x1fhello)",
301+
\ '}',
302+
\ ], [])
303+
finally
304+
unlet g:go_fmt_command
305+
endtry
306+
endfunction
307+
308+
function! Test_diagnostic_after_fmt_cleared() abort
309+
" craft a file that will be fixed when it is written.
310+
let g:go_fmt_command = 'gofmt'
311+
try
312+
call s:diagnostic_after_write( [
313+
\ 'package main',
314+
\ 'import "fmt"',
315+
\ '',
316+
\ 'func main() {',
317+
\ '',
318+
\ "fmt.Println(\x1fhello)",
319+
\ '}',
320+
\ ], ['hello := "hello, vim-go"'])
321+
finally
322+
unlet g:go_fmt_command
323+
endtry
324+
endfunction
325+
326+
function! Test_diagnostic_after_reload() abort
327+
let l:dir = gotest#write_file('diagnostic/after-reload.go', [
328+
\ 'package main',
329+
\ 'import "fmt"',
330+
\ '',
331+
\ 'func main() {',
332+
\ '',
333+
\ "\tfmt.Println(\x1fhello)",
334+
\ '}',
335+
\ ])
336+
try
337+
call s:check_diagnostics('', 'goDiagnosticError', 'initial')
338+
let l:pos = getcurpos()
339+
edit
340+
call setpos('.', l:pos)
341+
call s:check_diagnostics('', 'goDiagnosticError', 'after-reload')
342+
finally
343+
call delete(l:dir, 'rf')
344+
endtry
345+
endfunction
346+
347+
function! s:diagnostic_after_write(contents, changes) abort
348+
syntax on
349+
350+
let l:dir = gotest#write_file('diagnostic/after-write.go', a:contents)
351+
352+
try
353+
let l:pos = getcurpos()
354+
call s:check_diagnostics('', 'goDiagnosticError', 'initial')
355+
356+
" write a:changes to the previous line and make sure l:actual and
357+
" l:expected are set so that they won't accidentally match on the next
358+
" check.
359+
if len(a:changes) > 0
360+
call append(l:pos[1]-1, a:changes)
361+
let l:actual = 'goDiagnosticError'
362+
let l:expected = ''
363+
else
364+
let l:actual = ''
365+
let l:expected = 'goDiagnosticError'
366+
endif
367+
368+
write
369+
370+
call s:check_diagnostics(l:actual, l:expected, 'after-write')
371+
finally
372+
call delete(l:dir, 'rf')
373+
endtry
374+
endfunction
375+
376+
function! s:check_diagnostics(actual, expected, when)
377+
let l:actual = a:actual
378+
let l:start = reltime()
379+
380+
while l:actual != a:expected && reltimefloat(reltime(l:start)) < 10
381+
" Get the cursor position on each iteration, because the cursor postion
382+
" may change between iterations when go#fmt#GoFmt formats, reloads the
383+
" file, and moves the cursor to try to keep it where the user expects it
384+
" to be when gofmt modifies the files.
385+
let l:pos = getcurpos()
386+
if !has('textprop')
387+
let l:matches = getmatches()
388+
if len(l:matches) == 0
389+
let l:actual = ''
390+
endif
391+
392+
for l:m in l:matches
393+
let l:matchline = l:m.pos1[0]
394+
if len(l:m.pos1) < 2
395+
continue
396+
endif
397+
let l:matchcol = get(l:m.pos1, 1, 1)
398+
if l:pos[1] == l:matchline && l:pos[2] >= l:matchcol && l:pos[2] <= l:matchcol + l:m.pos1[2]
399+
" Ideally, we'd check that the cursor is within the match, but when a
400+
" tab is added on the current line, the cursor position within the
401+
" line will stay constant while the line itself is shifted over by a
402+
" column, so just check the line itself instead of checking a precise
403+
" cursor location.
404+
" if l:pos[1] == l:matchline
405+
let l:actual = l:m.group
406+
break
407+
endif
408+
endfor
409+
410+
sleep 100m
411+
continue
412+
endif
413+
414+
let l:actual = get(prop_list(l:pos[1]), 0, {'type': ''}).type
415+
sleep 100m
416+
endwhile
417+
418+
call assert_equal(a:expected, l:actual, a:when)
419+
endfunction
273420
" restore Vi compatibility settings
274421
let &cpo = s:cpo_save
275422
unlet s:cpo_save

autoload/go/lsp.vim

+11-7
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,11 @@ function! s:newlsp() abort
240240
if !bufexists(l:bufname)
241241
"let l:starttime = reltime()
242242
call bufadd(l:bufname)
243-
"echom printf('added %s (%s)', l:bufname, reltimestr(reltime(l:startime)))
244243
endif
245244

246245
if !bufloaded(l:bufname)
247246
"let l:starttime = reltime()
248247
call bufload(l:bufname)
249-
"echom printf('loaded %s (%s)', l:bufname, reltimestr(reltime(l:starttime)))
250248
endif
251249

252250
for l:diag in l:data.diagnostics
@@ -273,9 +271,11 @@ function! s:newlsp() abort
273271
" version.
274272
let l:lsp = s:lspfactory.get()
275273
let l:version = get(l:lsp.fileVersions, l:fname, 0)
276-
if l:version != 0 && (!has_key(l:data, 'version') || l:data.version == l:version)
277-
call s:highlightMatches(l:errorMatches, l:warningMatches)
278-
endif
274+
" it's tempting to only highlight matches when they are for the
275+
" current version of the buffer, but that causes problems when the
276+
" version number has been updated and the content has not. In such a
277+
" case, the diagnostics may not be sent for later versions.
278+
call s:highlightMatches(l:errorMatches, l:warningMatches)
279279
endif
280280

281281
let self.diagnostics[l:fname] = l:diagnostics
@@ -1269,11 +1269,15 @@ function! s:highlightMatches(errorMatches, warningMatches) abort
12691269
augroup vim-go-diagnostics
12701270
autocmd! * <buffer>
12711271
autocmd BufDelete <buffer> autocmd! vim-go-diagnostics * <buffer=abuf>
1272-
autocmd BufWinEnter <buffer> nested call s:highlightMatches(b:go_diagnostic_matches.errors, b:go_diagnostic_matches.warnings)
1272+
if has('textprop')
1273+
autocmd BufReadPost <buffer> nested call s:highlightMatches(b:go_diagnostic_matches.errors, b:go_diagnostic_matches.warnings)
1274+
else
1275+
autocmd BufWinEnter <buffer> nested call s:highlightMatches(b:go_diagnostic_matches.errors, b:go_diagnostic_matches.warnings)
1276+
endif
12731277
augroup end
12741278
endfunction
12751279

1276-
" ClearDiagnosticsHighlights removes all goDiagnosticError and
1280+
" ClearDiagnosticHighlights removes all goDiagnosticError and
12771281
" goDiagnosticWarning matches.
12781282
function! go#lsp#ClearDiagnosticHighlights() abort
12791283
call go#util#ClearHighlights('goDiagnosticError')

autoload/go/util.vim

+2-2
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ function! go#util#SetEnv(name, value) abort
559559
endfunction
560560

561561
function! go#util#ClearHighlights(group) abort
562-
if exists('*prop_remove')
562+
if has('textprop')
563563
" the property type may not exist when syntax highlighting is not enabled.
564564
if empty(prop_type_get(a:group))
565565
return
@@ -617,7 +617,7 @@ endfunction
617617
" pos should be a list of 3 element lists. The lists should be [line, col,
618618
" length] as used by matchaddpos().
619619
function! go#util#HighlightPositions(group, pos) abort
620-
if exists('*prop_add')
620+
if has('textprop')
621621
for l:pos in a:pos
622622
" use a single line prop by default
623623
let l:prop = {'type': a:group, 'length': l:pos[2]}

autoload/gotest.vim

-7
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ set cpo&vim
1212
" The full path to the created directory is returned, it is the caller's
1313
" responsibility to clean that up!
1414
fun! gotest#write_file(path, contents) abort
15-
if go#util#has_job()
16-
call go#lsp#CleanWorkspaces()
17-
endif
1815
let l:dir = go#util#tempdir("vim-go-test/testrun/")
1916
let $GOPATH .= ':' . l:dir
2017
let l:full_path = l:dir . '/src/' . a:path
@@ -23,10 +20,6 @@ fun! gotest#write_file(path, contents) abort
2320
call writefile(a:contents, l:full_path)
2421
exe 'cd ' . l:dir . '/src'
2522

26-
if go#util#has_job()
27-
call go#lsp#AddWorkspaceDirectory(fnamemodify(l:full_path, ':p:h'))
28-
endif
29-
3023
silent exe 'e! ' . a:path
3124

3225
" Set cursor.

ftplugin/go.vim

+23-19
Original file line numberDiff line numberDiff line change
@@ -99,25 +99,29 @@ augroup vim-go-buffer
9999
autocmd BufWritePre <buffer> call go#auto#fmt_autosave()
100100
autocmd BufWritePost <buffer> call go#auto#metalinter_autosave()
101101

102-
"TODO(bc): how to clear sameids and diagnostics when a non-go buffer is
103-
" loaded into a window and the previously loaded buffer is still loaded in
104-
" another window?
105-
106-
" clear SameIds when the buffer is unloaded from its last window so that
107-
" loading another buffer (especially of a different filetype) in the same
108-
" window doesn't highlight the most recently matched identifier's positions.
109-
autocmd BufWinLeave <buffer> call go#guru#ClearSameIds()
110-
" clear SameIds when a new buffer is loaded in the window so that the
111-
" previous buffer's highlighting isn't used.
112-
autocmd BufWinEnter <buffer> call go#guru#ClearSameIds()
113-
114-
" clear diagnostics when the buffer is unloaded from its last window so that
115-
" loading another buffer (especially of a different filetype) in the same
116-
" window doesn't highlight th previously loaded buffer's diagnostics.
117-
autocmd BufWinLeave <buffer> call go#lsp#ClearDiagnosticHighlights()
118-
" clear diagnostics when a new buffer is loaded in the window so that the
119-
" previous buffer's diagnostics aren't used.
120-
autocmd BufWinEnter <buffer> call go#lsp#ClearDiagnosticHighlights()
102+
if !has('textprop')
103+
"TODO(bc): how to clear sameids and diagnostics when a non-go buffer is
104+
" loaded into a window and the previously loaded buffer is still loaded in
105+
" another window?
106+
107+
" TODO(bc): only clear when the new buffer isn't the old buffer
108+
109+
" clear SameIds when the buffer is unloaded from its last window so that
110+
" loading another buffer (especially of a different filetype) in the same
111+
" window doesn't highlight the most recently matched identifier's positions.
112+
autocmd BufWinLeave <buffer> call go#guru#ClearSameIds()
113+
" clear SameIds when a new buffer is loaded in the window so that the
114+
" previous buffer's highlighting isn't used.
115+
autocmd BufWinEnter <buffer> call go#guru#ClearSameIds()
116+
117+
" clear diagnostics when the buffer is unloaded from its last window so that
118+
" loading another buffer (especially of a different filetype) in the same
119+
" window doesn't highlight the previously loaded buffer's diagnostics.
120+
autocmd BufWinLeave <buffer> call go#lsp#ClearDiagnosticHighlights()
121+
" clear diagnostics when a new buffer is loaded in the window so that the
122+
" previous buffer's diagnostics aren't used.
123+
"autocmd BufWinEnter <buffer> call go#lsp#ClearDiagnosticHighlights()
124+
endif
121125

122126
autocmd BufEnter <buffer>
123127
\ if go#config#AutodetectGopath() && !exists('b:old_gopath')

plugin/go.vim

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ call s:checkVersion()
3838
" these packages are used by vim-go and can be automatically installed if
3939
" needed by the user with GoInstallBinaries.
4040

41-
" NOTE(bc): varying the binary name and the tail of the import path (e.g.
42-
" gocode-gomod) does not yet work in module aware mode.
41+
" NOTE(bc): varying the binary name and the tail of the import path does not yet work in module aware mode.
4342
let s:packages = {
4443
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt@master'],
4544
\ 'dlv': ['github.com/go-delve/delve/cmd/dlv@master'],

syntax/go.vim

+1-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ function! s:hi()
399399
" filetype plugin on, the highlight groups won't be defined when
400400
" ftplugin/go.vim is executed when the first go file is opened.
401401
" See https://github.com/fatih/vim-go/issues/2658.
402-
if exists('*prop_type_add')
402+
if has('textprop')
403403
if empty(prop_type_get('goSameId'))
404404
call prop_type_add('goSameId', {'highlight': 'goSameId'})
405405
endif

0 commit comments

Comments
 (0)