Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for LSP/tsserver Code Actions #3437

Merged
merged 26 commits into from
Nov 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions ale_linters/python/jedils.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
" Author: Dalius Dobravolskas <dalius.dobravolskas@gmail.com>
" Description: https://github.com/pappasam/jedi-language-server

call ale#Set('python_jedils_executable', 'jedi-language-server')
call ale#Set('python_jedils_use_global', get(g:, 'ale_use_global_executables', 0))
call ale#Set('python_jedils_auto_pipenv', 0)

function! ale_linters#python#jedils#GetExecutable(buffer) abort
if (ale#Var(a:buffer, 'python_auto_pipenv') || ale#Var(a:buffer, 'python_jedils_auto_pipenv'))
\ && ale#python#PipenvPresent(a:buffer)
return 'pipenv'
endif

return ale#python#FindExecutable(a:buffer, 'python_jedils', ['jedi-language-server'])
endfunction

function! ale_linters#python#jedils#GetCommand(buffer) abort
let l:executable = ale_linters#python#jedils#GetExecutable(a:buffer)

let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run jedi-language-server'
\ : ''

return ale#Escape(l:executable) . l:exec_args
endfunction

call ale#linter#Define('python', {
\ 'name': 'jedils',
\ 'lsp': 'stdio',
\ 'executable': function('ale_linters#python#jedils#GetExecutable'),
\ 'command': function('ale_linters#python#jedils#GetCommand'),
\ 'project_root': function('ale#python#FindProjectRoot'),
\ 'completion_filter': 'ale#completion#python#CompletionItemFilter',
\})
106 changes: 77 additions & 29 deletions autoload/ale/code_action.vim
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,35 @@ endfunction

function! s:ChangeCmp(left, right) abort
if a:left.start.line < a:right.start.line
return -1
return 1
endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes more sense for the cmp function to sort forwards. Apply reverse() to reverse the List below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.


if a:left.start.line > a:right.start.line
return 1
return -1
endif

if a:left.start.offset < a:right.start.offset
return -1
return 1
endif

if a:left.start.offset > a:right.start.offset
return 1
return -1
endif

if a:left.end.line < a:right.end.line
return -1
return 1
endif

if a:left.end.line > a:right.end.line
return 1
return -1
endif

if a:left.end.offset < a:right.end.offset
return -1
return 1
endif

if a:left.end.offset > a:right.end.offset
return 1
return -1
endif

return 0
Expand All @@ -85,29 +85,14 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:pos = [1, 1]
endif

" We have to keep track of how many lines we have added, and offset
" changes accordingly.
let l:line_offset = 0
let l:column_offset = 0
let l:last_end_line = 0

" Changes have to be sorted so we apply them from top-to-bottom.
" Changes have to be sorted so we apply them from bottom-to-top
for l:code_edit in sort(copy(a:changes), function('s:ChangeCmp'))
if l:code_edit.start.line isnot l:last_end_line
let l:column_offset = 0
endif

let l:line = l:code_edit.start.line + l:line_offset
let l:column = l:code_edit.start.offset + l:column_offset
let l:end_line = l:code_edit.end.line + l:line_offset
let l:end_column = l:code_edit.end.offset + l:column_offset
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
let l:text = l:code_edit.newText

let l:cur_line = l:pos[0]
let l:cur_column = l:pos[1]

let l:last_end_line = l:end_line

" Adjust the ends according to previous edits.
if l:end_line > len(l:lines)
let l:end_line_len = 0
Expand All @@ -125,6 +110,12 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
let l:start = l:lines[: l:line - 2]
endif

" Special case when text must be added after new line
if l:column > len(l:lines[l:line - 1])
call extend(l:start, [l:lines[l:line - 1]])
let l:column = 1
endif

if l:column is 1
" We need to handle column 1 specially, because we can't slice an
" empty string ending on index 0.
Expand All @@ -140,7 +131,6 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
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:line_offset += l:current_line_offset
let l:column_offset = len(l:middle[-1]) - l:end_line_len

let l:pos = s:UpdateCursor(l:pos,
Expand Down Expand Up @@ -215,3 +205,61 @@ function! s:UpdateCursor(cursor, start, end, offset) abort

return [l:cur_line, l:cur_column]
endfunction

function! ale#code_action#GetChanges(workspace_edit) abort
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, {
\ 'fileName': ale#path#FromURI(l:file_name),
\ 'textChanges': l:text_changes,
\})
endfor

return l:changes
endfunction
Loading