Dispatch textDocument/didChange after rename (2) (#4049)

* Dispatch textDocument/didChange after rename

Previously whenever we renamed a symbol that was referenced from other
files we'd just edit those files in the background, and the LSP wouldn't
know about these changes. If we tried to rename the same symbol again,
the renaming would fail. In some scenarios, the operation would just be
wrong. Here is an attempt to fix this issue.

I also noticed another bug when using Go with `gopls` LSP and the `gofmt`
fixer. Whenever the file was saved, the `gofmt` would run and reformat
the file. But it seems there was some kind of a race condition so I
disabled saving for now, and all of the modified files will be unsaved,
so the user should call `:wa` to save them. I personally like this even
better because I can inspect exactly what changes happened, and I
instantly see them in the other opened buffers, which was previously not
the case.

Fixes #3343, #3642, #3781.

* Address PR comments

* Remove mode tests in corner case tests

* Address PR comments

* Save after ALERename and ALEOrganizeImports

Also provide options to disable automatic saving, as well as instructions to
enable `set hidden` before doing that.

* Fix broken test

* Save only when !&hidden

* Update doc

* Update doc

* Add silent
This commit is contained in:
Jerko Steiner
2022-02-08 12:07:39 +01:00
committed by GitHub
parent 8b1ea33cc0
commit 4a4516e3bf
9 changed files with 326 additions and 178 deletions

View File

@@ -16,13 +16,12 @@ endfunction
function! ale#code_action#HandleCodeAction(code_action, options) abort
let l:current_buffer = bufnr('')
let l:changes = a:code_action.changes
let l:should_save = get(a:options, 'should_save')
for l:file_code_edit in l:changes
call ale#code_action#ApplyChanges(
\ l:file_code_edit.fileName,
\ l:file_code_edit.textChanges,
\ l:should_save,
\ a:options,
\)
endfor
endfunction
@@ -63,29 +62,29 @@ function! s:ChangeCmp(left, right) abort
return 0
endfunction
function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:current_buffer = bufnr('')
function! ale#code_action#ApplyChanges(filename, changes, options) abort
let l:should_save = get(a:options, 'should_save')
let l:conn_id = get(a:options, 'conn_id')
let l:orig_buffer = bufnr('')
" The buffer is used to determine the fileformat, if available.
let l:buffer = bufnr(a:filename)
let l:is_current_buffer = l:buffer > 0 && l:buffer == l:current_buffer
if l:buffer > 0
let l:lines = getbufline(l:buffer, 1, '$')
" Add empty line if there's trailing newline, like readfile() does.
if getbufvar(l:buffer, '&eol')
let l:lines += ['']
endif
else
let l:lines = readfile(a:filename, 'b')
if l:buffer != l:orig_buffer
call ale#util#Execute('silent edit ' . a:filename)
let l:buffer = bufnr('')
endif
if l:is_current_buffer
let l:pos = getpos('.')[1:2]
else
let l:pos = [1, 1]
let l:lines = getbufline(l:buffer, 1, '$')
" Add empty line if there's trailing newline, like readfile() does.
if getbufvar(l:buffer, '&eol')
let l:lines += ['']
endif
let l:pos = getpos('.')[1:2]
" Changes have to be sorted so we apply them from bottom-to-top
for l:code_edit in reverse(sort(copy(a:changes), function('s:ChangeCmp')))
let l:line = l:code_edit.start.line
@@ -155,46 +154,25 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
endif
endfor
if l:buffer > 0
" Make sure ale#util#{Writefile,SetBufferContents} add trailing
" newline if and only if it should be added.
if l:lines[-1] is# '' && getbufvar(l:buffer, '&eol')
call remove(l:lines, -1)
else
call setbufvar(l:buffer, '&eol', 0)
endif
elseif exists('+fixeol') && &fixeol && l:lines[-1] is# ''
" Not in buffer, ale#util#Writefile can't check &eol and always adds
" newline if &fixeol: remove to prevent double trailing newline.
" Make sure to add a trailing newline if and only if it should be added.
if l:lines[-1] is# '' && getbufvar(l:buffer, '&eol')
call remove(l:lines, -1)
endif
if a:should_save || l:buffer < 0
call ale#util#Writefile(l:buffer, l:lines, a:filename)
else
call ale#util#SetBufferContents(l:buffer, l:lines)
call setbufvar(l:buffer, '&eol', 0)
endif
if l:is_current_buffer
if a:should_save
call ale#util#Execute(':e!')
endif
call ale#util#SetBufferContents(l:buffer, l:lines)
call setpos('.', [0, l:pos[0], l:pos[1], 0])
call ale#lsp#NotifyForChanges(l:conn_id, l:buffer)
if l:should_save
call ale#util#Execute('silent w!')
endif
if a:should_save && l:buffer > 0 && !l:is_current_buffer
" Set up a one-time use event that will delete itself to reload the
" buffer next time it's entered to view the changes made to it.
execute 'augroup ALECodeActionReloadGroup' . l:buffer
autocmd!
call setpos('.', [0, l:pos[0], l:pos[1], 0])
execute printf(
\ 'autocmd BufEnter <buffer=%d>'
\ . ' call ale#code_action#ReloadBuffer()',
\ l:buffer
\)
augroup END
if l:orig_buffer != l:buffer && bufexists(l:orig_buffer)
call ale#util#Execute('silent buf ' . string(l:orig_buffer))
endif
endfunction

View File

@@ -1,6 +1,6 @@
" Author: Jerko Steiner <jerko.steiner@gmail.com>
" Description: Organize imports support for tsserver
"
function! ale#organize_imports#HandleTSServerResponse(conn_id, response) abort
if get(a:response, 'command', '') isnot# 'organizeImports'
return
@@ -17,7 +17,10 @@ function! ale#organize_imports#HandleTSServerResponse(conn_id, response) abort
\ 'description': 'Organize Imports',
\ 'changes': l:file_code_edits,
\ },
\ {}
\ {
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endfunction

View File

@@ -84,7 +84,8 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort
\ 'changes': l:changes,
\ },
\ {
\ 'should_save': 1,
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endfunction
@@ -116,7 +117,8 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
\ 'changes': l:changes,
\ },
\ {
\ 'should_save': 1,
\ 'conn_id': a:conn_id,
\ 'should_save': !&hidden,
\ },
\)
endif