summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2020-11-21 01:18:27 +0000
committerw0rp <devw0rp@gmail.com>2020-11-21 01:18:27 +0000
commit7c04ee5c200b64ef0886677216fbec1303ec9475 (patch)
tree0f0af86e0fde0992818fdd057ea5745c47e8205c
parent48fe0dd4f629bb1282277ba8a6757a84c13a4dda (diff)
downloadale-7c04ee5c200b64ef0886677216fbec1303ec9475.zip
Close #1466 - Add GVIM refactor menu support
Code actions and ALERename now appear in the right click context menu for GVim by default.
-rw-r--r--autoload/ale/code_action.vim102
-rw-r--r--autoload/ale/codefix.vim392
-rw-r--r--doc/ale.txt35
-rw-r--r--plugin/ale.vim7
-rw-r--r--test/test_codefix.vader75
5 files changed, 396 insertions, 215 deletions
diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim
index fe6b175f..506107f4 100644
--- a/autoload/ale/code_action.vim
+++ b/autoload/ale/code_action.vim
@@ -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
diff --git a/autoload/ale/codefix.vim b/autoload/ale/codefix.vim
index b58f5e4b..3120e7cb 100644
--- a/autoload/ale/codefix.vim
+++ b/autoload/ale/codefix.vim
@@ -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)
+
+ let l:result = get(a:response, 'result')
+
+ if type(l:result) != v:t_list
+ let l:result = []
+ endif
- if !has_key(a:response, 'result')
- \ || type(a:response.result) != v:t_list
- \ || len(a:response.result) == 0
+ " 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]
-
- 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:item = l:result[l:codeaction_to_apply - 1]
- 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
+ call ale#codefix#ApplyLSPCodeAction(l:data, l:item)
+ endif
+endfunction
- let l:changes_map = ale#code_action#GetChanges(l:topass)
+function! s:FindError(buffer, line, column, end_line, end_column) abort
+ let l:nearest_error = v:null
- if empty(l:changes_map)
- return
- endif
+ 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
- let l:changes = ale#code_action#BuildChangesList(l:changes_map)
+ 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)
- call ale#code_action#HandleCodeAction({
- \ 'description': 'codeaction',
- \ 'changes': l:changes,
- \}, {})
- endif
+ 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
-endfunction
+ return l:nearest_error
+endfunction
-function! s:OnReady(line, column, end_line, end_column, linter, lsp_details) abort
+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: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,
- \ [],
- \)
+ let l:diagnostics = []
+ 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: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
diff --git a/doc/ale.txt b/doc/ale.txt
index 013b39f9..20baf355 100644
--- a/doc/ale.txt
+++ b/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|
diff --git a/plugin/ale.vim b/plugin/ale.vim
index c5c1d3d3..2398956e 100644
--- a/plugin/ale.vim
+++ b/plugin/ale.vim
@@ -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>)
diff --git a/test/test_codefix.vader b/test/test_codefix.vader
index 63275d7f..deb97256 100644
--- a/test/test_codefix.vader
+++ b/test/test_codefix.vader
@@ -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:]