Close #1466 - Add GVIM refactor menu support
Code actions and ALERename now appear in the right click context menu for GVim by default.
This commit is contained in:
parent
48fe0dd4f6
commit
7c04ee5c20
5 changed files with 398 additions and 217 deletions
|
@ -263,3 +263,105 @@ function! ale#code_action#BuildChangesList(changes_map) abort
|
|||
|
||||
return l:changes
|
||||
endfunction
|
||||
|
||||
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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
" Author: Dalius Dobravolskas <dalius.dobravolskas@gmail.com>
|
||||
" Description: Code Fix support for tsserver
|
||||
" Description: Code Fix support for tsserver and LSP servers
|
||||
|
||||
let s:codefix_map = {}
|
||||
|
||||
|
@ -21,23 +21,65 @@ function! s:message(message) abort
|
|||
call ale#util#Execute('echom ' . string(a:message))
|
||||
endfunction
|
||||
|
||||
function! ale#codefix#ApplyTSServerCodeAction(data, item) abort
|
||||
if has_key(a:item, 'changes')
|
||||
let l:changes = a:item.changes
|
||||
|
||||
call ale#code_action#HandleCodeAction(
|
||||
\ {
|
||||
\ 'description': 'codefix',
|
||||
\ 'changes': l:changes,
|
||||
\ },
|
||||
\ {},
|
||||
\)
|
||||
else
|
||||
let l:message = ale#lsp#tsserver_message#GetEditsForRefactor(
|
||||
\ a:data.buffer,
|
||||
\ a:data.line,
|
||||
\ a:data.column,
|
||||
\ a:data.end_line,
|
||||
\ a:data.end_column,
|
||||
\ a:item.id[0],
|
||||
\ a:item.id[1],
|
||||
\)
|
||||
|
||||
let l:request_id = ale#lsp#Send(a:data.connection_id, l:message)
|
||||
|
||||
let s:codefix_map[l:request_id] = a:data
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
||||
if !has_key(a:response, 'request_seq')
|
||||
\ || !has_key(s:codefix_map, a:response.request_seq)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:location = remove(s:codefix_map, a:response.request_seq)
|
||||
let l:data = remove(s:codefix_map, a:response.request_seq)
|
||||
let l:MenuCallback = get(l:data, 'menu_callback', v:null)
|
||||
|
||||
if get(a:response, 'command', '') is# 'getCodeFixes'
|
||||
if get(a:response, 'success', v:false) is v:false
|
||||
\&& l:MenuCallback is v:null
|
||||
let l:message = get(a:response, 'message', 'unknown')
|
||||
call s:message('Error while getting code fixes. Reason: ' . l:message)
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if len(a:response.body) == 0
|
||||
let l:result = get(a:response, 'body', [])
|
||||
call filter(l:result, 'has_key(v:val, ''changes'')')
|
||||
|
||||
if l:MenuCallback isnot v:null
|
||||
call l:MenuCallback(
|
||||
\ l:data,
|
||||
\ map(copy(l:result), '[''tsserver'', v:val]')
|
||||
\)
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if len(l:result) == 0
|
||||
call s:message('No code fixes available.')
|
||||
|
||||
return
|
||||
|
@ -45,14 +87,15 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
|
||||
let l:code_fix_to_apply = 0
|
||||
|
||||
if len(a:response.body) == 1
|
||||
if len(l:result) == 1
|
||||
let l:code_fix_to_apply = 1
|
||||
else
|
||||
let l:codefix_no = 1
|
||||
let l:codefixstring = "Code Fixes:\n"
|
||||
|
||||
for l:codefix in a:response.body
|
||||
let l:codefixstring .= l:codefix_no . ') ' . l:codefix.description . "\n"
|
||||
for l:codefix in l:result
|
||||
let l:codefixstring .= l:codefix_no . ') '
|
||||
\ . l:codefix.description . "\n"
|
||||
let l:codefix_no += 1
|
||||
endfor
|
||||
|
||||
|
@ -66,21 +109,22 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
endif
|
||||
endif
|
||||
|
||||
let l:changes = a:response.body[l:code_fix_to_apply - 1].changes
|
||||
|
||||
call ale#code_action#HandleCodeAction({
|
||||
\ 'description': 'codefix',
|
||||
\ 'changes': l:changes,
|
||||
\}, {})
|
||||
call ale#codefix#ApplyTSServerCodeAction(
|
||||
\ l:data,
|
||||
\ l:result[l:code_fix_to_apply - 1],
|
||||
\)
|
||||
elseif get(a:response, 'command', '') is# 'getApplicableRefactors'
|
||||
if get(a:response, 'success', v:false) is v:false
|
||||
\&& l:MenuCallback is v:null
|
||||
let l:message = get(a:response, 'message', 'unknown')
|
||||
call s:message('Error while getting applicable refactors. Reason: ' . l:message)
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if len(a:response.body) == 0
|
||||
let l:result = get(a:response, 'body', [])
|
||||
|
||||
if len(l:result) == 0
|
||||
call s:message('No applicable refactors available.')
|
||||
|
||||
return
|
||||
|
@ -88,7 +132,7 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
|
||||
let l:refactors = []
|
||||
|
||||
for l:item in a:response.body
|
||||
for l:item in l:result
|
||||
for l:action in l:item.actions
|
||||
call add(l:refactors, {
|
||||
\ 'name': l:action.description,
|
||||
|
@ -97,11 +141,21 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
endfor
|
||||
endfor
|
||||
|
||||
if l:MenuCallback isnot v:null
|
||||
call l:MenuCallback(
|
||||
\ l:data,
|
||||
\ map(copy(l:refactors), '[''tsserver'', v:val]')
|
||||
\)
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:refactor_no = 1
|
||||
let l:refactorstring = "Applicable refactors:\n"
|
||||
|
||||
for l:refactor in l:refactors
|
||||
let l:refactorstring .= l:refactor_no . ') ' . l:refactor.name . "\n"
|
||||
let l:refactorstring .= l:refactor_no . ') '
|
||||
\ . l:refactor.name . "\n"
|
||||
let l:refactor_no += 1
|
||||
endfor
|
||||
|
||||
|
@ -116,19 +170,10 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
|
||||
let l:id = l:refactors[l:refactor_to_apply - 1].id
|
||||
|
||||
let l:message = ale#lsp#tsserver_message#GetEditsForRefactor(
|
||||
\ l:location.buffer,
|
||||
\ l:location.line,
|
||||
\ l:location.column,
|
||||
\ l:location.end_line,
|
||||
\ l:location.end_column,
|
||||
\ l:id[0],
|
||||
\ l:id[1],
|
||||
call ale#codefix#ApplyTSServerCodeAction(
|
||||
\ l:data,
|
||||
\ l:refactors[l:refactor_to_apply - 1],
|
||||
\)
|
||||
|
||||
let l:request_id = ale#lsp#Send(l:location.connection_id, l:message)
|
||||
|
||||
let s:codefix_map[l:request_id] = l:location
|
||||
elseif get(a:response, 'command', '') is# 'getEditsForRefactor'
|
||||
if get(a:response, 'success', v:false) is v:false
|
||||
let l:message = get(a:response, 'message', 'unknown')
|
||||
|
@ -137,10 +182,48 @@ function! ale#codefix#HandleTSServerResponse(conn_id, response) abort
|
|||
return
|
||||
endif
|
||||
|
||||
call ale#code_action#HandleCodeAction({
|
||||
\ 'description': 'editsForRefactor',
|
||||
\ 'changes': a:response.body.edits,
|
||||
\}, {})
|
||||
call ale#code_action#HandleCodeAction(
|
||||
\ {
|
||||
\ 'description': 'editsForRefactor',
|
||||
\ 'changes': a:response.body.edits,
|
||||
\ },
|
||||
\ {},
|
||||
\)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#codefix#ApplyLSPCodeAction(data, item) abort
|
||||
if has_key(a:item, 'command')
|
||||
\&& type(a:item.command) == v:t_dict
|
||||
let l:command = a:item.command
|
||||
let l:message = ale#lsp#message#ExecuteCommand(
|
||||
\ l:command.command,
|
||||
\ l:command.arguments,
|
||||
\)
|
||||
|
||||
let l:request_id = ale#lsp#Send(a:data.connection_id, l:message)
|
||||
elseif has_key(a:item, 'edit') || has_key(a:item, 'arguments')
|
||||
if has_key(a:item, 'edit')
|
||||
let l:topass = a:item.edit
|
||||
else
|
||||
let l:topass = a:item.arguments[0]
|
||||
endif
|
||||
|
||||
let l:changes_map = ale#code_action#GetChanges(l:topass)
|
||||
|
||||
if empty(l:changes_map)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:changes = ale#code_action#BuildChangesList(l:changes_map)
|
||||
|
||||
call ale#code_action#HandleCodeAction(
|
||||
\ {
|
||||
\ 'description': 'codeaction',
|
||||
\ 'changes': l:changes,
|
||||
\ },
|
||||
\ {},
|
||||
\)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
@ -158,17 +241,32 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort
|
|||
|
||||
let l:changes = ale#code_action#BuildChangesList(l:changes_map)
|
||||
|
||||
call ale#code_action#HandleCodeAction({
|
||||
\ 'description': 'applyEdit',
|
||||
\ 'changes': l:changes,
|
||||
\}, {})
|
||||
call ale#code_action#HandleCodeAction(
|
||||
\ {
|
||||
\ 'description': 'applyEdit',
|
||||
\ 'changes': l:changes,
|
||||
\ },
|
||||
\ {}
|
||||
\)
|
||||
elseif has_key(a:response, 'id')
|
||||
\&& has_key(s:codefix_map, a:response.id)
|
||||
let l:location = remove(s:codefix_map, a:response.id)
|
||||
let l:data = remove(s:codefix_map, a:response.id)
|
||||
let l:MenuCallback = get(l:data, 'menu_callback', v:null)
|
||||
|
||||
if !has_key(a:response, 'result')
|
||||
\ || type(a:response.result) != v:t_list
|
||||
\ || len(a:response.result) == 0
|
||||
let l:result = get(a:response, 'result')
|
||||
|
||||
if type(l:result) != v:t_list
|
||||
let l:result = []
|
||||
endif
|
||||
|
||||
" Send the results to the menu callback, if set.
|
||||
if l:MenuCallback isnot v:null
|
||||
call l:MenuCallback(map(copy(l:result), '[''lsp'', v:val]'))
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
if len(l:result) == 0
|
||||
call s:message('No code actions received from server')
|
||||
|
||||
return
|
||||
|
@ -177,8 +275,9 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort
|
|||
let l:codeaction_no = 1
|
||||
let l:codeactionstring = "Code Fixes:\n"
|
||||
|
||||
for l:codeaction in a:response.result
|
||||
let l:codeactionstring .= l:codeaction_no . ') ' . l:codeaction.title . "\n"
|
||||
for l:codeaction in l:result
|
||||
let l:codeactionstring .= l:codeaction_no . ') '
|
||||
\ . l:codeaction.title . "\n"
|
||||
let l:codeaction_no += 1
|
||||
endfor
|
||||
|
||||
|
@ -191,42 +290,44 @@ function! ale#codefix#HandleLSPResponse(conn_id, response) abort
|
|||
return
|
||||
endif
|
||||
|
||||
let l:item = a:response.result[l:codeaction_to_apply - 1]
|
||||
let l:item = l:result[l:codeaction_to_apply - 1]
|
||||
|
||||
if has_key(l:item, 'command')
|
||||
\ && type(l:item.command) == v:t_dict
|
||||
let l:command = l:item.command
|
||||
let l:message = ale#lsp#message#ExecuteCommand(
|
||||
\ l:command.command,
|
||||
\ l:command.arguments,
|
||||
\)
|
||||
|
||||
let l:request_id = ale#lsp#Send(l:location.connection_id, l:message)
|
||||
elseif has_key(l:item, 'edit') || has_key(l:item, 'arguments')
|
||||
if has_key(l:item, 'edit')
|
||||
let l:topass = l:item.edit
|
||||
else
|
||||
let l:topass = l:item.arguments[0]
|
||||
endif
|
||||
|
||||
let l:changes_map = ale#code_action#GetChanges(l:topass)
|
||||
|
||||
if empty(l:changes_map)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:changes = ale#code_action#BuildChangesList(l:changes_map)
|
||||
|
||||
call ale#code_action#HandleCodeAction({
|
||||
\ 'description': 'codeaction',
|
||||
\ 'changes': l:changes,
|
||||
\}, {})
|
||||
endif
|
||||
call ale#codefix#ApplyLSPCodeAction(l:data, l:item)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:FindError(buffer, line, column, end_line, end_column) abort
|
||||
let l:nearest_error = v:null
|
||||
|
||||
function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abort
|
||||
if a:line == a:end_line
|
||||
\&& a:column == a:end_column
|
||||
\&& has_key(g:ale_buffer_info, a:buffer)
|
||||
let l:nearest_error_diff = -1
|
||||
|
||||
for l:error in get(g:ale_buffer_info[a:buffer], 'loclist', [])
|
||||
if has_key(l:error, 'code') && l:error.lnum == a:line
|
||||
let l:diff = abs(l:error.col - a:column)
|
||||
|
||||
if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff
|
||||
let l:nearest_error_diff = l:diff
|
||||
let l:nearest_error = l:error
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
return l:nearest_error
|
||||
endfunction
|
||||
|
||||
function! s:OnReady(
|
||||
\ line,
|
||||
\ column,
|
||||
\ end_line,
|
||||
\ end_column,
|
||||
\ MenuCallback,
|
||||
\ linter,
|
||||
\ lsp_details,
|
||||
\) abort
|
||||
let l:id = a:lsp_details.connection_id
|
||||
|
||||
if !ale#lsp#HasCapability(l:id, 'code_actions')
|
||||
|
@ -236,32 +337,17 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo
|
|||
let l:buffer = a:lsp_details.buffer
|
||||
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
if a:line == a:end_line && a:column == a:end_column
|
||||
if !has_key(g:ale_buffer_info, l:buffer)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:nearest_error = v:null
|
||||
let l:nearest_error_diff = -1
|
||||
|
||||
for l:error in get(g:ale_buffer_info[l:buffer], 'loclist', [])
|
||||
if has_key(l:error, 'code') && l:error.lnum == a:line
|
||||
let l:diff = abs(l:error.col - a:column)
|
||||
|
||||
if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff
|
||||
let l:nearest_error_diff = l:diff
|
||||
let l:nearest_error = l:error.code
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
let l:nearest_error =
|
||||
\ s:FindError(l:buffer, a:line, a:column, a:end_line, a:end_column)
|
||||
|
||||
if l:nearest_error isnot v:null
|
||||
let l:message = ale#lsp#tsserver_message#GetCodeFixes(
|
||||
\ l:buffer,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ [l:nearest_error],
|
||||
\ [l:nearest_error.code],
|
||||
\)
|
||||
else
|
||||
let l:message = ale#lsp#tsserver_message#GetApplicableRefactors(
|
||||
|
@ -277,56 +363,37 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo
|
|||
" completions won't know what text is nearby.
|
||||
call ale#lsp#NotifyForChanges(l:id, l:buffer)
|
||||
|
||||
if a:line == a:end_line && a:column == a:end_column
|
||||
if !has_key(g:ale_buffer_info, l:buffer)
|
||||
return
|
||||
endif
|
||||
let l:diagnostics = []
|
||||
let l:nearest_error =
|
||||
\ s:FindError(l:buffer, a:line, a:column, a:end_line, a:end_column)
|
||||
|
||||
let l:nearest_error = v:null
|
||||
let l:nearest_error_diff = -1
|
||||
|
||||
for l:error in get(g:ale_buffer_info[l:buffer], 'loclist', [])
|
||||
if has_key(l:error, 'code') && l:error.lnum == a:line
|
||||
let l:diff = abs(l:error.col - a:column)
|
||||
|
||||
if l:nearest_error_diff == -1 || l:diff < l:nearest_error_diff
|
||||
let l:nearest_error_diff = l:diff
|
||||
let l:nearest_error = l:error
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
let l:diagnostics = []
|
||||
|
||||
if l:nearest_error isnot v:null
|
||||
let l:diagnostics = [{
|
||||
\ 'code': l:nearest_error.code,
|
||||
\ 'message': l:nearest_error.text,
|
||||
\ 'range': {
|
||||
\ 'start': { 'line': l:nearest_error.lnum - 1, 'character': l:nearest_error.col - 1 },
|
||||
\ 'end': { 'line': l:nearest_error.end_lnum - 1, 'character': l:nearest_error.end_col - 1 }
|
||||
\}
|
||||
\}]
|
||||
endif
|
||||
|
||||
let l:message = ale#lsp#message#CodeAction(
|
||||
\ l:buffer,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ a:end_line,
|
||||
\ a:end_column,
|
||||
\ l:diagnostics,
|
||||
\)
|
||||
else
|
||||
let l:message = ale#lsp#message#CodeAction(
|
||||
\ l:buffer,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ a:end_line,
|
||||
\ a:end_column,
|
||||
\ [],
|
||||
\)
|
||||
if l:nearest_error isnot v:null
|
||||
let l:diagnostics = [
|
||||
\ {
|
||||
\ 'code': l:nearest_error.code,
|
||||
\ 'message': l:nearest_error.text,
|
||||
\ 'range': {
|
||||
\ 'start': {
|
||||
\ 'line': l:nearest_error.lnum - 1,
|
||||
\ 'character': l:nearest_error.col - 1,
|
||||
\ },
|
||||
\ 'end': {
|
||||
\ 'line': l:nearest_error.end_lnum - 1,
|
||||
\ 'character': l:nearest_error.end_col - 1,
|
||||
\ },
|
||||
\ },
|
||||
\ },
|
||||
\]
|
||||
endif
|
||||
|
||||
let l:message = ale#lsp#message#CodeAction(
|
||||
\ l:buffer,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ a:end_line,
|
||||
\ a:end_column,
|
||||
\ l:diagnostics,
|
||||
\)
|
||||
endif
|
||||
|
||||
let l:Callback = a:linter.lsp is# 'tsserver'
|
||||
|
@ -338,22 +405,40 @@ function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abo
|
|||
let l:request_id = ale#lsp#Send(l:id, l:message)
|
||||
|
||||
let s:codefix_map[l:request_id] = {
|
||||
\ 'connection_id': l:id,
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'line': a:line,
|
||||
\ 'column': a:column,
|
||||
\ 'end_line': a:end_line,
|
||||
\ 'end_column': a:end_column,
|
||||
\ 'connection_id': l:id,
|
||||
\ 'buffer': l:buffer,
|
||||
\ 'line': a:line,
|
||||
\ 'column': a:column,
|
||||
\ 'end_line': a:end_line,
|
||||
\ 'end_column': a:end_column,
|
||||
\ 'menu_callback': a:MenuCallback,
|
||||
\}
|
||||
endfunction
|
||||
|
||||
function! s:ExecuteGetCodeFix(linter, range) abort
|
||||
function! s:ExecuteGetCodeFix(linter, range, MenuCallback) abort
|
||||
let l:buffer = bufnr('')
|
||||
|
||||
if a:range == 0
|
||||
let [l:line, l:column] = getpos('.')[1:2]
|
||||
let l:end_line = l:line
|
||||
let l:end_column = l:column
|
||||
|
||||
" Expand the range to cover the current word, if there is one.
|
||||
let l:cword = expand('<cword>')
|
||||
|
||||
if !empty(l:cword)
|
||||
let l:search_pos = searchpos('\V' . l:cword, 'bn', l:line)
|
||||
|
||||
if l:search_pos != [0, 0]
|
||||
let l:column = l:search_pos[1]
|
||||
let l:end_column = l:column + len(l:cword) - 1
|
||||
endif
|
||||
endif
|
||||
elseif mode() is# 'v' || mode() is# "\<C-V>"
|
||||
" You need to get the start and end in a different way when you're in
|
||||
" visual mode.
|
||||
let [l:line, l:column] = getpos('v')[1:2]
|
||||
let [l:end_line, l:end_column] = getpos('.')[1:2]
|
||||
else
|
||||
let [l:line, l:column] = getpos("'<")[1:2]
|
||||
let [l:end_line, l:end_column] = getpos("'>")[1:2]
|
||||
|
@ -363,11 +448,18 @@ function! s:ExecuteGetCodeFix(linter, range) abort
|
|||
let l:end_column = min([l:end_column, len(getline(l:end_line))])
|
||||
|
||||
let l:Callback = function(
|
||||
\ 's:OnReady', [l:line, l:column, l:end_line, l:end_column])
|
||||
\ 's:OnReady', [l:line, l:column, l:end_line, l:end_column, a:MenuCallback]
|
||||
\)
|
||||
|
||||
call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
|
||||
endfunction
|
||||
|
||||
function! ale#codefix#Execute(range) abort
|
||||
function! ale#codefix#Execute(range, ...) abort
|
||||
if a:0 > 1
|
||||
throw 'Too many arguments'
|
||||
endif
|
||||
|
||||
let l:MenuCallback = get(a:000, 0, v:null)
|
||||
let l:lsp_linters = []
|
||||
|
||||
for l:linter in ale#linter#Get(&filetype)
|
||||
|
@ -377,12 +469,16 @@ function! ale#codefix#Execute(range) abort
|
|||
endfor
|
||||
|
||||
if empty(l:lsp_linters)
|
||||
call s:message('No active LSPs')
|
||||
if l:MenuCallback is v:null
|
||||
call s:message('No active LSPs')
|
||||
else
|
||||
call l:MenuCallback({}, [])
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
for l:lsp_linter in l:lsp_linters
|
||||
call s:ExecuteGetCodeFix(l:lsp_linter, a:range)
|
||||
call s:ExecuteGetCodeFix(l:lsp_linter, a:range, l:MenuCallback)
|
||||
endfor
|
||||
endfunction
|
||||
|
|
35
doc/ale.txt
35
doc/ale.txt
|
@ -673,13 +673,31 @@ for a full list of options.
|
|||
-------------------------------------------------------------------------------
|
||||
5.7 Refactoring: Rename, Actions *ale-refactor*
|
||||
|
||||
ALE supports renaming symbols in symbols in code such as variables or class
|
||||
names with the |ALERename| command.
|
||||
ALE supports renaming symbols in code such as variables or class names with
|
||||
the |ALERename| command.
|
||||
|
||||
|ALECodeAction| will execute actions on the cursor or applied to a visual
|
||||
range selection, such as automatically fixing errors.
|
||||
|
||||
Actions will appear in the right click mouse menu by default for GUI versions
|
||||
of Vim, unless disabled by setting |g:ale_popup_menu_enabled| to `0`.
|
||||
|
||||
Make sure to set your Vim to move the cursor position whenever you right
|
||||
click, and enable the mouse menu: >
|
||||
|
||||
set mouse=a
|
||||
set mousemodel=popup_setpos
|
||||
<
|
||||
You may wish to remove some other menu items you don't want to see: >
|
||||
|
||||
silent! aunmenu PopUp.Select\ Word
|
||||
silent! aunmenu PopUp.Select\ Sentence
|
||||
silent! aunmenu PopUp.Select\ Paragraph
|
||||
silent! aunmenu PopUp.Select\ Line
|
||||
silent! aunmenu PopUp.Select\ Block
|
||||
silent! aunmenu PopUp.Select\ Blockwise
|
||||
silent! aunmenu PopUp.Select\ All
|
||||
<
|
||||
===============================================================================
|
||||
6. Global Options *ale-options*
|
||||
|
||||
|
@ -1784,6 +1802,19 @@ g:ale_pattern_options_enabled *g:ale_pattern_options_enabled*
|
|||
will not set buffer variables per |g:ale_pattern_options|.
|
||||
|
||||
|
||||
g:ale_popup_menu_enabled *g:ale_popup_menu_enabled*
|
||||
|
||||
Type: |Number|
|
||||
Default: `has('gui')`
|
||||
|
||||
When this option is set to `1`, ALE will show code actions and rename
|
||||
capabilities in the right click mouse menu when there's a LSP server or
|
||||
tsserver available. See |ale-refactor|.
|
||||
|
||||
This setting must be set to `1` before ALE is loaded for this behavior
|
||||
to be enabled. See |ale-lint-settings-on-startup|.
|
||||
|
||||
|
||||
g:ale_rename_tsserver_find_in_comments *g:ale_rename_tsserver_find_in_comments*
|
||||
|
||||
Type: |Number|
|
||||
|
|
|
@ -158,6 +158,9 @@ let g:ale_python_auto_pipenv = get(g:, 'ale_python_auto_pipenv', 0)
|
|||
" This variable can be overridden to set the GO111MODULE environment variable.
|
||||
let g:ale_go_go111module = get(g:, 'ale_go_go111module', '')
|
||||
|
||||
" If 1, enable a popup menu for commands.
|
||||
let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui'))
|
||||
|
||||
if g:ale_set_balloons
|
||||
call ale#balloon#Enable()
|
||||
endif
|
||||
|
@ -166,6 +169,10 @@ if g:ale_completion_enabled
|
|||
call ale#completion#Enable()
|
||||
endif
|
||||
|
||||
if g:ale_popup_menu_enabled
|
||||
call ale#code_action#EnablePopUpMenu()
|
||||
endif
|
||||
|
||||
" Define commands for moving through warnings and errors.
|
||||
command! -bar -nargs=* ALEPrevious
|
||||
\ :call ale#loclist_jumping#WrapJump('before', <q-args>)
|
||||
|
|
|
@ -105,11 +105,9 @@ Execute(Failed codefix responses should be handled correctly):
|
|||
\)
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
|
||||
|
||||
|
||||
Given typescript(Some typescript file):
|
||||
foo
|
||||
somelongerline
|
||||
somelongerline ()
|
||||
bazxyzxyzxyz
|
||||
|
||||
Execute(getCodeFixes from tsserver should be handled):
|
||||
|
@ -283,7 +281,7 @@ Execute(tsserver codefix requests should be sent):
|
|||
|
||||
runtime ale_linters/typescript/tsserver.vim
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'code': 2304}]}}
|
||||
call setpos('.', [bufnr(''), 2, 5, 0])
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
@ -303,9 +301,9 @@ Execute(tsserver codefix requests should be sent):
|
|||
\ ale#lsp#tsserver_message#Change(bufnr('')),
|
||||
\ [0, 'ts@getCodeFixes', {
|
||||
\ 'startLine': 2,
|
||||
\ 'startOffset': 5,
|
||||
\ 'startOffset': 16,
|
||||
\ 'endLine': 2,
|
||||
\ 'endOffset': 6,
|
||||
\ 'endOffset': 17,
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'errorCodes': [2304],
|
||||
\ }]
|
||||
|
@ -316,8 +314,8 @@ Execute(tsserver codefix requests should be sent only for error with code):
|
|||
call ale#linter#Reset()
|
||||
|
||||
runtime ale_linters/typescript/tsserver.vim
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5}, {'lnum': 2, 'col': 5, 'code': 2304}]}}
|
||||
call setpos('.', [bufnr(''), 2, 5, 0])
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 16}, {'lnum': 2, 'col': 16, 'code': 2304}]}}
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
@ -337,9 +335,9 @@ Execute(tsserver codefix requests should be sent only for error with code):
|
|||
\ ale#lsp#tsserver_message#Change(bufnr('')),
|
||||
\ [0, 'ts@getCodeFixes', {
|
||||
\ 'startLine': 2,
|
||||
\ 'startOffset': 5,
|
||||
\ 'startOffset': 16,
|
||||
\ 'endLine': 2,
|
||||
\ 'endOffset': 6,
|
||||
\ 'endOffset': 17,
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'errorCodes': [2304],
|
||||
\ }]
|
||||
|
@ -424,43 +422,6 @@ Execute(getEditsForRefactor should print error on failure):
|
|||
|
||||
AssertEqual ['echom ''Error while getting edits for refactor. Reason: oops'''], g:expr_list
|
||||
|
||||
" TODO: I can't figure out how to run ALECodeAction on range
|
||||
" in test function. Therefore I can't write properly working
|
||||
" test. If somebody knows how to do that help is appreciated.
|
||||
"
|
||||
" Execute(tsserver getApplicableRefactors requests should be sent):
|
||||
" call ale#linter#Reset()
|
||||
"
|
||||
" runtime ale_linters/typescript/tsserver.vim
|
||||
" let g:ale_buffer_info = {bufnr(''): {'loclist': []}}
|
||||
" call setpos('.', [bufnr(''), 2, 5, 0])
|
||||
" normal "v$"
|
||||
"
|
||||
" execute "ALECodeAction"
|
||||
"
|
||||
" " We shouldn't register the callback yet.
|
||||
" AssertEqual '''''', string(g:Callback)
|
||||
"
|
||||
" AssertEqual type(function('type')), type(g:InitCallback)
|
||||
" call g:InitCallback()
|
||||
"
|
||||
" AssertEqual 'code_actions', g:capability_checked
|
||||
" AssertEqual
|
||||
" \ 'function(''ale#codefix#HandleTSServerResponse'')',
|
||||
" \ string(g:Callback)
|
||||
" AssertEqual
|
||||
" \ [
|
||||
" \ ale#lsp#tsserver_message#Change(bufnr('')),
|
||||
" \ [0, 'ts@getApplicableRefactors', {
|
||||
" \ 'startLine': 2,
|
||||
" \ 'startOffset': 5,
|
||||
" \ 'endLine': 2,
|
||||
" \ 'endOffset': 15,
|
||||
" \ 'file': expand('%:p'),
|
||||
" \ }]
|
||||
" \ ],
|
||||
" \ g:message_list
|
||||
|
||||
Execute(Failed LSP responses should be handled correctly):
|
||||
call ale#codefix#HandleLSPResponse(
|
||||
\ 1,
|
||||
|
@ -545,14 +506,6 @@ Execute(LSP code action requests should be sent):
|
|||
\ string(g:Callback)
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [1, 'workspace/didChangeConfiguration', {'settings': {'python': {}}}],
|
||||
\ [1, 'textDocument/didChange', {
|
||||
\ 'contentChanges': [{'text': "def main():\n a = 1\n b = a + 2\n"}],
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('%:p')),
|
||||
\ 'version': g:ale_lsp_next_version_id - 1,
|
||||
\ },
|
||||
\ }],
|
||||
\ [0, 'textDocument/codeAction', {
|
||||
\ 'context': {
|
||||
\ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
|
||||
|
@ -561,7 +514,7 @@ Execute(LSP code action requests should be sent):
|
|||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
\ g:message_list[-1:]
|
||||
|
||||
Execute(LSP code action requests should be sent only for error with code):
|
||||
call ale#linter#Reset()
|
||||
|
@ -585,14 +538,6 @@ Execute(LSP code action requests should be sent only for error with code):
|
|||
\ string(g:Callback)
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [1, 'workspace/didChangeConfiguration', {'settings': {'python': {}}}],
|
||||
\ [1, 'textDocument/didChange', {
|
||||
\ 'contentChanges': [{'text': "def main():\n a = 1\n b = a + 2\n"}],
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('%:p')),
|
||||
\ 'version': g:ale_lsp_next_version_id - 1,
|
||||
\ },
|
||||
\ }],
|
||||
\ [0, 'textDocument/codeAction', {
|
||||
\ 'context': {
|
||||
\ 'diagnostics': [{'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
|
||||
|
@ -601,4 +546,4 @@ Execute(LSP code action requests should be sent only for error with code):
|
|||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
\ g:message_list[-1:]
|
||||
|
|
Reference in a new issue