2019-09-12 20:53:23 +00:00
|
|
|
" Author: Jerko Steiner <jerko.steiner@gmail.com>
|
|
|
|
" Description: Code action support for LSP / tsserver
|
|
|
|
|
2020-11-21 19:00:53 +00:00
|
|
|
function! ale#code_action#ReloadBuffer() abort
|
|
|
|
let l:buffer = bufnr('')
|
|
|
|
|
|
|
|
execute 'augroup ALECodeActionReloadGroup' . l:buffer
|
|
|
|
autocmd!
|
|
|
|
augroup END
|
|
|
|
|
|
|
|
silent! execute 'augroup! ALECodeActionReloadGroup' . l:buffer
|
|
|
|
|
|
|
|
call ale#util#Execute(':e!')
|
|
|
|
endfunction
|
|
|
|
|
2020-10-15 20:56:21 +00:00
|
|
|
function! ale#code_action#HandleCodeAction(code_action, options) abort
|
2019-09-12 20:53:23 +00:00
|
|
|
let l:current_buffer = bufnr('')
|
|
|
|
let l:changes = a:code_action.changes
|
|
|
|
|
|
|
|
for l:file_code_edit in l:changes
|
|
|
|
call ale#code_action#ApplyChanges(
|
2020-10-15 20:56:21 +00:00
|
|
|
\ l:file_code_edit.fileName,
|
|
|
|
\ l:file_code_edit.textChanges,
|
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
2022-02-08 11:07:39 +00:00
|
|
|
\ a:options,
|
2020-10-15 20:56:21 +00:00
|
|
|
\)
|
2019-09-12 20:53:23 +00:00
|
|
|
endfor
|
|
|
|
endfunction
|
|
|
|
|
2020-09-04 08:37:33 +00:00
|
|
|
function! s:ChangeCmp(left, right) abort
|
|
|
|
if a:left.start.line < a:right.start.line
|
2020-11-14 10:17:02 +00:00
|
|
|
return -1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.start.line > a:right.start.line
|
2020-11-14 10:17:02 +00:00
|
|
|
return 1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.start.offset < a:right.start.offset
|
2020-11-14 10:17:02 +00:00
|
|
|
return -1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.start.offset > a:right.start.offset
|
2020-11-14 10:17:02 +00:00
|
|
|
return 1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.end.line < a:right.end.line
|
2020-11-14 10:17:02 +00:00
|
|
|
return -1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.end.line > a:right.end.line
|
2020-11-14 10:17:02 +00:00
|
|
|
return 1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.end.offset < a:right.end.offset
|
2020-11-14 10:17:02 +00:00
|
|
|
return -1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
if a:left.end.offset > a:right.end.offset
|
2020-11-14 10:17:02 +00:00
|
|
|
return 1
|
2020-09-04 08:37:33 +00:00
|
|
|
endif
|
|
|
|
|
|
|
|
return 0
|
|
|
|
endfunction
|
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
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('')
|
|
|
|
|
2019-09-12 20:53:23 +00:00
|
|
|
" The buffer is used to determine the fileformat, if available.
|
|
|
|
let l:buffer = bufnr(a:filename)
|
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
if l:buffer != l:orig_buffer
|
|
|
|
call ale#util#Execute('silent edit ' . a:filename)
|
|
|
|
let l:buffer = bufnr('')
|
2019-09-12 20:53:23 +00:00
|
|
|
endif
|
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
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 += ['']
|
2019-09-12 20:53:23 +00:00
|
|
|
endif
|
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
let l:pos = getpos('.')[1:2]
|
|
|
|
|
2020-11-14 10:15:17 +00:00
|
|
|
" Changes have to be sorted so we apply them from bottom-to-top
|
2020-11-14 10:17:02 +00:00
|
|
|
for l:code_edit in reverse(sort(copy(a:changes), function('s:ChangeCmp')))
|
2020-11-14 10:15:17 +00:00
|
|
|
let l:line = l:code_edit.start.line
|
|
|
|
let l:column = l:code_edit.start.offset
|
|
|
|
let l:end_line = l:code_edit.end.line
|
|
|
|
let l:end_column = l:code_edit.end.offset
|
2019-09-12 20:53:23 +00:00
|
|
|
let l:text = l:code_edit.newText
|
|
|
|
|
|
|
|
let l:insertions = split(l:text, '\n', 1)
|
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Fix invalid columns
|
|
|
|
let l:column = l:column > 0 ? l:column : 1
|
|
|
|
let l:end_column = l:end_column > 0 ? l:end_column : 1
|
2019-09-12 20:53:23 +00:00
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Clamp start to BOF
|
|
|
|
if l:line < 1
|
|
|
|
let [l:line, l:column] = [1, 1]
|
2020-11-14 10:15:17 +00:00
|
|
|
endif
|
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Clamp start to EOF
|
|
|
|
if l:line > len(l:lines) || l:line == len(l:lines) && l:column > len(l:lines[-1]) + 1
|
|
|
|
let [l:line, l:column] = [len(l:lines), len(l:lines[-1]) + 1]
|
|
|
|
" Special case when start is after EOL
|
|
|
|
elseif l:line < len(l:lines) && l:column > len(l:lines[l:line - 1]) + 1
|
|
|
|
let [l:line, l:column] = [l:line + 1, 1]
|
2019-09-12 20:53:23 +00:00
|
|
|
endif
|
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Adjust end: clamp if invalid and/or adjust if we moved start
|
|
|
|
if l:end_line < l:line || l:end_line == l:line && l:end_column < l:column
|
|
|
|
let [l:end_line, l:end_column] = [l:line, l:column]
|
|
|
|
endif
|
2020-09-09 15:44:09 +00:00
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Clamp end to EOF
|
|
|
|
if l:end_line > len(l:lines) || l:end_line == len(l:lines) && l:end_column > len(l:lines[-1]) + 1
|
|
|
|
let [l:end_line, l:end_column] = [len(l:lines), len(l:lines[-1]) + 1]
|
|
|
|
" Special case when end is after EOL
|
|
|
|
elseif l:end_line < len(l:lines) && l:end_column > len(l:lines[l:end_line - 1]) + 1
|
|
|
|
let [l:end_line, l:end_column] = [l:end_line + 1, 1]
|
2020-09-09 15:30:41 +00:00
|
|
|
endif
|
2019-09-12 20:53:23 +00:00
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Careful, [:-1] is not an empty list
|
|
|
|
let l:start = l:line is 1 ? [] : l:lines[: l:line - 2]
|
|
|
|
let l:middle = l:column is 1 ? [''] : [l:lines[l:line - 1][: l:column - 2]]
|
|
|
|
|
|
|
|
let l:middle[-1] .= l:insertions[0]
|
|
|
|
let l:middle += l:insertions[1:]
|
|
|
|
let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :]
|
|
|
|
|
|
|
|
let l:end_line_len = len(l:lines[l:end_line - 1])
|
2019-09-12 20:53:23 +00:00
|
|
|
let l:lines_before_change = len(l:lines)
|
|
|
|
let l:lines = l:start + l:middle + l:lines[l:end_line :]
|
|
|
|
|
|
|
|
let l:current_line_offset = len(l:lines) - l:lines_before_change
|
|
|
|
let l:column_offset = len(l:middle[-1]) - l:end_line_len
|
|
|
|
|
2021-02-20 16:16:47 +00:00
|
|
|
" Keep cursor where it was (if outside of changes) or move it after
|
|
|
|
" the changed text (if inside), but don't touch it when the change
|
|
|
|
" spans the entire buffer, in which case we have no clue and it's
|
|
|
|
" better to not do anything.
|
|
|
|
if l:line isnot 1 || l:column isnot 1
|
|
|
|
\|| l:end_line < l:lines_before_change
|
|
|
|
\|| l:end_line == l:lines_before_change && l:end_column <= l:end_line_len
|
|
|
|
let l:pos = s:UpdateCursor(l:pos,
|
|
|
|
\ [l:line, l:column],
|
|
|
|
\ [l:end_line, l:end_column],
|
|
|
|
\ [l:current_line_offset, l:column_offset])
|
|
|
|
endif
|
2019-09-12 20:53:23 +00:00
|
|
|
endfor
|
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
" Make sure to add a trailing newline if and only if it should be added.
|
|
|
|
if l:lines[-1] is# '' && getbufvar(l:buffer, '&eol')
|
2019-09-12 20:53:23 +00:00
|
|
|
call remove(l:lines, -1)
|
2019-10-10 01:42:41 +00:00
|
|
|
else
|
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
2022-02-08 11:07:39 +00:00
|
|
|
call setbufvar(l:buffer, '&eol', 0)
|
2019-10-10 01:42:41 +00:00
|
|
|
endif
|
2019-09-12 20:53:23 +00:00
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
call ale#util#SetBufferContents(l:buffer, l:lines)
|
2019-10-10 01:42:41 +00:00
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
call ale#lsp#NotifyForChanges(l:conn_id, l:buffer)
|
|
|
|
|
|
|
|
if l:should_save
|
|
|
|
call ale#util#Execute('silent w!')
|
2019-09-12 20:53:23 +00:00
|
|
|
endif
|
2020-11-21 19:00:53 +00:00
|
|
|
|
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
2022-02-08 11:07:39 +00:00
|
|
|
call setpos('.', [0, l:pos[0], l:pos[1], 0])
|
|
|
|
|
|
|
|
if l:orig_buffer != l:buffer && bufexists(l:orig_buffer)
|
|
|
|
call ale#util#Execute('silent buf ' . string(l:orig_buffer))
|
2020-11-21 19:00:53 +00:00
|
|
|
endif
|
2019-09-12 20:53:23 +00:00
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:UpdateCursor(cursor, start, end, offset) abort
|
|
|
|
let l:cur_line = a:cursor[0]
|
|
|
|
let l:cur_column = a:cursor[1]
|
|
|
|
let l:line = a:start[0]
|
|
|
|
let l:column = a:start[1]
|
|
|
|
let l:end_line = a:end[0]
|
|
|
|
let l:end_column = a:end[1]
|
|
|
|
let l:line_offset = a:offset[0]
|
|
|
|
let l:column_offset = a:offset[1]
|
|
|
|
|
|
|
|
if l:end_line < l:cur_line
|
|
|
|
" both start and end lines are before the cursor. only line offset
|
|
|
|
" needs to be updated
|
|
|
|
let l:cur_line += l:line_offset
|
|
|
|
elseif l:end_line == l:cur_line
|
|
|
|
" end line is at the same location as cursor, which means
|
|
|
|
" l:line <= l:cur_line
|
|
|
|
if l:line < l:cur_line || l:column <= l:cur_column
|
|
|
|
" updates are happening either before or around the cursor
|
|
|
|
if l:end_column < l:cur_column
|
|
|
|
" updates are happening before the cursor, update the
|
|
|
|
" column offset for cursor
|
|
|
|
let l:cur_line += l:line_offset
|
|
|
|
let l:cur_column += l:column_offset
|
|
|
|
else
|
|
|
|
" updates are happening around the cursor, move the cursor
|
|
|
|
" to the end of the changes
|
|
|
|
let l:cur_line += l:line_offset
|
|
|
|
let l:cur_column = l:end_column + l:column_offset
|
|
|
|
endif
|
|
|
|
" else is not necessary, it means modifications are happening
|
|
|
|
" after the cursor so no cursor updates need to be done
|
|
|
|
endif
|
|
|
|
else
|
|
|
|
" end line is after the cursor
|
|
|
|
if l:line < l:cur_line || l:line == l:cur_line && l:column <= l:cur_column
|
|
|
|
" changes are happening around the cursor, move the cursor
|
|
|
|
" to the end of the changes
|
|
|
|
let l:cur_line = l:end_line + l:line_offset
|
|
|
|
let l:cur_column = l:end_column + l:column_offset
|
|
|
|
" else is not necesary, it means modifications are happening
|
|
|
|
" after the cursor so no cursor updates need to be done
|
|
|
|
endif
|
|
|
|
endif
|
|
|
|
|
|
|
|
return [l:cur_line, l:cur_column]
|
|
|
|
endfunction
|
2020-11-14 10:15:17 +00:00
|
|
|
|
|
|
|
function! ale#code_action#GetChanges(workspace_edit) abort
|
2021-05-04 07:44:28 +00:00
|
|
|
if a:workspace_edit is v:null
|
|
|
|
return {}
|
|
|
|
endif
|
|
|
|
|
2020-11-14 10:15:17 +00:00
|
|
|
let l:changes = {}
|
|
|
|
|
|
|
|
if has_key(a:workspace_edit, 'changes') && !empty(a:workspace_edit.changes)
|
|
|
|
return a:workspace_edit.changes
|
|
|
|
elseif has_key(a:workspace_edit, 'documentChanges')
|
|
|
|
let l:document_changes = []
|
|
|
|
|
|
|
|
if type(a:workspace_edit.documentChanges) is v:t_dict
|
|
|
|
\ && has_key(a:workspace_edit.documentChanges, 'edits')
|
|
|
|
call add(l:document_changes, a:workspace_edit.documentChanges)
|
|
|
|
elseif type(a:workspace_edit.documentChanges) is v:t_list
|
|
|
|
let l:document_changes = a:workspace_edit.documentChanges
|
|
|
|
endif
|
|
|
|
|
|
|
|
for l:text_document_edit in l:document_changes
|
|
|
|
let l:filename = l:text_document_edit.textDocument.uri
|
|
|
|
let l:edits = l:text_document_edit.edits
|
|
|
|
let l:changes[l:filename] = l:edits
|
|
|
|
endfor
|
|
|
|
endif
|
|
|
|
|
|
|
|
return l:changes
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! ale#code_action#BuildChangesList(changes_map) abort
|
|
|
|
let l:changes = []
|
|
|
|
|
|
|
|
for l:file_name in keys(a:changes_map)
|
|
|
|
let l:text_edits = a:changes_map[l:file_name]
|
|
|
|
let l:text_changes = []
|
|
|
|
|
|
|
|
for l:edit in l:text_edits
|
|
|
|
let l:range = l:edit.range
|
|
|
|
let l:new_text = l:edit.newText
|
|
|
|
|
|
|
|
call add(l:text_changes, {
|
|
|
|
\ 'start': {
|
|
|
|
\ 'line': l:range.start.line + 1,
|
|
|
|
\ 'offset': l:range.start.character + 1,
|
|
|
|
\ },
|
|
|
|
\ 'end': {
|
|
|
|
\ 'line': l:range.end.line + 1,
|
|
|
|
\ 'offset': l:range.end.character + 1,
|
|
|
|
\ },
|
|
|
|
\ 'newText': l:new_text,
|
|
|
|
\})
|
|
|
|
endfor
|
|
|
|
|
|
|
|
call add(l:changes, {
|
2022-03-04 19:03:27 +00:00
|
|
|
\ 'fileName': ale#util#ToResource(l:file_name),
|
2020-11-14 10:15:17 +00:00
|
|
|
\ 'textChanges': l:text_changes,
|
|
|
|
\})
|
|
|
|
endfor
|
|
|
|
|
|
|
|
return l:changes
|
|
|
|
endfunction
|
2020-11-21 01:18:27 +00:00
|
|
|
|
|
|
|
function! s:EscapeMenuName(text) abort
|
|
|
|
return substitute(a:text, '\\\| \|\.\|&', '\\\0', 'g')
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:UpdateMenu(data, menu_items) abort
|
|
|
|
silent! aunmenu PopUp.Refactor\.\.\.
|
|
|
|
|
|
|
|
if empty(a:data)
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
|
|
|
|
for [l:type, l:item] in a:menu_items
|
|
|
|
let l:name = l:type is# 'tsserver' ? l:item.name : l:item.title
|
|
|
|
let l:func_name = l:type is# 'tsserver'
|
|
|
|
\ ? 'ale#codefix#ApplyTSServerCodeAction'
|
|
|
|
\ : 'ale#codefix#ApplyLSPCodeAction'
|
|
|
|
|
|
|
|
execute printf(
|
|
|
|
\ 'anoremenu <silent> PopUp.&Refactor\.\.\..%s'
|
|
|
|
\ . ' :call %s(%s, %s)<CR>',
|
|
|
|
\ s:EscapeMenuName(l:name),
|
|
|
|
\ l:func_name,
|
|
|
|
\ string(a:data),
|
|
|
|
\ string(l:item),
|
|
|
|
\)
|
|
|
|
endfor
|
|
|
|
|
|
|
|
if empty(a:menu_items)
|
|
|
|
silent! anoremenu PopUp.Refactor\.\.\..(None) :silent
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:GetCodeActions(linter, options) abort
|
|
|
|
let l:buffer = bufnr('')
|
|
|
|
let [l:line, l:column] = getpos('.')[1:2]
|
|
|
|
let l:column = min([l:column, len(getline(l:line))])
|
|
|
|
|
|
|
|
let l:location = {
|
|
|
|
\ 'buffer': l:buffer,
|
|
|
|
\ 'line': l:line,
|
|
|
|
\ 'column': l:column,
|
|
|
|
\ 'end_line': l:line,
|
|
|
|
\ 'end_column': l:column,
|
|
|
|
\}
|
|
|
|
let l:Callback = function('s:OnReady', [l:location, a:options])
|
|
|
|
call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! ale#code_action#GetCodeActions(options) abort
|
|
|
|
silent! aunmenu PopUp.Rename
|
|
|
|
silent! aunmenu PopUp.Refactor\.\.\.
|
|
|
|
|
|
|
|
" Only display the menu items if there's an LSP server.
|
|
|
|
let l:has_lsp = 0
|
|
|
|
|
|
|
|
for l:linter in ale#linter#Get(&filetype)
|
|
|
|
if !empty(l:linter.lsp)
|
|
|
|
let l:has_lsp = 1
|
|
|
|
|
|
|
|
break
|
|
|
|
endif
|
|
|
|
endfor
|
|
|
|
|
|
|
|
if l:has_lsp
|
|
|
|
if !empty(expand('<cword>'))
|
|
|
|
silent! anoremenu <silent> PopUp.Rename :ALERename<CR>
|
|
|
|
endif
|
|
|
|
|
|
|
|
silent! anoremenu <silent> PopUp.Refactor\.\.\..(None) :silent<CR>
|
|
|
|
|
|
|
|
call ale#codefix#Execute(
|
|
|
|
\ mode() is# 'v' || mode() is# "\<C-V>",
|
|
|
|
\ function('s:UpdateMenu')
|
|
|
|
\)
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! s:Setup(enabled) abort
|
|
|
|
augroup ALECodeActionsGroup
|
|
|
|
autocmd!
|
|
|
|
|
|
|
|
if a:enabled
|
|
|
|
autocmd MenuPopup * :call ale#code_action#GetCodeActions({})
|
|
|
|
endif
|
|
|
|
augroup END
|
|
|
|
|
|
|
|
if !a:enabled
|
|
|
|
silent! augroup! ALECodeActionsGroup
|
|
|
|
|
|
|
|
silent! aunmenu PopUp.Rename
|
|
|
|
silent! aunmenu PopUp.Refactor\.\.\.
|
|
|
|
endif
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! ale#code_action#EnablePopUpMenu() abort
|
|
|
|
call s:Setup(1)
|
|
|
|
endfunction
|
|
|
|
|
|
|
|
function! ale#code_action#DisablePopUpMenu() abort
|
|
|
|
call s:Setup(0)
|
|
|
|
endfunction
|