merging master
This commit is contained in:
commit
9c7e1fe4dd
71 changed files with 2491 additions and 155 deletions
24
README.md
24
README.md
|
@ -53,6 +53,7 @@ other content at [w0rp.com](https://w0rp.com).
|
|||
5. [Find References](#usage-find-references)
|
||||
6. [Hovering](#usage-hover)
|
||||
7. [Symbol Search](#usage-symbol-search)
|
||||
8. [Refactoring: Rename, Actions](#usage-refactoring)
|
||||
3. [Installation](#installation)
|
||||
1. [Installation with Vim package management](#standard-installation)
|
||||
2. [Installation with Pathogen](#installation-with-pathogen)
|
||||
|
@ -253,6 +254,18 @@ similar to a given query string.
|
|||
|
||||
See `:help ale-symbol-search` for more information.
|
||||
|
||||
<a name="usage-refactoring"></a>
|
||||
|
||||
### 2.viii Refactoring: Rename, Actions
|
||||
|
||||
ALE supports renaming symbols in 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.
|
||||
|
||||
See `:help ale-refactor` for more information.
|
||||
|
||||
<a name="installation"></a>
|
||||
|
||||
## 3. Installation
|
||||
|
@ -328,12 +341,14 @@ git clone https://github.com/dense-analysis/ale.git
|
|||
### 3.iii. Installation with Vundle
|
||||
|
||||
You can install this plugin using [Vundle](https://github.com/VundleVim/Vundle.vim)
|
||||
by using the path on GitHub for this repository.
|
||||
by adding the GitHub path for this repository to your `~/.vimrc`:
|
||||
|
||||
```vim
|
||||
Plugin 'dense-analysis/ale'
|
||||
```
|
||||
|
||||
Then run the command `:PluginInstall` in Vim.
|
||||
|
||||
See the Vundle documentation for more information.
|
||||
|
||||
<a name="installation-with-vim-plug"></a>
|
||||
|
@ -341,13 +356,16 @@ See the Vundle documentation for more information.
|
|||
### 3.iiii. Installation with Vim-Plug
|
||||
|
||||
You can install this plugin using [Vim-Plug](https://github.com/junegunn/vim-plug)
|
||||
by adding the GitHub path for this repository to your `~/.vimrc`
|
||||
and running `:PlugInstall`.
|
||||
by adding the GitHub path for this repository to your `~/.vimrc`:
|
||||
|
||||
```vim
|
||||
Plug 'dense-analysis/ale'
|
||||
```
|
||||
|
||||
Then run the command `:PlugInstall` in Vim.
|
||||
|
||||
See the Vim-Plug documentation for more information.
|
||||
|
||||
<a name="contributing"></a>
|
||||
|
||||
## 4. Contributing
|
||||
|
|
39
ale_linters/erlang/elvis.vim
Normal file
39
ale_linters/erlang/elvis.vim
Normal file
|
@ -0,0 +1,39 @@
|
|||
" Author: Dmitri Vereshchagin <dmitri.vereshchagin@gmail.com>
|
||||
" Description: Elvis linter for Erlang files
|
||||
|
||||
call ale#Set('erlang_elvis_executable', 'elvis')
|
||||
|
||||
function! ale_linters#erlang#elvis#Handle(buffer, lines) abort
|
||||
let l:pattern = '\v:(\d+):[^:]+:(.+)'
|
||||
let l:loclist = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:loclist, {
|
||||
\ 'lnum': str2nr(l:match[1]),
|
||||
\ 'text': s:AbbreviateMessage(l:match[2]),
|
||||
\ 'type': 'W',
|
||||
\})
|
||||
endfor
|
||||
|
||||
return l:loclist
|
||||
endfunction
|
||||
|
||||
function! s:AbbreviateMessage(text) abort
|
||||
let l:pattern = '\v\c^(line \d+ is too long):.*$'
|
||||
|
||||
return substitute(a:text, l:pattern, '\1.', '')
|
||||
endfunction
|
||||
|
||||
function! s:GetCommand(buffer) abort
|
||||
let l:file = ale#Escape(expand('#' . a:buffer . ':.'))
|
||||
|
||||
return '%e rock --output-format=parsable ' . l:file
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('erlang', {
|
||||
\ 'name': 'elvis',
|
||||
\ 'callback': 'ale_linters#erlang#elvis#Handle',
|
||||
\ 'executable': {b -> ale#Var(b, 'erlang_elvis_executable')},
|
||||
\ 'command': function('s:GetCommand'),
|
||||
\ 'lint_file': 1,
|
||||
\})
|
32
ale_linters/php/intelephense.vim
Normal file
32
ale_linters/php/intelephense.vim
Normal file
|
@ -0,0 +1,32 @@
|
|||
" Author: Eric Stern <eric@ericstern.com>,
|
||||
" Arnold Chand <creativenull@outlook.com>
|
||||
" Description: Intelephense language server integration for ALE
|
||||
|
||||
call ale#Set('php_intelephense_executable', 'intelephense')
|
||||
call ale#Set('php_intelephense_use_global', 1)
|
||||
call ale#Set('php_intelephense_config', {})
|
||||
|
||||
function! ale_linters#php#intelephense#GetProjectRoot(buffer) abort
|
||||
let l:composer_path = ale#path#FindNearestFile(a:buffer, 'composer.json')
|
||||
|
||||
if (!empty(l:composer_path))
|
||||
return fnamemodify(l:composer_path, ':h')
|
||||
endif
|
||||
|
||||
let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
|
||||
|
||||
return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : ''
|
||||
endfunction
|
||||
|
||||
function! ale_linters#php#intelephense#GetInitializationOptions() abort
|
||||
return ale#Get('php_intelephense_config')
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('php', {
|
||||
\ 'name': 'intelephense',
|
||||
\ 'lsp': 'stdio',
|
||||
\ 'initialization_options': function('ale_linters#php#intelephense#GetInitializationOptions'),
|
||||
\ 'executable': {b -> ale#node#FindExecutable(b, 'php_intelephense', [])},
|
||||
\ 'command': '%e --stdio',
|
||||
\ 'project_root': function('ale_linters#php#intelephense#GetProjectRoot'),
|
||||
\})
|
|
@ -23,7 +23,7 @@ function! ale_linters#php#phpcs#Handle(buffer, lines) abort
|
|||
" Matches against lines like the following:
|
||||
"
|
||||
" /path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)
|
||||
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) - \(.\+\) (\(.\+\))$'
|
||||
let l:pattern = '^.*:\(\d\+\):\(\d\+\): \(.\+\) - \(.\+\) (\(.\+\)).*$'
|
||||
let l:output = []
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
|
|
80
ale_linters/php/tlint.vim
Normal file
80
ale_linters/php/tlint.vim
Normal file
|
@ -0,0 +1,80 @@
|
|||
" Author: Jose Soto <jose@tighten.co>
|
||||
"
|
||||
" Description: Tighten Opinionated PHP Linting
|
||||
" Website: https://github.com/tightenco/tlint
|
||||
|
||||
call ale#Set('php_tlint_executable', 'tlint')
|
||||
call ale#Set('php_tlint_use_global', get(g:, 'ale_use_global_executables', 0))
|
||||
call ale#Set('php_tlint_options', '')
|
||||
|
||||
function! ale_linters#php#tlint#GetProjectRoot(buffer) abort
|
||||
let l:composer_path = ale#path#FindNearestFile(a:buffer, 'composer.json')
|
||||
|
||||
if !empty(l:composer_path)
|
||||
return fnamemodify(l:composer_path, ':h')
|
||||
endif
|
||||
|
||||
let l:git_path = ale#path#FindNearestDirectory(a:buffer, '.git')
|
||||
|
||||
return !empty(l:git_path) ? fnamemodify(l:git_path, ':h:h') : ''
|
||||
endfunction
|
||||
|
||||
function! ale_linters#php#tlint#GetExecutable(buffer) abort
|
||||
return ale#node#FindExecutable(a:buffer, 'php_tlint', [
|
||||
\ 'vendor/bin/tlint',
|
||||
\ 'tlint',
|
||||
\])
|
||||
endfunction
|
||||
|
||||
function! ale_linters#php#tlint#GetCommand(buffer) abort
|
||||
let l:executable = ale_linters#php#tlint#GetExecutable(a:buffer)
|
||||
let l:options = ale#Var(a:buffer, 'php_tlint_options')
|
||||
|
||||
return ale#node#Executable(a:buffer, l:executable)
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' lint %s'
|
||||
endfunction
|
||||
|
||||
function! ale_linters#php#tlint#Handle(buffer, lines) abort
|
||||
" Matches against lines like the following:
|
||||
"
|
||||
" ! There should be 1 space around `.` concatenations, and additional lines should always start with a `.`
|
||||
" 22 : ` $something = 'a'.'name';`
|
||||
"
|
||||
let l:loop_count = 0
|
||||
let l:messages_pattern = '^\! \(.*\)'
|
||||
let l:output = []
|
||||
let l:pattern = '^\(\d\+\) \:'
|
||||
let l:temp_messages = []
|
||||
|
||||
for l:message in ale#util#GetMatches(a:lines, l:messages_pattern)
|
||||
call add(l:temp_messages, l:message)
|
||||
endfor
|
||||
|
||||
let l:loop_count = 0
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
let l:num = l:match[1]
|
||||
let l:text = l:temp_messages[l:loop_count]
|
||||
|
||||
call add(l:output, {
|
||||
\ 'lnum': l:num,
|
||||
\ 'col': 0,
|
||||
\ 'text': l:text,
|
||||
\ 'type': 'W',
|
||||
\ 'sub_type': 'style',
|
||||
\})
|
||||
|
||||
let l:loop_count += 1
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('php', {
|
||||
\ 'name': 'tlint',
|
||||
\ 'executable': function('ale_linters#php#tlint#GetExecutable'),
|
||||
\ 'command': function('ale_linters#php#tlint#GetCommand'),
|
||||
\ 'callback': 'ale_linters#php#tlint#Handle',
|
||||
\ 'project_root': function('ale_linters#php#tlint#GetProjectRoot'),
|
||||
\})
|
34
ale_linters/python/jedils.vim
Normal file
34
ale_linters/python/jedils.vim
Normal file
|
@ -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',
|
||||
\})
|
26
ale_linters/r/languageserver.vim
Normal file
26
ale_linters/r/languageserver.vim
Normal file
|
@ -0,0 +1,26 @@
|
|||
" Author: Eric Zhao <21zhaoe@protonmail.com>
|
||||
" Description: Implementation of the Language Server Protocol for R.
|
||||
|
||||
call ale#Set('r_languageserver_cmd', 'languageserver::run()')
|
||||
call ale#Set('r_languageserver_config', {})
|
||||
|
||||
function! ale_linters#r#languageserver#GetCommand(buffer) abort
|
||||
let l:cmd_string = ale#Var(a:buffer, 'r_languageserver_cmd')
|
||||
|
||||
return 'Rscript --vanilla -e ' . ale#Escape(l:cmd_string)
|
||||
endfunction
|
||||
|
||||
function! ale_linters#r#languageserver#GetProjectRoot(buffer) abort
|
||||
let l:project_root = ale#path#FindNearestFile(a:buffer, '.Rprofile')
|
||||
|
||||
return !empty(l:project_root) ? fnamemodify(l:project_root, ':h') : fnamemodify(a:buffer, ':h')
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('r', {
|
||||
\ 'name': 'languageserver',
|
||||
\ 'lsp': 'stdio',
|
||||
\ 'lsp_config': {b -> ale#Var(b, 'r_languageserver_config')},
|
||||
\ 'executable': 'Rscript',
|
||||
\ 'command': function('ale_linters#r#languageserver#GetCommand'),
|
||||
\ 'project_root': function('ale_linters#r#languageserver#GetProjectRoot')
|
||||
\})
|
|
@ -1,14 +1,17 @@
|
|||
call ale#Set('ruby_sorbet_executable', 'srb')
|
||||
call ale#Set('ruby_sorbet_options', '')
|
||||
call ale#Set('ruby_sorbet_enable_watchman', 0)
|
||||
|
||||
function! ale_linters#ruby#sorbet#GetCommand(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'ruby_sorbet_executable')
|
||||
let l:options = ale#Var(a:buffer, 'ruby_sorbet_options')
|
||||
let l:enable_watchman = ale#Var(a:buffer, 'ruby_sorbet_enable_watchman')
|
||||
|
||||
return ale#ruby#EscapeExecutable(l:executable, 'srb')
|
||||
\ . ' tc'
|
||||
\ . (!empty(l:options) ? ' ' . l:options : '')
|
||||
\ . ' --lsp --disable-watchman'
|
||||
\ . ' --lsp'
|
||||
\ . (l:enable_watchman ? '' : ' --disable-watchman')
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('ruby', {
|
||||
|
|
|
@ -9,6 +9,7 @@ call ale#linter#Define('typescript', {
|
|||
\ 'name': 'tsserver',
|
||||
\ 'lsp': 'tsserver',
|
||||
\ 'executable': {b -> ale#node#FindExecutable(b, 'typescript_tsserver', [
|
||||
\ '.yarn/sdks/typescript/bin/tsserver',
|
||||
\ 'node_modules/.bin/tsserver',
|
||||
\ ])},
|
||||
\ 'command': '%e',
|
||||
|
|
|
@ -2,23 +2,39 @@
|
|||
" Description: balloonexpr support for ALE.
|
||||
|
||||
function! ale#balloon#MessageForPos(bufnr, lnum, col) abort
|
||||
let l:set_balloons = ale#Var(a:bufnr, 'set_balloons')
|
||||
let l:show_problems = 0
|
||||
let l:show_hover = 0
|
||||
|
||||
if l:set_balloons is 1
|
||||
let l:show_problems = 1
|
||||
let l:show_hover = 1
|
||||
elseif l:set_balloons is# 'hover'
|
||||
let l:show_hover = 1
|
||||
endif
|
||||
|
||||
" Don't show balloons if they are disabled, or linting is disabled.
|
||||
if !ale#Var(a:bufnr, 'set_balloons')
|
||||
if !(l:show_problems || l:show_hover)
|
||||
\|| !g:ale_enabled
|
||||
\|| !getbufvar(a:bufnr, 'ale_enabled', 1)
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist
|
||||
let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col)
|
||||
if l:show_problems
|
||||
let l:loclist = get(g:ale_buffer_info, a:bufnr, {'loclist': []}).loclist
|
||||
let l:index = ale#util#BinarySearch(l:loclist, a:bufnr, a:lnum, a:col)
|
||||
endif
|
||||
|
||||
" Show the diagnostics message if found, 'Hover' output otherwise
|
||||
if l:index >= 0
|
||||
if l:show_problems && l:index >= 0
|
||||
return l:loclist[l:index].text
|
||||
elseif exists('*balloon_show') || getbufvar(
|
||||
\ a:bufnr,
|
||||
\ 'ale_set_balloons_legacy_echo',
|
||||
\ get(g:, 'ale_set_balloons_legacy_echo', 0)
|
||||
elseif l:show_hover && (
|
||||
\ exists('*balloon_show')
|
||||
\ || getbufvar(
|
||||
\ a:bufnr,
|
||||
\ 'ale_set_balloons_legacy_echo',
|
||||
\ get(g:, 'ale_set_balloons_legacy_echo', 0)
|
||||
\ )
|
||||
\)
|
||||
" Request LSP/tsserver hover information, but only if this version of
|
||||
" Vim supports the balloon_show function, or if we turned a legacy
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
" Author: Jerko Steiner <jerko.steiner@gmail.com>
|
||||
" Description: Code action support for LSP / tsserver
|
||||
|
||||
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
|
||||
|
||||
function! ale#code_action#HandleCodeAction(code_action, options) abort
|
||||
let l:current_buffer = bufnr('')
|
||||
let l:changes = a:code_action.changes
|
||||
let l:should_save = get(a:options, 'should_save')
|
||||
let l:force_save = get(a:options, 'force_save')
|
||||
let l:safe_changes = []
|
||||
|
||||
for l:file_code_edit in l:changes
|
||||
let l:buf = bufnr(l:file_code_edit.fileName)
|
||||
|
||||
if l:buf != -1 && l:buf != l:current_buffer && getbufvar(l:buf, '&mod')
|
||||
if !l:force_save
|
||||
call ale#util#Execute('echom ''Aborting action, file is unsaved''')
|
||||
|
||||
return
|
||||
endif
|
||||
else
|
||||
call add(l:safe_changes, l:file_code_edit)
|
||||
endif
|
||||
endfor
|
||||
|
||||
for l:file_code_edit in l:safe_changes
|
||||
call ale#code_action#ApplyChanges(
|
||||
\ l:file_code_edit.fileName,
|
||||
\ l:file_code_edit.textChanges,
|
||||
|
@ -85,29 +81,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.
|
||||
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
|
||||
" Changes have to be sorted so we apply them from bottom-to-top
|
||||
for l:code_edit in reverse(sort(copy(a:changes), function('s:ChangeCmp')))
|
||||
let l:line = l:code_edit.start.line
|
||||
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
|
||||
|
@ -125,6 +106,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.
|
||||
|
@ -134,13 +121,17 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
|
|||
endif
|
||||
|
||||
call extend(l:middle, l:insertions[1:])
|
||||
let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :]
|
||||
|
||||
if l:end_line <= len(l:lines)
|
||||
" Only extend the last line if end_line is within the range of
|
||||
" lines.
|
||||
let l:middle[-1] .= l:lines[l:end_line - 1][l:end_column - 1 :]
|
||||
endif
|
||||
|
||||
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: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,
|
||||
|
@ -166,6 +157,20 @@ function! ale#code_action#ApplyChanges(filename, changes, should_save) abort
|
|||
|
||||
call setpos('.', [0, l:pos[0], l:pos[1], 0])
|
||||
endif
|
||||
|
||||
if a:should_save && l:buffer > 0 && !l:is_current_buffer
|
||||
" Set up a one-time use event that will delete itself to reload the
|
||||
" buffer next time it's entered to view the changes made to it.
|
||||
execute 'augroup ALECodeActionReloadGroup' . l:buffer
|
||||
autocmd!
|
||||
|
||||
execute printf(
|
||||
\ 'autocmd BufEnter <buffer=%d>'
|
||||
\ . ' call ale#code_action#ReloadBuffer()',
|
||||
\ l:buffer
|
||||
\)
|
||||
augroup END
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:UpdateCursor(cursor, start, end, offset) abort
|
||||
|
@ -215,3 +220,163 @@ 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
|
||||
|
||||
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
|
||||
|
|
484
autoload/ale/codefix.vim
Normal file
484
autoload/ale/codefix.vim
Normal file
|
@ -0,0 +1,484 @@
|
|||
" Author: Dalius Dobravolskas <dalius.dobravolskas@gmail.com>
|
||||
" Description: Code Fix support for tsserver and LSP servers
|
||||
|
||||
let s:codefix_map = {}
|
||||
|
||||
" Used to get the codefix map in tests.
|
||||
function! ale#codefix#GetMap() abort
|
||||
return deepcopy(s:codefix_map)
|
||||
endfunction
|
||||
|
||||
" Used to set the codefix map in tests.
|
||||
function! ale#codefix#SetMap(map) abort
|
||||
let s:codefix_map = a:map
|
||||
endfunction
|
||||
|
||||
function! ale#codefix#ClearLSPData() abort
|
||||
let s:codefix_map = {}
|
||||
endfunction
|
||||
|
||||
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: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
|
||||
|
||||
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
|
||||
endif
|
||||
|
||||
let l:code_fix_to_apply = 0
|
||||
|
||||
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 l:result
|
||||
let l:codefixstring .= l:codefix_no . ') '
|
||||
\ . l:codefix.description . "\n"
|
||||
let l:codefix_no += 1
|
||||
endfor
|
||||
|
||||
let l:codefixstring .= 'Type number and <Enter> (empty cancels): '
|
||||
|
||||
let l:code_fix_to_apply = ale#util#Input(l:codefixstring, '')
|
||||
let l:code_fix_to_apply = str2nr(l:code_fix_to_apply)
|
||||
|
||||
if l:code_fix_to_apply == 0
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
let l:result = get(a:response, 'body', [])
|
||||
|
||||
if len(l:result) == 0
|
||||
call s:message('No applicable refactors available.')
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:refactors = []
|
||||
|
||||
for l:item in l:result
|
||||
for l:action in l:item.actions
|
||||
call add(l:refactors, {
|
||||
\ 'name': l:action.description,
|
||||
\ 'id': [l:item.name, l:action.name],
|
||||
\})
|
||||
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:refactor_no += 1
|
||||
endfor
|
||||
|
||||
let l:refactorstring .= 'Type number and <Enter> (empty cancels): '
|
||||
|
||||
let l:refactor_to_apply = ale#util#Input(l:refactorstring, '')
|
||||
let l:refactor_to_apply = str2nr(l:refactor_to_apply)
|
||||
|
||||
if l:refactor_to_apply == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:id = l:refactors[l:refactor_to_apply - 1].id
|
||||
|
||||
call ale#codefix#ApplyTSServerCodeAction(
|
||||
\ l:data,
|
||||
\ l:refactors[l:refactor_to_apply - 1],
|
||||
\)
|
||||
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')
|
||||
call s:message('Error while getting edits for refactor. Reason: ' . l:message)
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
function! ale#codefix#HandleLSPResponse(conn_id, response) abort
|
||||
if has_key(a:response, 'method')
|
||||
\ && a:response.method is# 'workspace/applyEdit'
|
||||
\ && has_key(a:response, 'params')
|
||||
let l:params = a:response.params
|
||||
|
||||
let l:changes_map = ale#code_action#GetChanges(l:params.edit)
|
||||
|
||||
if empty(l:changes_map)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:changes = ale#code_action#BuildChangesList(l:changes_map)
|
||||
|
||||
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: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
|
||||
|
||||
" 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
|
||||
endif
|
||||
|
||||
let l:codeaction_no = 1
|
||||
let l:codeactionstring = "Code Fixes:\n"
|
||||
|
||||
for l:codeaction in l:result
|
||||
let l:codeactionstring .= l:codeaction_no . ') '
|
||||
\ . l:codeaction.title . "\n"
|
||||
let l:codeaction_no += 1
|
||||
endfor
|
||||
|
||||
let l:codeactionstring .= 'Type number and <Enter> (empty cancels): '
|
||||
|
||||
let l:codeaction_to_apply = ale#util#Input(l:codeactionstring, '')
|
||||
let l:codeaction_to_apply = str2nr(l:codeaction_to_apply)
|
||||
|
||||
if l:codeaction_to_apply == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:item = l:result[l:codeaction_to_apply - 1]
|
||||
|
||||
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
|
||||
|
||||
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')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:buffer = a:lsp_details.buffer
|
||||
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
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.code],
|
||||
\)
|
||||
else
|
||||
let l:message = ale#lsp#tsserver_message#GetApplicableRefactors(
|
||||
\ l:buffer,
|
||||
\ a:line,
|
||||
\ a:column,
|
||||
\ a:end_line,
|
||||
\ a:end_column,
|
||||
\)
|
||||
endif
|
||||
else
|
||||
" Send a message saying the buffer has changed first, otherwise
|
||||
" completions won't know what text is nearby.
|
||||
call ale#lsp#NotifyForChanges(l:id, l:buffer)
|
||||
|
||||
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,
|
||||
\ },
|
||||
\ },
|
||||
\ },
|
||||
\]
|
||||
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'
|
||||
\ ? function('ale#codefix#HandleTSServerResponse')
|
||||
\ : function('ale#codefix#HandleLSPResponse')
|
||||
|
||||
call ale#lsp#RegisterCallback(l:id, l:Callback)
|
||||
|
||||
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,
|
||||
\ 'menu_callback': a:MenuCallback,
|
||||
\}
|
||||
endfunction
|
||||
|
||||
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]
|
||||
endif
|
||||
|
||||
let l:column = min([l:column, len(getline(l:line))])
|
||||
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, a:MenuCallback]
|
||||
\)
|
||||
|
||||
call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
|
||||
endfunction
|
||||
|
||||
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)
|
||||
if !empty(l:linter.lsp)
|
||||
call add(l:lsp_linters, l:linter)
|
||||
endif
|
||||
endfor
|
||||
|
||||
if empty(l:lsp_linters)
|
||||
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, l:MenuCallback)
|
||||
endfor
|
||||
endfunction
|
|
@ -606,17 +606,21 @@ function! ale#completion#ParseLSPCompletions(response) abort
|
|||
let l:doc = l:doc.value
|
||||
endif
|
||||
|
||||
" Collapse whitespaces and line breaks into a single space.
|
||||
let l:detail = substitute(get(l:item, 'detail', ''), '\_s\+', ' ', 'g')
|
||||
|
||||
let l:result = {
|
||||
\ 'word': l:word,
|
||||
\ 'kind': ale#completion#GetCompletionSymbols(get(l:item, 'kind', '')),
|
||||
\ 'icase': 1,
|
||||
\ 'menu': get(l:item, 'detail', ''),
|
||||
\ 'menu': l:detail,
|
||||
\ 'info': (type(l:doc) is v:t_string ? l:doc : ''),
|
||||
\}
|
||||
" This flag is used to tell if this completion came from ALE or not.
|
||||
let l:user_data = {'_ale_completion_item': 1}
|
||||
|
||||
if has_key(l:item, 'additionalTextEdits')
|
||||
\ && l:item.additionalTextEdits isnot v:null
|
||||
let l:text_changes = []
|
||||
|
||||
for l:edit in l:item.additionalTextEdits
|
||||
|
|
|
@ -12,6 +12,11 @@ let s:default_registry = {
|
|||
\ 'suggested_filetypes': ['help'],
|
||||
\ 'description': 'Align help tags to the right margin',
|
||||
\ },
|
||||
\ 'autoimport': {
|
||||
\ 'function': 'ale#fixers#autoimport#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
\ 'description': 'Fix import issues with autoimport.',
|
||||
\ },
|
||||
\ 'autopep8': {
|
||||
\ 'function': 'ale#fixers#autopep8#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
|
@ -32,6 +37,11 @@ let s:default_registry = {
|
|||
\ 'suggested_filetypes': ['d'],
|
||||
\ 'description': 'Fix D files with dfmt.',
|
||||
\ },
|
||||
\ 'dhall': {
|
||||
\ 'function': 'ale#fixers#dhall#Fix',
|
||||
\ 'suggested_filetypes': ['dhall'],
|
||||
\ 'description': 'Fix Dhall files with dhall-format.',
|
||||
\ },
|
||||
\ 'dhall-format': {
|
||||
\ 'function': 'ale#fixers#dhall_format#Fix',
|
||||
\ 'suggested_filetypes': ['dhall'],
|
||||
|
@ -121,6 +131,11 @@ let s:default_registry = {
|
|||
\ 'suggested_filetypes': [],
|
||||
\ 'description': 'Remove all trailing whitespace characters at the end of every line.',
|
||||
\ },
|
||||
\ 'yamlfix': {
|
||||
\ 'function': 'ale#fixers#yamlfix#Fix',
|
||||
\ 'suggested_filetypes': ['yaml'],
|
||||
\ 'description': 'Fix yaml files with yamlfix.',
|
||||
\ },
|
||||
\ 'yapf': {
|
||||
\ 'function': 'ale#fixers#yapf#Fix',
|
||||
\ 'suggested_filetypes': ['python'],
|
||||
|
@ -391,6 +406,16 @@ let s:default_registry = {
|
|||
\ 'suggested_filetypes': ['html', 'htmldjango'],
|
||||
\ 'description': 'Fix HTML files with html-beautify.',
|
||||
\ },
|
||||
\ 'luafmt': {
|
||||
\ 'function': 'ale#fixers#luafmt#Fix',
|
||||
\ 'suggested_filetypes': ['lua'],
|
||||
\ 'description': 'Fix Lua files with luafmt.',
|
||||
\ },
|
||||
\ 'ormolu': {
|
||||
\ 'function': 'ale#fixers#ormolu#Fix',
|
||||
\ 'suggested_filetypes': ['haskell'],
|
||||
\ 'description': 'A formatter for Haskell source code.',
|
||||
\ }
|
||||
\}
|
||||
|
||||
" Reset the function registry to the default entries.
|
||||
|
|
25
autoload/ale/fixers/autoimport.vim
Normal file
25
autoload/ale/fixers/autoimport.vim
Normal file
|
@ -0,0 +1,25 @@
|
|||
" Author: lyz-code
|
||||
" Description: Fixing Python imports with autoimport.
|
||||
|
||||
call ale#Set('python_autoimport_executable', 'autoimport')
|
||||
call ale#Set('python_autoimport_options', '')
|
||||
call ale#Set('python_autoimport_use_global', get(g:, 'ale_use_global_executables', 0))
|
||||
|
||||
function! ale#fixers#autoimport#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'python_autoimport_options')
|
||||
|
||||
let l:executable = ale#python#FindExecutable(
|
||||
\ a:buffer,
|
||||
\ 'python_autoimport',
|
||||
\ ['autoimport'],
|
||||
\)
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#path#BufferCdString(a:buffer)
|
||||
\ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -',
|
||||
\}
|
||||
endfunction
|
|
@ -11,9 +11,6 @@ function! ale#fixers#gofmt#Fix(buffer) abort
|
|||
|
||||
return {
|
||||
\ 'command': l:env . ale#Escape(l:executable)
|
||||
\ . ' -l -w'
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' %t',
|
||||
\ 'read_temporary_file': 1,
|
||||
\}
|
||||
endfunction
|
||||
|
|
13
autoload/ale/fixers/luafmt.vim
Normal file
13
autoload/ale/fixers/luafmt.vim
Normal file
|
@ -0,0 +1,13 @@
|
|||
call ale#Set('lua_luafmt_executable', 'luafmt')
|
||||
call ale#Set('lua_luafmt_options', '')
|
||||
|
||||
function! ale#fixers#luafmt#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'lua_luafmt_executable')
|
||||
let l:options = ale#Var(a:buffer, 'lua_luafmt_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options)
|
||||
\ . ' --stdin',
|
||||
\}
|
||||
endfunction
|
12
autoload/ale/fixers/ormolu.vim
Normal file
12
autoload/ale/fixers/ormolu.vim
Normal file
|
@ -0,0 +1,12 @@
|
|||
call ale#Set('haskell_ormolu_executable', 'ormolu')
|
||||
call ale#Set('haskell_ormolu_options', '')
|
||||
|
||||
function! ale#fixers#ormolu#Fix(buffer) abort
|
||||
let l:executable = ale#Var(a:buffer, 'haskell_ormolu_executable')
|
||||
let l:options = ale#Var(a:buffer, 'haskell_ormolu_options')
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable)
|
||||
\ . (empty(l:options) ? '' : ' ' . l:options),
|
||||
\}
|
||||
endfunction
|
|
@ -2,6 +2,7 @@
|
|||
" Description: Fixing files with phpcbf.
|
||||
|
||||
call ale#Set('php_phpcbf_standard', '')
|
||||
call ale#Set('php_phpcbf_options', '')
|
||||
call ale#Set('php_phpcbf_executable', 'phpcbf')
|
||||
call ale#Set('php_phpcbf_use_global', get(g:, 'ale_use_global_executables', 0))
|
||||
|
||||
|
@ -20,6 +21,6 @@ function! ale#fixers#phpcbf#Fix(buffer) abort
|
|||
\ : ''
|
||||
|
||||
return {
|
||||
\ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ' -'
|
||||
\ 'command': ale#Escape(l:executable) . ' --stdin-path=%s ' . l:standard_option . ale#Pad(ale#Var(a:buffer, 'php_phpcbf_options')) . ' -'
|
||||
\}
|
||||
endfunction
|
||||
|
|
25
autoload/ale/fixers/yamlfix.vim
Normal file
25
autoload/ale/fixers/yamlfix.vim
Normal file
|
@ -0,0 +1,25 @@
|
|||
" Author: lyz-code
|
||||
" Description: Fixing yaml files with yamlfix.
|
||||
|
||||
call ale#Set('yaml_yamlfix_executable', 'yamlfix')
|
||||
call ale#Set('yaml_yamlfix_options', '')
|
||||
call ale#Set('yaml_yamlfix_use_global', get(g:, 'ale_use_global_executables', 0))
|
||||
|
||||
function! ale#fixers#yamlfix#Fix(buffer) abort
|
||||
let l:options = ale#Var(a:buffer, 'yaml_yamlfix_options')
|
||||
|
||||
let l:executable = ale#python#FindExecutable(
|
||||
\ a:buffer,
|
||||
\ 'yaml_yamlfix',
|
||||
\ ['yamlfix'],
|
||||
\)
|
||||
|
||||
if !executable(l:executable)
|
||||
return 0
|
||||
endif
|
||||
|
||||
return {
|
||||
\ 'command': ale#path#BufferCdString(a:buffer)
|
||||
\ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -',
|
||||
\}
|
||||
endfunction
|
|
@ -5,6 +5,7 @@ let s:executables = [
|
|||
\ 'node_modules/.bin/eslint_d',
|
||||
\ 'node_modules/eslint/bin/eslint.js',
|
||||
\ 'node_modules/.bin/eslint',
|
||||
\ '.yarn/sdks/eslint/bin/eslint',
|
||||
\]
|
||||
let s:sep = has('win32') ? '\' : '/'
|
||||
|
||||
|
|
|
@ -1,8 +1,32 @@
|
|||
" Author: w0rp <devw0rp@gmail.com>
|
||||
" Description: This file adds support for using the shellcheck linter
|
||||
|
||||
" Shellcheck supports shell directives to define the shell dialect for scripts
|
||||
" that do not have a shebang for some reason.
|
||||
" https://github.com/koalaman/shellcheck/wiki/Directive#shell
|
||||
function! ale#handlers#shellcheck#GetShellcheckDialectDirective(buffer) abort
|
||||
let l:linenr = 0
|
||||
let l:pattern = '\s\{-}#\s\{-}shellcheck\s\{-}shell=\(.*\)'
|
||||
let l:possible_shell = ['bash', 'dash', 'ash', 'tcsh', 'csh', 'zsh', 'ksh', 'sh']
|
||||
|
||||
while l:linenr < min([50, line('$')])
|
||||
let l:linenr += 1
|
||||
let l:match = matchlist(getline(l:linenr), l:pattern)
|
||||
|
||||
if len(l:match) > 1 && index(l:possible_shell, l:match[1]) >= 0
|
||||
return l:match[1]
|
||||
endif
|
||||
endwhile
|
||||
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! ale#handlers#shellcheck#GetDialectArgument(buffer) abort
|
||||
let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
|
||||
let l:shell_type = ale#handlers#shellcheck#GetShellcheckDialectDirective(a:buffer)
|
||||
|
||||
if empty(l:shell_type)
|
||||
let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
|
||||
endif
|
||||
|
||||
if !empty(l:shell_type)
|
||||
" Use the dash dialect for /bin/ash, etc.
|
||||
|
|
|
@ -24,6 +24,8 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
|
|||
|
||||
if get(a:response, 'success', v:false) is v:true
|
||||
\&& get(a:response, 'body', v:null) isnot v:null
|
||||
let l:set_balloons = ale#Var(l:options.buffer, 'set_balloons')
|
||||
|
||||
" If we pass the show_documentation flag, we should show the full
|
||||
" documentation, and always in the preview window.
|
||||
if get(l:options, 'show_documentation', 0)
|
||||
|
@ -40,7 +42,7 @@ function! ale#hover#HandleTSServerResponse(conn_id, response) abort
|
|||
endif
|
||||
elseif get(l:options, 'hover_from_balloonexpr', 0)
|
||||
\&& exists('*balloon_show')
|
||||
\&& ale#Var(l:options.buffer, 'set_balloons')
|
||||
\&& (l:set_balloons is 1 || l:set_balloons is# 'hover')
|
||||
call balloon_show(a:response.body.displayString)
|
||||
elseif get(l:options, 'truncated_echo', 0)
|
||||
call ale#cursor#TruncatedEcho(split(a:response.body.displayString, "\n")[0])
|
||||
|
@ -216,9 +218,11 @@ function! ale#hover#HandleLSPResponse(conn_id, response) abort
|
|||
let [l:commands, l:lines] = ale#hover#ParseLSPResult(l:result.contents)
|
||||
|
||||
if !empty(l:lines)
|
||||
let l:set_balloons = ale#Var(l:options.buffer, 'set_balloons')
|
||||
|
||||
if get(l:options, 'hover_from_balloonexpr', 0)
|
||||
\&& exists('*balloon_show')
|
||||
\&& ale#Var(l:options.buffer, 'set_balloons')
|
||||
\&& (l:set_balloons is 1 || l:set_balloons is# 'hover')
|
||||
call balloon_show(join(l:lines, "\n"))
|
||||
elseif get(l:options, 'truncated_echo', 0)
|
||||
call ale#cursor#TruncatedEcho(l:lines[0])
|
||||
|
|
|
@ -44,6 +44,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
|
|||
\ 'definition': 0,
|
||||
\ 'typeDefinition': 0,
|
||||
\ 'symbol_search': 0,
|
||||
\ 'code_actions': 0,
|
||||
\ },
|
||||
\}
|
||||
endif
|
||||
|
@ -219,6 +220,14 @@ function! s:UpdateCapabilities(conn, capabilities) abort
|
|||
let a:conn.capabilities.rename = 1
|
||||
endif
|
||||
|
||||
if get(a:capabilities, 'codeActionProvider') is v:true
|
||||
let a:conn.capabilities.code_actions = 1
|
||||
endif
|
||||
|
||||
if type(get(a:capabilities, 'codeActionProvider')) is v:t_dict
|
||||
let a:conn.capabilities.code_actions = 1
|
||||
endif
|
||||
|
||||
if !empty(get(a:capabilities, 'completionProvider'))
|
||||
let a:conn.capabilities.completion = 1
|
||||
endif
|
||||
|
@ -350,6 +359,7 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
|
|||
let l:conn.capabilities.definition = 1
|
||||
let l:conn.capabilities.symbol_search = 1
|
||||
let l:conn.capabilities.rename = 1
|
||||
let l:conn.capabilities.code_actions = 1
|
||||
endfunction
|
||||
|
||||
function! s:SendInitMessage(conn) abort
|
||||
|
|
|
@ -172,3 +172,25 @@ function! ale#lsp#message#Rename(buffer, line, column, new_name) abort
|
|||
\ 'newName': a:new_name,
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#CodeAction(buffer, line, column, end_line, end_column, diagnostics) abort
|
||||
return [0, 'textDocument/codeAction', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'range': {
|
||||
\ 'start': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\ 'end': {'line': a:end_line - 1, 'character': a:end_column},
|
||||
\ },
|
||||
\ 'context': {
|
||||
\ 'diagnostics': a:diagnostics
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#ExecuteCommand(command, arguments) abort
|
||||
return [0, 'workspace/executeCommand', {
|
||||
\ 'command': a:command,
|
||||
\ 'arguments': a:arguments,
|
||||
\}]
|
||||
endfunction
|
||||
|
|
|
@ -56,6 +56,7 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
|
|||
endif
|
||||
|
||||
if has_key(l:diagnostic, 'relatedInformation')
|
||||
\ && l:diagnostic.relatedInformation isnot v:null
|
||||
let l:related = deepcopy(l:diagnostic.relatedInformation)
|
||||
call map(l:related, {key, val ->
|
||||
\ ale#path#FromURI(val.location.uri) .
|
||||
|
|
|
@ -103,3 +103,39 @@ function! ale#lsp#tsserver_message#OrganizeImports(buffer) abort
|
|||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#GetCodeFixes(buffer, line, column, end_line, end_column, error_codes) abort
|
||||
" The lines and columns are 1-based.
|
||||
" The errors codes must be a list of tsserver error codes to fix.
|
||||
return [0, 'ts@getCodeFixes', {
|
||||
\ 'startLine': a:line,
|
||||
\ 'startOffset': a:column,
|
||||
\ 'endLine': a:end_line,
|
||||
\ 'endOffset': a:end_column + 1,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\ 'errorCodes': a:error_codes,
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#GetApplicableRefactors(buffer, line, column, end_line, end_column) abort
|
||||
" The arguments for this request can also be just 'line' and 'offset'
|
||||
return [0, 'ts@getApplicableRefactors', {
|
||||
\ 'startLine': a:line,
|
||||
\ 'startOffset': a:column,
|
||||
\ 'endLine': a:end_line,
|
||||
\ 'endOffset': a:end_column + 1,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#tsserver_message#GetEditsForRefactor(buffer, line, column, end_line, end_column, refactor, action) abort
|
||||
return [0, 'ts@getEditsForRefactor', {
|
||||
\ 'startLine': a:line,
|
||||
\ 'startOffset': a:column,
|
||||
\ 'endLine': a:end_line,
|
||||
\ 'endOffset': a:end_column + 1,
|
||||
\ 'file': expand('#' . a:buffer . ':p'),
|
||||
\ 'refactor': a:refactor,
|
||||
\ 'action': a:action,
|
||||
\}]
|
||||
endfunction
|
||||
|
|
|
@ -85,36 +85,10 @@ function! ale#rename#HandleTSServerResponse(conn_id, response) abort
|
|||
\ },
|
||||
\ {
|
||||
\ 'should_save': 1,
|
||||
\ 'force_save': get(l:options, 'force_save'),
|
||||
\ },
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s: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#rename#HandleLSPResponse(conn_id, response) abort
|
||||
if has_key(a:response, 'id')
|
||||
\&& has_key(s:rename_map, a:response.id)
|
||||
|
@ -126,7 +100,7 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
|
|||
return
|
||||
endif
|
||||
|
||||
let l:changes_map = s:getChanges(a:response.result)
|
||||
let l:changes_map = ale#code_action#GetChanges(a:response.result)
|
||||
|
||||
if empty(l:changes_map)
|
||||
call s:message('No changes received from server')
|
||||
|
@ -134,34 +108,7 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
|
|||
return
|
||||
endif
|
||||
|
||||
let l:changes = []
|
||||
|
||||
for l:file_name in keys(l:changes_map)
|
||||
let l:text_edits = l: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
|
||||
let l:changes = ale#code_action#BuildChangesList(l:changes_map)
|
||||
|
||||
call ale#code_action#HandleCodeAction(
|
||||
\ {
|
||||
|
@ -170,7 +117,6 @@ function! ale#rename#HandleLSPResponse(conn_id, response) abort
|
|||
\ },
|
||||
\ {
|
||||
\ 'should_save': 1,
|
||||
\ 'force_save': get(l:options, 'force_save'),
|
||||
\ },
|
||||
\)
|
||||
endif
|
||||
|
@ -229,7 +175,7 @@ function! s:ExecuteRename(linter, options) abort
|
|||
call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
|
||||
endfunction
|
||||
|
||||
function! ale#rename#Execute(options) abort
|
||||
function! ale#rename#Execute() abort
|
||||
let l:lsp_linters = []
|
||||
|
||||
for l:linter in ale#linter#Get(&filetype)
|
||||
|
@ -257,7 +203,6 @@ function! ale#rename#Execute(options) abort
|
|||
call s:ExecuteRename(l:lsp_linter, {
|
||||
\ 'old_name': l:old_name,
|
||||
\ 'new_name': l:new_name,
|
||||
\ 'force_save': get(a:options, 'force_save') is 1,
|
||||
\})
|
||||
endfor
|
||||
endfunction
|
||||
|
|
|
@ -486,7 +486,7 @@ function! ale#util#Input(message, value) abort
|
|||
endfunction
|
||||
|
||||
function! ale#util#HasBuflineApi() abort
|
||||
return exists('*deletebufline') && exists('*setbufline')
|
||||
return exists('*deletebufline') && exists('*appendbufline') && exists('*getpos') && exists('*setpos')
|
||||
endfunction
|
||||
|
||||
" Sets buffer contents to lines
|
||||
|
@ -507,8 +507,11 @@ function! ale#util#SetBufferContents(buffer, lines) abort
|
|||
|
||||
" Use a Vim API for setting lines in other buffers, if available.
|
||||
if l:has_bufline_api
|
||||
call setbufline(a:buffer, 1, l:new_lines)
|
||||
call deletebufline(a:buffer, l:first_line_to_remove, '$')
|
||||
let l:save_cursor = getpos('.')
|
||||
call deletebufline(a:buffer, 1, '$')
|
||||
call appendbufline(a:buffer, 1, l:new_lines)
|
||||
call deletebufline(a:buffer, 1, 1)
|
||||
call setpos('.', l:save_cursor)
|
||||
" Fall back on setting lines the old way, for the current buffer.
|
||||
else
|
||||
let l:old_line_length = line('$')
|
||||
|
|
|
@ -31,6 +31,18 @@ g:ale_erlang_dialyzer_rebar3_profile *g:ale_erlang_dialyzer_rebar3_profile*
|
|||
This variable can be changed to specify the profile that is used to
|
||||
run dialyzer with rebar3.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
elvis *ale-erlang-elvis*
|
||||
|
||||
g:ale_erlang_elvis_executable *g:ale_erlang_elvis_executable*
|
||||
*b:ale_erlang_elvis_executable*
|
||||
Type: |String|
|
||||
Default: `'elvis'`
|
||||
|
||||
This variable can be changed to specify the elvis executable.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
erlc *ale-erlang-erlc*
|
||||
|
||||
|
|
|
@ -172,5 +172,25 @@ g:ale_haskell_hie_executable *g:ale_haskell_hie_executable*
|
|||
ide engine. i.e. `'hie-wrapper'`
|
||||
|
||||
|
||||
===============================================================================
|
||||
ormolu *ale-haskell-ormolu*
|
||||
|
||||
g:ale_haskell_ormolu_executable *g:ale_haskell_ormolu_executable*
|
||||
*b:ale_haskell_ormolu_executable*
|
||||
Type: |String|
|
||||
Default: `'ormolu'`
|
||||
|
||||
This variable can be changed to use a different executable for ormolu.
|
||||
|
||||
|
||||
g:ale_haskell_ormolu_options *g:ale_haskell_ormolu_options*
|
||||
*b:ale_haskell_ormolu_options*
|
||||
Type: String
|
||||
Default: ''
|
||||
|
||||
This variable can be used to pass extra options to the underlying ormolu
|
||||
executable.
|
||||
|
||||
|
||||
===============================================================================
|
||||
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
|
||||
|
|
|
@ -30,5 +30,21 @@ g:ale_lua_luacheck_options *g:ale_lua_luacheck_options*
|
|||
This variable can be set to pass additional options to luacheck.
|
||||
|
||||
|
||||
===============================================================================
|
||||
luafmt *ale-lua-luafmt*
|
||||
|
||||
g:ale_lua_luafmt_executable *g:ale_lua_luafmt_executable*
|
||||
*b:ale_lua_luafmt_executable*
|
||||
Type: |String|
|
||||
Default: `'luafmt'`
|
||||
|
||||
This variable can be set to use a different executable for luafmt.
|
||||
|
||||
g:ale_lua_luafmt_options *g:ale_lua_luafmt_options*
|
||||
*b:ale_lua_luafmt_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable can be set to pass additional options to the luafmt fixer.
|
||||
===============================================================================
|
||||
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
|
||||
|
|
|
@ -85,6 +85,14 @@ g:ale_php_phpcbf_use_global *g:ale_php_phpcbf_use_global*
|
|||
See |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_php_phpcbf_options *g:ale_php_phpcbf_options*
|
||||
*b:ale_php_phpcbf_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable can be set to pass additional options to php-cbf
|
||||
|
||||
|
||||
===============================================================================
|
||||
phpcs *ale-php-phpcs*
|
||||
|
||||
|
@ -243,5 +251,70 @@ g:ale_php_php_executable *g:ale_php_php_executable*
|
|||
|
||||
This variable sets the executable used for php.
|
||||
|
||||
|
||||
===============================================================================
|
||||
tlint *ale-php-tlint*
|
||||
|
||||
g:ale_php_tlint_executable *g:ale_php_tlint_executable*
|
||||
*b:ale_php_tlint_executable*
|
||||
Type: |String|
|
||||
Default: `'tlint'`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_php_tlint_use_global *g:ale_php_tlint_use_global*
|
||||
*b:ale_php_tlint_use_global*
|
||||
Type: |Number|
|
||||
Default: `get(g:, 'ale_use_global_executables', 0)`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_php_tlint_options *g:ale_php_tlint_options*
|
||||
*b:ale_php_tlint_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable can be set to pass additional options to tlint
|
||||
|
||||
|
||||
===============================================================================
|
||||
intelephense *ale-php-intelephense*
|
||||
|
||||
g:ale_php_intelephense_executable *g:ale_php_intelephense_executable*
|
||||
*b:ale_php_intelephense_executable*
|
||||
Type: |String|
|
||||
Default: `'intelephense'`
|
||||
|
||||
The variable can be set to configure the executable that will be used for
|
||||
running the intelephense language server. `node_modules` directory
|
||||
executable will be preferred instead of this setting if
|
||||
|g:ale_php_intelephense_use_global| is `0`.
|
||||
|
||||
See: |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_php_intelephense_use_global *g:ale_php_intelephense_use_global*
|
||||
*b:ale_php_intelephense_use_global*
|
||||
Type: |Number|
|
||||
Default: `get(g:, 'ale_use_global_executables', 0)`
|
||||
|
||||
This variable can be set to `1` to force the language server to be run with
|
||||
the executable set for |g:ale_php_intelephense_executable|.
|
||||
|
||||
See: |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_php_intelephense_config *g:ale_php_intelephense_config*
|
||||
*b:ale_php_intelephense_config*
|
||||
Type: |Dictionary|
|
||||
Default: `{}`
|
||||
|
||||
The initialization options config specified by Intelephense. Refer to the
|
||||
installation docs provided by intelephense (github.com/bmewburn/intelephense
|
||||
-docs).
|
||||
|
||||
|
||||
===============================================================================
|
||||
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:
|
||||
|
|
|
@ -41,6 +41,32 @@ ALE will look for configuration files with the following filenames. >
|
|||
The first directory containing any of the files named above will be used.
|
||||
|
||||
|
||||
===============================================================================
|
||||
autoimport *ale-python-autoimport*
|
||||
|
||||
g:ale_python_autoimport_executable *g:ale_python_autoimport_executable*
|
||||
*b:ale_python_autoimport_executable*
|
||||
Type: |String|
|
||||
Default: `'autoimport'`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_python_autoimport_options *g:ale_python_autoimport_options*
|
||||
*b:ale_python_autoimport_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable can be set to pass extra options to autoimport.
|
||||
|
||||
|
||||
g:ale_python_autoimport_use_global *g:ale_python_autoimport_use_global*
|
||||
*b:ale_python_autoimport_use_global*
|
||||
Type: |Number|
|
||||
Default: `get(g:, 'ale_use_global_executables', 0)`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
===============================================================================
|
||||
autopep8 *ale-python-autopep8*
|
||||
|
||||
|
@ -687,7 +713,7 @@ g:ale_python_pyre_auto_pipenv *g:ale_python_pyre_auto_pipenv*
|
|||
===============================================================================
|
||||
pyright *ale-python-pyright*
|
||||
|
||||
The `pyrlight` linter requires a recent version of `pyright` which includes
|
||||
The `pyright` linter requires a recent version of `pyright` which includes
|
||||
the `pyright-langserver` executable. You can install `pyright` on your system
|
||||
through `npm` with `sudo npm install -g pyright` or similar.
|
||||
|
||||
|
|
|
@ -2,6 +2,29 @@
|
|||
ALE R Integration *ale-r-options*
|
||||
|
||||
|
||||
===============================================================================
|
||||
languageserver *ale-r-languageserver*
|
||||
|
||||
g:ale_r_languageserver_cmd *g:ale_r_languageserver_cmd*
|
||||
*b:ale_r_languageserver_cmd*
|
||||
Type: |String|
|
||||
Default: `'languageserver::run()'`
|
||||
|
||||
This option can be configured to change the execution command for
|
||||
languageserver.
|
||||
|
||||
See the languageserver documentation for more options.
|
||||
|
||||
|
||||
g:ale_r_languageserver_config *g:ale_r_languageserver_config*
|
||||
*b:ale_r_languageserver_config*
|
||||
Type: |Dictionary|
|
||||
Default: `{}`
|
||||
|
||||
This option can be configured to change settings for languageserver. See the
|
||||
languageserver documentation for more information.
|
||||
|
||||
|
||||
===============================================================================
|
||||
lintr *ale-r-lintr*
|
||||
|
||||
|
@ -22,7 +45,7 @@ g:ale_r_lintr_lint_package *g:ale_r_lintr_lint_package*
|
|||
Default: `0`
|
||||
|
||||
When set to `1`, the file will be checked with `lintr::lint_package` instead
|
||||
of `lintr::lint`. This prevents erroneous namespace warnings when linting
|
||||
of `lintr::lint`. This prevents erroneous namespace warnings when linting
|
||||
package files.
|
||||
|
||||
|
||||
|
@ -36,8 +59,8 @@ g:ale_r_styler_options *g:ale_r_styler_options*
|
|||
|
||||
This option can be configured to change the options for styler.
|
||||
|
||||
The value of this option will be used as the `style` argument for the
|
||||
`styler::style_file` options. Consult the styler documentation
|
||||
The value of this option will be used as the `style` argument for the
|
||||
`styler::style_file` options. Consult the styler documentation
|
||||
for more information.
|
||||
|
||||
|
||||
|
|
|
@ -177,6 +177,16 @@ g:ale_ruby_sorbet_options *g:ale_ruby_sorbet_options*
|
|||
This variable can be changed to modify flags given to sorbet.
|
||||
|
||||
|
||||
g:ale_ruby_sorbet_enable_watchman *g:ale_ruby_sorbet_enable_watchman*
|
||||
*b:ale_ruby_sorbet_enable_watchman*
|
||||
Type: |Number|
|
||||
Default: `0`
|
||||
|
||||
Whether or not to use watchman to let the LSP server to know about changes
|
||||
to files from outside of vim. Defaults to disable watchman because it
|
||||
requires watchman to be installed separately from sorbet.
|
||||
|
||||
|
||||
===============================================================================
|
||||
standardrb *ale-ruby-standardrb*
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ Integration Information
|
|||
3. rls -- If you have `rls` installed, you might prefer using this linter
|
||||
over cargo. rls implements the Language Server Protocol for incremental
|
||||
compilation of Rust code, and can check Rust files while you type. `rls`
|
||||
requires Rust files to contained in Cargo projects.
|
||||
requires Rust files to be contained in Cargo projects.
|
||||
4. analyzer -- If you have rust-analyzer installed, you might prefer using
|
||||
this linter over cargo and rls. rust-analyzer also implements the
|
||||
Language Server Protocol for incremental compilation of Rust code, and is
|
||||
the next iteration of rls. rust-analyzer, like rls, requires Rust files
|
||||
to contained in Cargo projects.
|
||||
to be contained in Cargo projects.
|
||||
5. rustfmt -- If you have `rustfmt` installed, you can use it as a fixer to
|
||||
consistently reformat your Rust code.
|
||||
|
||||
|
|
|
@ -142,6 +142,7 @@ Notes:
|
|||
* `erubis`
|
||||
* `ruumba`
|
||||
* Erlang
|
||||
* `elvis`!!
|
||||
* `erlc`
|
||||
* `SyntaxErl`
|
||||
* Fish
|
||||
|
@ -197,6 +198,7 @@ Notes:
|
|||
* `hie`
|
||||
* `hindent`
|
||||
* `hlint`
|
||||
* `ormolu`
|
||||
* `stack-build`!!
|
||||
* `stack-ghc`
|
||||
* `stylish-haskell`
|
||||
|
@ -267,6 +269,7 @@ Notes:
|
|||
* Lua
|
||||
* `luac`
|
||||
* `luacheck`
|
||||
* `luafmt`
|
||||
* Mail
|
||||
* `alex`!!
|
||||
* `languagetool`!!
|
||||
|
@ -326,6 +329,7 @@ Notes:
|
|||
* Perl6
|
||||
* `perl6 -c`
|
||||
* PHP
|
||||
* `intelephense`
|
||||
* `langserver`
|
||||
* `phan`
|
||||
* `phpcbf`
|
||||
|
@ -335,6 +339,7 @@ Notes:
|
|||
* `phpmd`
|
||||
* `phpstan`
|
||||
* `psalm`!!
|
||||
* `tlint`
|
||||
* PO
|
||||
* `alex`!!
|
||||
* `msgfmt`
|
||||
|
@ -363,6 +368,7 @@ Notes:
|
|||
* `purescript-language-server`
|
||||
* `purty`
|
||||
* Python
|
||||
* `autoimport`
|
||||
* `autopep8`
|
||||
* `bandit`
|
||||
* `black`
|
||||
|
@ -385,6 +391,7 @@ Notes:
|
|||
* `qmlfmt`
|
||||
* `qmllint`
|
||||
* R
|
||||
* `languageserver`
|
||||
* `lintr`
|
||||
* `styler`
|
||||
* Racket
|
||||
|
@ -519,6 +526,7 @@ Notes:
|
|||
* YAML
|
||||
* `prettier`
|
||||
* `swaglint`
|
||||
* `yamlfix`
|
||||
* `yamllint`
|
||||
* YANG
|
||||
* `yang-lsp`
|
||||
|
|
|
@ -15,7 +15,6 @@ Install prettier either globally or locally: >
|
|||
npm install prettier -g # global
|
||||
npm install prettier # local
|
||||
<
|
||||
|
||||
===============================================================================
|
||||
swaglint *ale-yaml-swaglint*
|
||||
|
||||
|
@ -49,6 +48,43 @@ g:ale_yaml_swaglint_use_global *g:ale_yaml_swaglint_use_global*
|
|||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
===============================================================================
|
||||
yamlfix *ale-yaml-yamlfix*
|
||||
|
||||
Website: https://lyz-code.github.io/yamlfix
|
||||
|
||||
|
||||
Installation
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Install yamlfix: >
|
||||
|
||||
pip install yamlfix
|
||||
<
|
||||
|
||||
Options
|
||||
-------------------------------------------------------------------------------
|
||||
g:ale_yaml_yamlfix_executable *g:ale_yaml_yamlfix_executable*
|
||||
*b:ale_yaml_yamlfix_executable*
|
||||
Type: |String|
|
||||
Default: `'yamlfix'`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
|
||||
g:ale_yaml_yamlfix_options *g:ale_yaml_yamlfix_options*
|
||||
*b:ale_yaml_yamlfix_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable can be set to pass extra options to yamlfix.
|
||||
|
||||
g:ale_yaml_yamlfix_use_global *g:ale_yaml_yamlfix_use_global*
|
||||
*b:ale_yaml_yamlfix_use_global*
|
||||
Type: |Number|
|
||||
Default: `get(g:, 'ale_use_global_executables', 0)`
|
||||
|
||||
See |ale-integrations-local-executables|
|
||||
|
||||
===============================================================================
|
||||
yamllint *ale-yaml-yamllint*
|
||||
|
|
76
doc/ale.txt
76
doc/ale.txt
|
@ -20,6 +20,7 @@ CONTENTS *ale-contents*
|
|||
5.4 Find References...................|ale-find-references|
|
||||
5.5 Hovering..........................|ale-hover|
|
||||
5.6 Symbol Search.....................|ale-symbol-search|
|
||||
5.7 Refactoring: Rename, Actions......|ale-refactor|
|
||||
6. Global Options.......................|ale-options|
|
||||
6.1 Highlights........................|ale-highlights|
|
||||
7. Linter/Fixer Options.................|ale-integration-options|
|
||||
|
@ -669,6 +670,34 @@ ALE supports searching for workspace symbols via LSP linters with the
|
|||
|ALESymbolSearch| command. See the documentation for the command
|
||||
for a full list of options.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
5.7 Refactoring: Rename, Actions *ale-refactor*
|
||||
|
||||
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*
|
||||
|
||||
|
@ -1665,6 +1694,7 @@ g:ale_lsp_root *g:ale_lsp_root*
|
|||
If neither variable yields a result, a linter-specific function is invoked to
|
||||
detect a project root. If this, too, yields no result, the linter is disabled.
|
||||
|
||||
|
||||
g:ale_max_buffer_history_size *g:ale_max_buffer_history_size*
|
||||
|
||||
Type: |Number|
|
||||
|
@ -1773,6 +1803,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_running')`
|
||||
|
||||
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|
|
||||
|
@ -1797,7 +1840,7 @@ g:ale_rename_tsserver_find_in_strings *g:ale_rename_tsserver_find_in_strings*
|
|||
g:ale_set_balloons *g:ale_set_balloons*
|
||||
*b:ale_set_balloons*
|
||||
|
||||
Type: |Number|
|
||||
Type: |Number| or |String|
|
||||
Default: `has('balloon_eval') && has('gui_running')`
|
||||
|
||||
When this option is set to `1`, balloon messages will be displayed for
|
||||
|
@ -1808,6 +1851,13 @@ g:ale_set_balloons *g:ale_set_balloons*
|
|||
supporting "Hover" information, per |ale-hover|, then brief information
|
||||
about the symbol under the cursor will be displayed in a balloon.
|
||||
|
||||
This option can be set to `'hover'` to only enable balloons for hover
|
||||
message, so diagnostics are never shown in balloons. You may wish to
|
||||
configure use this setting only in GUI Vim like so: >
|
||||
|
||||
let g:ale_set_balloons = has('gui_running') ? 'hover' : 0
|
||||
<
|
||||
|
||||
Balloons can be enabled for terminal versions of Vim that support balloons,
|
||||
but some versions of Vim will produce strange mouse behavior when balloons
|
||||
are enabled. To configure balloons for your terminal, you should first
|
||||
|
@ -2593,6 +2643,7 @@ documented in additional help files.
|
|||
elm-make..............................|ale-elm-elm-make|
|
||||
erlang..................................|ale-erlang-options|
|
||||
dialyzer..............................|ale-erlang-dialyzer|
|
||||
elvis.................................|ale-erlang-elvis|
|
||||
erlc..................................|ale-erlang-erlc|
|
||||
syntaxerl.............................|ale-erlang-syntaxerl|
|
||||
eruby...................................|ale-eruby-options|
|
||||
|
@ -2646,6 +2697,7 @@ documented in additional help files.
|
|||
stack-ghc.............................|ale-haskell-stack-ghc|
|
||||
stylish-haskell.......................|ale-haskell-stylish-haskell|
|
||||
hie...................................|ale-haskell-hie|
|
||||
ormolu................................|ale-haskell-ormolu|
|
||||
hcl.....................................|ale-hcl-options|
|
||||
terraform-fmt.........................|ale-hcl-terraform-fmt|
|
||||
html....................................|ale-html-options|
|
||||
|
@ -2705,6 +2757,7 @@ documented in additional help files.
|
|||
lua.....................................|ale-lua-options|
|
||||
luac..................................|ale-lua-luac|
|
||||
luacheck..............................|ale-lua-luacheck|
|
||||
luafmt................................|ale-lua-luafmt|
|
||||
markdown................................|ale-markdown-options|
|
||||
markdownlint..........................|ale-markdown-markdownlint|
|
||||
mdl...................................|ale-markdown-mdl|
|
||||
|
@ -2756,6 +2809,8 @@ documented in additional help files.
|
|||
psalm.................................|ale-php-psalm|
|
||||
php-cs-fixer..........................|ale-php-php-cs-fixer|
|
||||
php...................................|ale-php-php|
|
||||
tlint.................................|ale-php-tlint|
|
||||
intelephense..........................|ale-php-intelephense|
|
||||
po......................................|ale-po-options|
|
||||
write-good............................|ale-po-write-good|
|
||||
pod.....................................|ale-pod-options|
|
||||
|
@ -2781,6 +2836,7 @@ documented in additional help files.
|
|||
pyrex (cython)..........................|ale-pyrex-options|
|
||||
cython................................|ale-pyrex-cython|
|
||||
python..................................|ale-python-options|
|
||||
autoimport............................|ale-python-autoimport|
|
||||
autopep8..............................|ale-python-autopep8|
|
||||
bandit................................|ale-python-bandit|
|
||||
black.................................|ale-python-black|
|
||||
|
@ -2802,6 +2858,7 @@ documented in additional help files.
|
|||
qml.....................................|ale-qml-options|
|
||||
qmlfmt................................|ale-qml-qmlfmt|
|
||||
r.......................................|ale-r-options|
|
||||
languageserver........................|ale-r-languageserver|
|
||||
lintr.................................|ale-r-lintr|
|
||||
styler................................|ale-r-styler|
|
||||
reasonml................................|ale-reasonml-options|
|
||||
|
@ -2918,6 +2975,7 @@ documented in additional help files.
|
|||
yaml....................................|ale-yaml-options|
|
||||
prettier..............................|ale-yaml-prettier|
|
||||
swaglint..............................|ale-yaml-swaglint|
|
||||
yamlfix...............................|ale-yaml-yamlfix|
|
||||
yamllint..............................|ale-yaml-yamllint|
|
||||
yang....................................|ale-yang-options|
|
||||
yang-lsp..............................|ale-yang-lsp|
|
||||
|
@ -3103,11 +3161,17 @@ ALERename *ALERename*
|
|||
The symbol where the cursor is resting will be the symbol renamed, and a
|
||||
prompt will open to request a new name.
|
||||
|
||||
ALE will refuse to complete a rename operation if there are files to modify
|
||||
which have not yet been saved in Vim. If the command is run with a bang
|
||||
(`:ALERename!`), all warnings will be suppressed, and files that are still
|
||||
open in Vim and not saved will be ignored and left in a state where symbols
|
||||
in those files will not be updated.
|
||||
|
||||
ALECodeAction *ALECodeAction*
|
||||
|
||||
Apply a code action via LSP servers or `tsserver`.
|
||||
|
||||
If there is an error present on a line that can be fixed, ALE will
|
||||
automatically fix a line, unless there are multiple possible code fixes to
|
||||
apply.
|
||||
|
||||
This command can be run in visual mode apply actions, such as applicable
|
||||
refactors. A menu will be shown to select code action to apply.
|
||||
|
||||
|
||||
ALERepeatSelection *ALERepeatSelection*
|
||||
|
|
|
@ -158,7 +158,10 @@ 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 g:ale_set_balloons
|
||||
" If 1, enable a popup menu for commands.
|
||||
let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui_running'))
|
||||
|
||||
if g:ale_set_balloons is 1 || g:ale_set_balloons is# 'hover'
|
||||
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>)
|
||||
|
@ -238,7 +245,10 @@ command! -bar ALEComplete :call ale#completion#GetCompletions('ale-manual')
|
|||
command! -bar ALEImport :call ale#completion#Import()
|
||||
|
||||
" Rename symbols using tsserver and LSP
|
||||
command! -bar -bang ALERename :call ale#rename#Execute({'force_save': '<bang>' is# '!'})
|
||||
command! -bar -bang ALERename :call ale#rename#Execute()
|
||||
|
||||
" Apply code actions to a range.
|
||||
command! -bar -range ALECodeAction :call ale#codefix#Execute(<range>)
|
||||
|
||||
" Organize import statements using tsserver
|
||||
command! -bar ALEOrganizeImports :call ale#organize_imports#Execute()
|
||||
|
@ -283,6 +293,7 @@ nnoremap <silent> <Plug>(ale_documentation) :ALEDocumentation<Return>
|
|||
inoremap <silent> <Plug>(ale_complete) <C-\><C-O>:ALEComplete<Return>
|
||||
nnoremap <silent> <Plug>(ale_import) :ALEImport<Return>
|
||||
nnoremap <silent> <Plug>(ale_rename) :ALERename<Return>
|
||||
nnoremap <silent> <Plug>(ale_code_action) :ALECodeAction<Return>
|
||||
nnoremap <silent> <Plug>(ale_repeat_selection) :ALERepeatSelection<Return>
|
||||
|
||||
" Set up autocmd groups now.
|
||||
|
|
|
@ -151,6 +151,7 @@ formatting.
|
|||
* [erubis](https://github.com/kwatch/erubis)
|
||||
* [ruumba](https://github.com/ericqweinstein/ruumba)
|
||||
* Erlang
|
||||
* [elvis](https://github.com/inaka/elvis) :floppy_disk:
|
||||
* [erlc](http://erlang.org/doc/man/erlc.html)
|
||||
* [SyntaxErl](https://github.com/ten0s/syntaxerl)
|
||||
* Fish
|
||||
|
@ -206,6 +207,7 @@ formatting.
|
|||
* [hie](https://github.com/haskell/haskell-ide-engine)
|
||||
* [hindent](https://hackage.haskell.org/package/hindent)
|
||||
* [hlint](https://hackage.haskell.org/package/hlint)
|
||||
* [ormolu](https://github.com/tweag/ormolu)
|
||||
* [stack-build](https://haskellstack.org/) :floppy_disk:
|
||||
* [stack-ghc](https://haskellstack.org/)
|
||||
* [stylish-haskell](https://github.com/jaspervdj/stylish-haskell)
|
||||
|
@ -276,6 +278,7 @@ formatting.
|
|||
* Lua
|
||||
* [luac](https://www.lua.org/manual/5.1/luac.html)
|
||||
* [luacheck](https://github.com/mpeterv/luacheck)
|
||||
* [luafmt](https://github.com/trixnz/lua-fmt)
|
||||
* Mail
|
||||
* [alex](https://github.com/wooorm/alex) :floppy_disk:
|
||||
* [languagetool](https://languagetool.org/) :floppy_disk:
|
||||
|
@ -335,6 +338,7 @@ formatting.
|
|||
* Perl6
|
||||
* [perl6 -c](https://perl6.org) :warning:
|
||||
* PHP
|
||||
* [intelephense](https://github.com/bmewburn/intelephense-docs)
|
||||
* [langserver](https://github.com/felixfbecker/php-language-server)
|
||||
* [phan](https://github.com/phan/phan) see `:help ale-php-phan` to instructions
|
||||
* [phpcbf](https://github.com/squizlabs/PHP_CodeSniffer)
|
||||
|
@ -344,6 +348,7 @@ formatting.
|
|||
* [phpmd](https://phpmd.org)
|
||||
* [phpstan](https://github.com/phpstan/phpstan)
|
||||
* [psalm](https://getpsalm.org) :floppy_disk:
|
||||
* [tlint](https://github.com/tightenco/tlint)
|
||||
* PO
|
||||
* [alex](https://github.com/wooorm/alex) :floppy_disk:
|
||||
* [msgfmt](https://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html)
|
||||
|
@ -372,6 +377,7 @@ formatting.
|
|||
* [purescript-language-server](https://github.com/nwolverson/purescript-language-server)
|
||||
* [purty](https://gitlab.com/joneshf/purty)
|
||||
* Python
|
||||
* [autoimport](https://lyz-code.github.io/autoimport/)
|
||||
* [autopep8](https://github.com/hhatto/autopep8)
|
||||
* [bandit](https://github.com/PyCQA/bandit) :warning:
|
||||
* [black](https://github.com/ambv/black)
|
||||
|
@ -394,6 +400,7 @@ formatting.
|
|||
* [qmlfmt](https://github.com/jesperhh/qmlfmt)
|
||||
* [qmllint](https://github.com/qt/qtdeclarative/tree/5.11/tools/qmllint)
|
||||
* R
|
||||
* [languageserver](https://github.com/REditorSupport/languageserver)
|
||||
* [lintr](https://github.com/jimhester/lintr)
|
||||
* [styler](https://github.com/r-lib/styler)
|
||||
* Racket
|
||||
|
@ -528,6 +535,7 @@ formatting.
|
|||
* YAML
|
||||
* [prettier](https://github.com/prettier/prettier)
|
||||
* [swaglint](https://github.com/byCedric/swaglint)
|
||||
* [yamlfix](https://lyz-code.github.io/yamlfix)
|
||||
* [yamllint](https://yamllint.readthedocs.io/)
|
||||
* YANG
|
||||
* [yang-lsp](https://github.com/theia-ide/yang-lsp)
|
||||
|
|
0
test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe
vendored
Executable file
0
test/command_callback/python_paths/with_virtualenv/env/Scripts/autoimport.exe
vendored
Executable file
0
test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe
vendored
Normal file
0
test/command_callback/python_paths/with_virtualenv/env/Scripts/yamlfix.exe
vendored
Normal file
0
test/command_callback/python_paths/with_virtualenv/env/bin/autoimport
vendored
Executable file
0
test/command_callback/python_paths/with_virtualenv/env/bin/autoimport
vendored
Executable file
0
test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix
vendored
Executable file
0
test/command_callback/python_paths/with_virtualenv/env/bin/yamlfix
vendored
Executable file
0
test/command_callback/r_paths/.Rprofile
Normal file
0
test/command_callback/r_paths/.Rprofile
Normal file
|
@ -0,0 +1,16 @@
|
|||
Before:
|
||||
let b:file = fnamemodify(bufname(''), ':.')
|
||||
call ale#assert#SetUpLinterTest('erlang', 'elvis')
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(Default command should be correct):
|
||||
AssertLinter 'elvis',
|
||||
\ ale#Escape('elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file)
|
||||
|
||||
Execute(Executable should be configurable):
|
||||
let b:ale_erlang_elvis_executable = '/path/to/elvis'
|
||||
|
||||
AssertLinter '/path/to/elvis',
|
||||
\ ale#Escape('/path/to/elvis') . ' rock --output-format=parsable ' . ale#Escape(b:file)
|
|
@ -0,0 +1,26 @@
|
|||
Before:
|
||||
call ale#assert#SetUpLinterTest('php', 'intelephense')
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(The default executable path should be correct):
|
||||
AssertLinter 'intelephense',
|
||||
\ ale#Escape('intelephense') . ' --stdio'
|
||||
|
||||
Execute(The project path should be correct for .git directories):
|
||||
call ale#test#SetFilename('php-intelephense-project/with-git/test.php')
|
||||
silent! call mkdir('php-intelephense-project/with-git/.git', 'p')
|
||||
|
||||
AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-git')
|
||||
|
||||
Execute(The project path should be correct for composer.json file):
|
||||
call ale#test#SetFilename('php-intelephense-project/with-composer/test.php')
|
||||
|
||||
AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer')
|
||||
|
||||
Execute(The project cache should be saved in a temp dir):
|
||||
call ale#test#SetFilename('php-intelephense-project/with-composer/test.php')
|
||||
let g:ale_php_intelephense_config = { 'storagePath': '/tmp/intelephense' }
|
||||
|
||||
AssertLSPProject ale#path#Simplify(g:dir . '/php-intelephense-project/with-composer')
|
22
test/command_callback/test_r_languageserver_callbacks.vader
Normal file
22
test/command_callback/test_r_languageserver_callbacks.vader
Normal file
|
@ -0,0 +1,22 @@
|
|||
Before:
|
||||
call ale#assert#SetUpLinterTest('r', 'languageserver')
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(The default executable path should be correct):
|
||||
AssertLinter 'Rscript', 'Rscript --vanilla -e ' . ale#Escape('languageserver::run()')
|
||||
|
||||
Execute(The project root should be detected correctly):
|
||||
AssertLSPProject '.'
|
||||
|
||||
call ale#test#SetFilename('r_paths/dummy/test.R')
|
||||
|
||||
AssertLSPProject ale#path#Simplify(g:dir . '/r_paths')
|
||||
|
||||
Execute(Should accept configuration settings):
|
||||
AssertLSPConfig {}
|
||||
|
||||
let b:ale_r_languageserver_config = {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}}
|
||||
|
||||
AssertLSPConfig {'r': {'lsp': {'debug': 'true', 'diagnostics': 'true'}}}
|
|
@ -5,6 +5,7 @@ Before:
|
|||
|
||||
let g:ale_ruby_sorbet_executable = 'srb'
|
||||
let g:ale_ruby_sorbet_options = ''
|
||||
let g:ale_ruby_sorbet_enable_watchman = 0
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
@ -13,6 +14,12 @@ Execute(Executable should default to srb):
|
|||
AssertLinter 'srb', ale#Escape('srb')
|
||||
\ . ' tc --lsp --disable-watchman'
|
||||
|
||||
Execute(Able to enable watchman):
|
||||
let g:ale_ruby_sorbet_enable_watchman = 1
|
||||
|
||||
AssertLinter 'srb', ale#Escape('srb')
|
||||
\ . ' tc --lsp'
|
||||
|
||||
Execute(Should be able to set a custom executable):
|
||||
let g:ale_ruby_sorbet_executable = 'bin/srb'
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ Execute(Should handle Rust completion results correctly):
|
|||
\ {'word': 'from', 'menu': 'fn from(s: &''a str) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
|
||||
\ {'word': 'from', 'menu': 'fn from(s: Box<str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
|
||||
\ {'word': 'from', 'menu': 'fn from(s: Cow<''a, str>) -> String', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
|
||||
\ {'word': 'to_vec', 'menu': 'pub fn to_vec(&self) -> Vec<T> where T: Clone,', 'info': '', 'kind': 'f', 'icase': 1, 'user_data': json_encode({'_ale_completion_item': 1})},
|
||||
\],
|
||||
\ ale#completion#ParseLSPCompletions({
|
||||
\ "jsonrpc":"2.0",
|
||||
|
@ -184,6 +185,11 @@ Execute(Should handle Rust completion results correctly):
|
|||
\ "label":"from",
|
||||
\ "kind":3,
|
||||
\ "detail":"fn from(s: Cow<'a, str>) -> String"
|
||||
\ },
|
||||
\ {
|
||||
\ "label":"to_vec",
|
||||
\ "kind":3,
|
||||
\ "detail":"pub fn to_vec(&self) -> Vec<T>\nwhere\n T: Clone,"
|
||||
\ }
|
||||
\ ]
|
||||
\ })
|
||||
|
|
50
test/fixers/test_autoimport_fixer_callback.vader
Normal file
50
test/fixers/test_autoimport_fixer_callback.vader
Normal file
|
@ -0,0 +1,50 @@
|
|||
Before:
|
||||
Save g:ale_python_autoimport_executable
|
||||
Save g:ale_python_autoimport_options
|
||||
|
||||
" Use an invalid global executable, so we don't match it.
|
||||
let g:ale_python_autoimport_executable = 'xxxinvalid'
|
||||
let g:ale_python_autoimport_options = ''
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/fixers')
|
||||
silent cd ..
|
||||
silent cd command_callback
|
||||
let g:dir = getcwd()
|
||||
|
||||
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
unlet! b:bin_dir
|
||||
|
||||
call ale#test#RestoreDirectory()
|
||||
|
||||
Execute(The autoimport callback should return the correct default values):
|
||||
AssertEqual
|
||||
\ 0,
|
||||
\ ale#fixers#autoimport#Fix(bufnr(''))
|
||||
|
||||
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#path#BufferCdString(bufnr(''))
|
||||
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport')) . ' -',
|
||||
\ },
|
||||
\ ale#fixers#autoimport#Fix(bufnr(''))
|
||||
|
||||
Execute(The autoimport callback should respect custom options):
|
||||
let g:ale_python_autoimport_options = '--multi-line=3 --trailing-comma'
|
||||
|
||||
AssertEqual
|
||||
\ 0,
|
||||
\ ale#fixers#autoimport#Fix(bufnr(''))
|
||||
|
||||
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.py')
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#path#BufferCdString(bufnr(''))
|
||||
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/autoimport'))
|
||||
\ . ' --multi-line=3 --trailing-comma -',
|
||||
\ },
|
||||
\ ale#fixers#autoimport#Fix(bufnr(''))
|
|
@ -21,10 +21,7 @@ Execute(The gofmt callback should return the correct default values):
|
|||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'read_temporary_file': 1,
|
||||
\ 'command': ale#Escape('xxxinvalid')
|
||||
\ . ' -l -w'
|
||||
\ . ' %t',
|
||||
\ 'command': ale#Escape('xxxinvalid'),
|
||||
\ },
|
||||
\ ale#fixers#gofmt#Fix(bufnr(''))
|
||||
|
||||
|
@ -35,11 +32,8 @@ Execute(The gofmt callback should include custom gofmt options):
|
|||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'read_temporary_file': 1,
|
||||
\ 'command': ale#Escape('xxxinvalid')
|
||||
\ . ' -l -w'
|
||||
\ . ' ' . g:ale_go_gofmt_options
|
||||
\ . ' %t',
|
||||
\ . ' ' . g:ale_go_gofmt_options,
|
||||
\ },
|
||||
\ ale#fixers#gofmt#Fix(bufnr(''))
|
||||
|
||||
|
@ -50,9 +44,7 @@ Execute(The gofmt callback should support Go environment variables):
|
|||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'read_temporary_file': 1,
|
||||
\ 'command': ale#Env('GO111MODULE', 'off')
|
||||
\ . ale#Escape('xxxinvalid') . ' -l -w'
|
||||
\ . ' %t',
|
||||
\ . ale#Escape('xxxinvalid')
|
||||
\ },
|
||||
\ ale#fixers#gofmt#Fix(bufnr(''))
|
||||
|
|
35
test/fixers/test_luafmt_fixer_callback.vader
Normal file
35
test/fixers/test_luafmt_fixer_callback.vader
Normal file
|
@ -0,0 +1,35 @@
|
|||
Before:
|
||||
Save g:ale_lua_luafmt_executable
|
||||
Save g:ale_lua_luafmt_options
|
||||
|
||||
" Use an invalid global executable, so we don't match it.
|
||||
let g:ale_lua_luafmt_executable = 'xxxinvalid'
|
||||
let g:ale_lua_luafmt_options = ''
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/fixers')
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
call ale#test#RestoreDirectory()
|
||||
|
||||
Execute(The luafmt callback should return the correct default values):
|
||||
call ale#test#SetFilename('../lua_files/testfile.lua')
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#Escape('xxxinvalid') . ' --stdin',
|
||||
\ },
|
||||
\ ale#fixers#luafmt#Fix(bufnr(''))
|
||||
|
||||
Execute(The luafmt callback should include custom luafmt options):
|
||||
let g:ale_lua_luafmt_options = "--skip-children"
|
||||
call ale#test#SetFilename('../lua_files/testfile.lua')
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#Escape('xxxinvalid')
|
||||
\ . ' ' . g:ale_lua_luafmt_options
|
||||
\ . ' --stdin',
|
||||
\ },
|
||||
\ ale#fixers#luafmt#Fix(bufnr(''))
|
24
test/fixers/test_ormolu_fixer_callback.vader
Normal file
24
test/fixers/test_ormolu_fixer_callback.vader
Normal file
|
@ -0,0 +1,24 @@
|
|||
Before:
|
||||
Save g:ale_haskell_ormolu_executable
|
||||
Save g:ale_haskell_ormolu_options
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
Execute(The ormolu callback should return the correct default values):
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#Escape('ormolu')
|
||||
\ },
|
||||
\ ale#fixers#ormolu#Fix(bufnr(''))
|
||||
|
||||
Execute(The ormolu executable and options should be configurable):
|
||||
let g:ale_nix_nixpkgsfmt_executable = '/path/to/ormolu'
|
||||
let g:ale_nix_nixpkgsfmt_options = '-h'
|
||||
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#Escape('/path/to/ormolu')
|
||||
\ . ' -h',
|
||||
\ },
|
||||
\ ale#fixers#nixpkgsfmt#Fix(bufnr(''))
|
|
@ -5,6 +5,7 @@ Before:
|
|||
|
||||
let g:ale_php_phpcbf_executable = 'phpcbf_test'
|
||||
let g:ale_php_phpcbf_standard = ''
|
||||
let g:ale_php_phpcbf_options = ''
|
||||
let g:ale_php_phpcbf_use_global = 0
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/fixers')
|
||||
|
@ -54,6 +55,15 @@ Execute(The phpcbf callback should include the phpcbf_standard option):
|
|||
\ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . '--standard=phpcbf_ruleset.xml' . ' -'},
|
||||
\ ale#fixers#phpcbf#Fix(bufnr(''))
|
||||
|
||||
Execute(User provided options should be used):
|
||||
let g:ale_php_phpcbf_options = '--my-user-provided-option my-value'
|
||||
call ale#test#SetFilename('php_paths/project-with-phpcbf/foo/test.php')
|
||||
|
||||
AssertEqual
|
||||
\ {'command': ale#Escape(ale#path#Simplify(g:dir . '/php_paths/project-with-phpcbf/vendor/bin/phpcbf')) . ' --stdin-path=%s ' . ale#Pad('--my-user-provided-option my-value') . ' -'},
|
||||
\ ale#fixers#phpcbf#Fix(bufnr(''))
|
||||
|
||||
|
||||
Before:
|
||||
Save g:ale_php_phpcbf_executable
|
||||
Save g:ale_php_phpcbf_standard
|
||||
|
@ -61,6 +71,7 @@ Before:
|
|||
|
||||
let g:ale_php_phpcbf_executable = 'phpcbf_test'
|
||||
let g:ale_php_phpcbf_standard = ''
|
||||
let g:ale_php_phpcbf_options = ''
|
||||
let g:ale_php_phpcbf_use_global = 0
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/fixers')
|
||||
|
|
50
test/fixers/test_yamlfix_fixer_callback.vader
Normal file
50
test/fixers/test_yamlfix_fixer_callback.vader
Normal file
|
@ -0,0 +1,50 @@
|
|||
Before:
|
||||
Save g:ale_python_yamlfix_executable
|
||||
Save g:ale_python_yamlfix_options
|
||||
|
||||
" Use an invalid global executable, so we don't match it.
|
||||
let g:ale_python_yamlfix_executable = 'xxxinvalid'
|
||||
let g:ale_python_yamlfix_options = ''
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/fixers')
|
||||
silent cd ..
|
||||
silent cd command_callback
|
||||
let g:dir = getcwd()
|
||||
|
||||
let b:bin_dir = has('win32') ? 'Scripts' : 'bin'
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
unlet! b:bin_dir
|
||||
|
||||
call ale#test#RestoreDirectory()
|
||||
|
||||
Execute(The yamlfix callback should return the correct default values):
|
||||
AssertEqual
|
||||
\ 0,
|
||||
\ ale#fixers#yamlfix#Fix(bufnr(''))
|
||||
|
||||
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml')
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#path#BufferCdString(bufnr(''))
|
||||
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix')) . ' -',
|
||||
\ },
|
||||
\ ale#fixers#yamlfix#Fix(bufnr(''))
|
||||
|
||||
Execute(The yamlfix callback should respect custom options):
|
||||
let g:ale_yaml_yamlfix_options = '--multi-line=3 --trailing-comma'
|
||||
|
||||
AssertEqual
|
||||
\ 0,
|
||||
\ ale#fixers#yamlfix#Fix(bufnr(''))
|
||||
|
||||
silent execute 'file ' . fnameescape(g:dir . '/python_paths/with_virtualenv/subdir/foo/bar.yaml')
|
||||
AssertEqual
|
||||
\ {
|
||||
\ 'command': ale#path#BufferCdString(bufnr(''))
|
||||
\ . ale#Escape(ale#path#Simplify(g:dir . '/python_paths/with_virtualenv/env/' . b:bin_dir . '/yamlfix'))
|
||||
\ . ' --multi-line=3 --trailing-comma -',
|
||||
\ },
|
||||
\ ale#fixers#yamlfix#Fix(bufnr(''))
|
37
test/handler/test_erlang_elvis_handler.vader
Normal file
37
test/handler/test_erlang_elvis_handler.vader
Normal file
|
@ -0,0 +1,37 @@
|
|||
Before:
|
||||
runtime ale_linters/erlang/elvis.vim
|
||||
|
||||
After:
|
||||
call ale#linter#Reset()
|
||||
|
||||
Execute(Warning messages should be handled):
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': 11,
|
||||
\ 'text': "Replace the 'if' expression on line 11 with a 'case' expression or function clauses.",
|
||||
\ 'type': 'W',
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': 20,
|
||||
\ 'text': 'Remove the debug call to io:format/1 on line 20.',
|
||||
\ 'type': 'W',
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#erlang#elvis#Handle(bufnr(''), [
|
||||
\ "src/foo.erl:11:no_if_expression:Replace the 'if' expression on line 11 with a 'case' expression or function clauses.",
|
||||
\ 'src/foo.erl:20:no_debug_call:Remove the debug call to io:format/1 on line 20.',
|
||||
\ ])
|
||||
|
||||
Execute(Line length message shouldn't contain the line itself):
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': 24,
|
||||
\ 'text': 'Line 24 is too long.',
|
||||
\ 'type': 'W',
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#erlang#elvis#Handle(bufnr(''), [
|
||||
\ 'src/foo.erl:24:line_length:Line 24 is too long: io:format("Look ma, too long!"),.',
|
||||
\ ])
|
|
@ -13,7 +13,16 @@ Execute(phpcs errors should be handled):
|
|||
\ 'type': 'E',
|
||||
\ 'sub_type': 'style',
|
||||
\ 'text': 'Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)',
|
||||
\ }],
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': 22,
|
||||
\ 'col': 3,
|
||||
\ 'type': 'E',
|
||||
\ 'sub_type': 'style',
|
||||
\ 'text': 'All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks)',
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#php#phpcs#Handle(bufnr(''), [
|
||||
\ '/path/to/some-filename.php:18:3: error - Line indented incorrectly; expected 4 spaces, found 2 (Generic.WhiteSpace.ScopeIndent.IncorrectExact)',
|
||||
\ "/path/to/some-filename.php:22:3: error - All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '\"\n'.",
|
||||
\ ])
|
||||
|
|
34
test/handler/test_tlint_handler.vader
Normal file
34
test/handler/test_tlint_handler.vader
Normal file
|
@ -0,0 +1,34 @@
|
|||
Before:
|
||||
runtime ale_linters/php/tlint.vim
|
||||
|
||||
After:
|
||||
call ale#linter#Reset()
|
||||
|
||||
Execute(The tlint handler should calculate line numbers):
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': '5',
|
||||
\ 'col': 0,
|
||||
\ 'sub_type':
|
||||
\ 'style',
|
||||
\ 'type': 'W',
|
||||
\ 'text': ['! There should be no unused imports.', 'There should be no unused imports.', '', '', '', '', '', '', '', '']
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': '15',
|
||||
\ 'col': 0,
|
||||
\ 'sub_type':
|
||||
\ 'style',
|
||||
\ 'type': 'W',
|
||||
\ 'text': ['! There should be no method visibility in test methods.', 'There should be no method visibility in test methods.', '', '', '', '', '', '', '', '']
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#php#tlint#Handle(347, [
|
||||
\ "Lints for /Users/jose/Code/Tighten/tester/tests/Unit/ExampleTest.php",
|
||||
\ "============",
|
||||
\ "! There should be no unused imports.",
|
||||
\ "5 : `use Illuminate\Foundation\Testing\RefreshDatabase;`",
|
||||
\ "! There should be no method visibility in test methods.",
|
||||
\ "15 : ` public function testBasicTest()`",
|
||||
\ ])
|
|
@ -23,6 +23,7 @@ Before:
|
|||
\ 'completion_trigger_characters': [],
|
||||
\ 'definition': 0,
|
||||
\ 'symbol_search': 0,
|
||||
\ 'code_actions': 0,
|
||||
\ },
|
||||
\}
|
||||
|
||||
|
@ -102,6 +103,7 @@ Execute(Capabilities should bet set up correctly):
|
|||
\ 'definition': 1,
|
||||
\ 'symbol_search': 1,
|
||||
\ 'rename': 1,
|
||||
\ 'code_actions': 1,
|
||||
\ },
|
||||
\ b:conn.capabilities
|
||||
AssertEqual [[1, 'initialized', {}]], g:message_list
|
||||
|
@ -125,7 +127,7 @@ Execute(Disabled capabilities should be recognised correctly):
|
|||
\ 'referencesProvider': v:false,
|
||||
\ 'textDocumentSync': 2,
|
||||
\ 'documentFormattingProvider': v:true,
|
||||
\ 'codeActionProvider': v:true,
|
||||
\ 'codeActionProvider': v:false,
|
||||
\ 'signatureHelpProvider': {
|
||||
\ 'triggerCharacters': ['(', ','],
|
||||
\ },
|
||||
|
@ -146,6 +148,7 @@ Execute(Disabled capabilities should be recognised correctly):
|
|||
\ 'definition': 0,
|
||||
\ 'symbol_search': 0,
|
||||
\ 'rename': 0,
|
||||
\ 'code_actions': 0,
|
||||
\ },
|
||||
\ b:conn.capabilities
|
||||
AssertEqual [[1, 'initialized', {}]], g:message_list
|
||||
|
@ -197,6 +200,7 @@ Execute(Capabilities should be enabled when send as Dictionaries):
|
|||
\ 'typeDefinition': 1,
|
||||
\ 'symbol_search': 1,
|
||||
\ 'rename': 1,
|
||||
\ 'code_actions': 1,
|
||||
\ },
|
||||
\ b:conn.capabilities
|
||||
AssertEqual [[1, 'initialized', {}]], g:message_list
|
||||
|
|
0
test/lua_files/testfile.lua
Normal file
0
test/lua_files/testfile.lua
Normal file
|
@ -3,6 +3,9 @@ Before:
|
|||
|
||||
let g:ale_enabled = 0
|
||||
|
||||
" Enable fix end-of-line as tests below expect that
|
||||
set fixeol
|
||||
|
||||
runtime autoload/ale/code_action.vim
|
||||
runtime autoload/ale/util.vim
|
||||
|
||||
|
@ -211,6 +214,7 @@ Execute(End of file can be modified):
|
|||
\)
|
||||
|
||||
AssertEqual g:test.text + [
|
||||
\ '',
|
||||
\ 'type A: string',
|
||||
\ 'type B: number',
|
||||
\ '',
|
||||
|
|
59
test/test_code_action_python.vader
Normal file
59
test/test_code_action_python.vader
Normal file
|
@ -0,0 +1,59 @@
|
|||
Given python(An example Python file):
|
||||
def main():
|
||||
a = 1
|
||||
c = a + 1
|
||||
|
||||
Execute():
|
||||
let g:changes = [
|
||||
\ {'end': {'offset': 7, 'line': 1}, 'newText': 'func_qtffgsv', 'start': {'offset': 5, 'line': 1}},
|
||||
\ {'end': {'offset': 9, 'line': 1}, 'newText': '', 'start': {'offset': 8, 'line': 1}},
|
||||
\ {'end': {'offset': 15, 'line': 3}, 'newText': " return c\n\n\ndef main():\n c = func_qtffgsvi()\n", 'start': {'offset': 15, 'line': 3}}
|
||||
\]
|
||||
|
||||
call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0)
|
||||
|
||||
Expect(The changes should be applied correctly):
|
||||
def func_qtffgsvi():
|
||||
a = 1
|
||||
c = a + 1
|
||||
return c
|
||||
|
||||
|
||||
def main():
|
||||
c = func_qtffgsvi()
|
||||
|
||||
|
||||
Given python(Second python example):
|
||||
import sys
|
||||
import exifread
|
||||
|
||||
def main():
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
exif = exifread.process_file(f)
|
||||
dt = str(exif['Image DateTime'])
|
||||
date = dt[:10].replace(':', '-')
|
||||
|
||||
Execute():
|
||||
let g:changes = [
|
||||
\ {'end': {'offset': 16, 'line': 2}, 'newText': "\n\ndef func_ivlpdpao(f):\n exif = exifread.process_file(f)\n dt = str(exif['Image DateTime'])\n date = dt[:10].replace(':', '-')\n return date\n", 'start': {'offset': 16, 'line': 2}},
|
||||
\ {'end': {'offset': 32, 'line': 6}, 'newText': 'date = func', 'start': {'offset': 9, 'line': 6}},
|
||||
\ {'end': {'offset': 42, 'line': 8}, 'newText': "ivlpdpao(f)\n", 'start': {'offset': 33, 'line': 6}}
|
||||
\]
|
||||
|
||||
call ale#code_action#ApplyChanges(expand('%:p'), g:changes, 0)
|
||||
|
||||
Expect(The changes should be applied correctly):
|
||||
import sys
|
||||
import exifread
|
||||
|
||||
|
||||
def func_ivlpdpao(f):
|
||||
exif = exifread.process_file(f)
|
||||
dt = str(exif['Image DateTime'])
|
||||
date = dt[:10].replace(':', '-')
|
||||
return date
|
||||
|
||||
|
||||
def main():
|
||||
with open(sys.argv[1], 'rb') as f:
|
||||
date = func_ivlpdpao(f)
|
549
test/test_codefix.vader
Normal file
549
test/test_codefix.vader
Normal file
|
@ -0,0 +1,549 @@
|
|||
Before:
|
||||
call ale#test#SetDirectory('/testplugin/test')
|
||||
call ale#test#SetFilename('dummy.txt')
|
||||
Save g:ale_buffer_info
|
||||
|
||||
let g:ale_buffer_info = {}
|
||||
|
||||
let g:old_filename = expand('%:p')
|
||||
let g:Callback = ''
|
||||
let g:expr_list = []
|
||||
let g:message_list = []
|
||||
let g:handle_code_action_called = 0
|
||||
let g:code_actions = []
|
||||
let g:options = {}
|
||||
let g:capability_checked = ''
|
||||
let g:conn_id = v:null
|
||||
let g:InitCallback = v:null
|
||||
|
||||
runtime autoload/ale/lsp_linter.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/codefix.vim
|
||||
runtime autoload/ale/code_action.vim
|
||||
|
||||
function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort
|
||||
let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {})
|
||||
call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer)
|
||||
|
||||
if a:linter.lsp is# 'tsserver'
|
||||
call ale#lsp#MarkConnectionAsTsserver(g:conn_id)
|
||||
endif
|
||||
|
||||
let l:details = {
|
||||
\ 'command': 'foobar',
|
||||
\ 'buffer': a:buffer,
|
||||
\ 'connection_id': g:conn_id,
|
||||
\ 'project_root': '/foo/bar',
|
||||
\}
|
||||
|
||||
let g:InitCallback = {-> ale#lsp_linter#OnInit(a:linter, l:details, a:Callback)}
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#HasCapability(conn_id, capability) abort
|
||||
let g:capability_checked = a:capability
|
||||
|
||||
return 1
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#RegisterCallback(conn_id, callback) abort
|
||||
let g:Callback = a:callback
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#Send(conn_id, message) abort
|
||||
call add(g:message_list, a:message)
|
||||
|
||||
return 42
|
||||
endfunction
|
||||
|
||||
function! ale#util#Execute(expr) abort
|
||||
call add(g:expr_list, a:expr)
|
||||
endfunction
|
||||
|
||||
function! ale#code_action#HandleCodeAction(code_action, options) abort
|
||||
let g:handle_code_action_called = 1
|
||||
Assert !get(a:options, 'should_save')
|
||||
call add(g:code_actions, a:code_action)
|
||||
endfunction
|
||||
|
||||
function! ale#util#Input(message, value) abort
|
||||
return '2'
|
||||
endfunction
|
||||
|
||||
After:
|
||||
Restore
|
||||
|
||||
if g:conn_id isnot v:null
|
||||
call ale#lsp#RemoveConnectionWithID(g:conn_id)
|
||||
endif
|
||||
|
||||
call ale#test#RestoreDirectory()
|
||||
call ale#linter#Reset()
|
||||
|
||||
unlet! g:capability_checked
|
||||
unlet! g:InitCallback
|
||||
unlet! g:old_filename
|
||||
unlet! g:conn_id
|
||||
unlet! g:Callback
|
||||
unlet! g:message_list
|
||||
unlet! g:expr_list
|
||||
unlet! b:ale_linters
|
||||
unlet! g:options
|
||||
unlet! g:code_actions
|
||||
unlet! g:handle_code_action_called
|
||||
|
||||
runtime autoload/ale/lsp_linter.vim
|
||||
runtime autoload/ale/lsp.vim
|
||||
runtime autoload/ale/util.vim
|
||||
runtime autoload/ale/codefix.vim
|
||||
runtime autoload/ale/code_action.vim
|
||||
|
||||
Execute(Failed codefix responses should be handled correctly):
|
||||
call ale#codefix#HandleTSServerResponse(
|
||||
\ 1,
|
||||
\ {'command': 'getCodeFixes', 'request_seq': 3}
|
||||
\)
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
|
||||
Given typescript(Some typescript file):
|
||||
foo
|
||||
somelongerline ()
|
||||
bazxyzxyzxyz
|
||||
|
||||
Execute(getCodeFixes from tsserver should be handled):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1, {
|
||||
\ 'command': 'getCodeFixes',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:true,
|
||||
\ 'type': 'response',
|
||||
\ 'body': [
|
||||
\ {
|
||||
\ 'description': 'Import default "x" from module "./z"',
|
||||
\ 'fixName': 'import',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': "/foo/bar/file1.ts",
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'end': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ },
|
||||
\ 'newText': 'import x from "./z";^@',
|
||||
\ 'start': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ }
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'description': 'codefix',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': "/foo/bar/file1.ts",
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'end': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1
|
||||
\ },
|
||||
\ 'newText': 'import x from "./z";^@',
|
||||
\ 'start': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1
|
||||
\ }
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(getCodeFixes from tsserver should be handled with user input if there are more than one action):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1, {
|
||||
\ 'command': 'getCodeFixes',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:true,
|
||||
\ 'type': 'response',
|
||||
\ 'body': [
|
||||
\ {
|
||||
\ 'description': 'Import default "x" from module "./z"',
|
||||
\ 'fixName': 'import',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': "/foo/bar/file1.ts",
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'end': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ },
|
||||
\ 'newText': 'import x from "./z";^@',
|
||||
\ 'start': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ }
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\ },
|
||||
\ {
|
||||
\ 'description': 'Import default "x" from module "./y"',
|
||||
\ 'fixName': 'import',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': "/foo/bar/file1.ts",
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'end': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ },
|
||||
\ 'newText': 'import x from "./y";^@',
|
||||
\ 'start': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1,
|
||||
\ }
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'description': 'codefix',
|
||||
\ 'changes': [
|
||||
\ {
|
||||
\ 'fileName': "/foo/bar/file1.ts",
|
||||
\ 'textChanges': [
|
||||
\ {
|
||||
\ 'end': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1
|
||||
\ },
|
||||
\ 'newText': 'import x from "./y";^@',
|
||||
\ 'start': {
|
||||
\ 'line': 2,
|
||||
\ 'offset': 1
|
||||
\ }
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ]
|
||||
\ }
|
||||
\ ],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(Prints a tsserver error message when getCodeFixes unsuccessful):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1, {
|
||||
\ 'command': 'getCodeFixes',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:false,
|
||||
\ 'message': 'something is wrong',
|
||||
\})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
AssertEqual ['echom ''Error while getting code fixes. Reason: something is wrong'''], g:expr_list
|
||||
|
||||
Execute(Does nothing when where are no code fixes):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1, {
|
||||
\ 'command': 'getCodeFixes',
|
||||
\ 'request_seq': 3,
|
||||
\ 'success': v:true,
|
||||
\ 'body': []
|
||||
\})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
AssertEqual ['echom ''No code fixes available.'''], g:expr_list
|
||||
|
||||
Execute(tsserver codefix requests should be sent):
|
||||
call ale#linter#Reset()
|
||||
|
||||
runtime ale_linters/typescript/tsserver.vim
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'code': 2304}]}}
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
||||
" 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@getCodeFixes', {
|
||||
\ 'startLine': 2,
|
||||
\ 'startOffset': 16,
|
||||
\ 'endLine': 2,
|
||||
\ 'endOffset': 17,
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'errorCodes': [2304],
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
|
||||
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': 16}, {'lnum': 2, 'col': 16, 'code': 2304}]}}
|
||||
call setpos('.', [bufnr(''), 2, 16, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
||||
" 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@getCodeFixes', {
|
||||
\ 'startLine': 2,
|
||||
\ 'startOffset': 16,
|
||||
\ 'endLine': 2,
|
||||
\ 'endOffset': 17,
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'errorCodes': [2304],
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
|
||||
Execute(getApplicableRefactors from tsserver should be handled):
|
||||
call ale#codefix#SetMap({3: {
|
||||
\ 'buffer': expand('%:p'),
|
||||
\ 'line': 1,
|
||||
\ 'column': 2,
|
||||
\ 'end_line': 3,
|
||||
\ 'end_column': 4,
|
||||
\ 'connection_id': 0,
|
||||
\}})
|
||||
call ale#codefix#HandleTSServerResponse(1,
|
||||
\ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [{'actions': [{'description': 'Extract to constant in enclosing scope', 'name': 'constant_scope_0'}], 'description': 'Extract constant', 'name': 'Extract Symbol'}, {'actions': [{'description': 'Extract to function in module scope', 'name': 'function_scope_1'}], 'description': 'Extract function', 'name': 'Extract Symbol'}], 'command': 'getApplicableRefactors'})
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'ts@getEditsForRefactor', {
|
||||
\ 'startLine': 1,
|
||||
\ 'startOffset': 2,
|
||||
\ 'endLine': 3,
|
||||
\ 'endOffset': 5,
|
||||
\ 'file': expand('%:p'),
|
||||
\ 'refactor': 'Extract Symbol',
|
||||
\ 'action': 'function_scope_1',
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
|
||||
Execute(getApplicableRefactors should print error on failure):
|
||||
call ale#codefix#SetMap({3: {
|
||||
\ 'buffer': expand('%:p'),
|
||||
\ 'line': 1,
|
||||
\ 'column': 2,
|
||||
\ 'end_line': 3,
|
||||
\ 'end_column': 4,
|
||||
\ 'connection_id': 0,
|
||||
\}})
|
||||
call ale#codefix#HandleTSServerResponse(1,
|
||||
\ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getApplicableRefactors'})
|
||||
|
||||
AssertEqual ['echom ''Error while getting applicable refactors. Reason: oops'''], g:expr_list
|
||||
|
||||
Execute(getApplicableRefactors should do nothing if there are no refactors):
|
||||
call ale#codefix#SetMap({3: {
|
||||
\ 'buffer': expand('%:p'),
|
||||
\ 'line': 1,
|
||||
\ 'column': 2,
|
||||
\ 'end_line': 3,
|
||||
\ 'end_column': 4,
|
||||
\ 'connection_id': 0,
|
||||
\}})
|
||||
call ale#codefix#HandleTSServerResponse(1,
|
||||
\ {'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': [], 'command': 'getApplicableRefactors'})
|
||||
|
||||
AssertEqual ['echom ''No applicable refactors available.'''], g:expr_list
|
||||
|
||||
Execute(getEditsForRefactor from tsserver should be handled):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1,
|
||||
\{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:true, 'body': {'edits': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}], 'renameLocation': {'offset': 3, 'line': 8}, 'renameFilename': '/foo/bar/file.ts'}, 'command': 'getEditsForRefactor' }
|
||||
\)
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'description': 'editsForRefactor',
|
||||
\ 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 35, 'line': 9}, 'newText': 'newFunction(app);', 'start': {'offset': 3, 'line': 8}}, {'end': {'offset': 4, 'line': 19}, 'newText': '^@function newFunction(app: Router) {^@ app.use(booExpressCsrf());^@ app.use(booExpressRequireHttps);^@}^@', 'start': {'offset': 4, 'line': 19}}]}],
|
||||
\ }
|
||||
\ ],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(getEditsForRefactor should print error on failure):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleTSServerResponse(1,
|
||||
\{'seq': 0, 'request_seq': 3, 'type': 'response', 'success': v:false, 'message': 'oops', 'command': 'getEditsForRefactor' }
|
||||
\)
|
||||
|
||||
AssertEqual ['echom ''Error while getting edits for refactor. Reason: oops'''], g:expr_list
|
||||
|
||||
Execute(Failed LSP responses should be handled correctly):
|
||||
call ale#codefix#HandleLSPResponse(
|
||||
\ 1,
|
||||
\ {'method': 'workspace/applyEdit', 'request_seq': 3}
|
||||
\)
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
|
||||
Given python(Some python file):
|
||||
def main():
|
||||
a = 1
|
||||
b = a + 2
|
||||
|
||||
Execute("workspace/applyEdit" from LSP should be handled):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleLSPResponse(1,
|
||||
\ {'id': 0, 'jsonrpc': '2.0', 'method': 'workspace/applyEdit', 'params': {'edit': {'changes': {'file:///foo/bar/file.ts': [{'range': {'end': {'character': 27, 'line': 7}, 'start': {'character': 27, 'line': 7}}, 'newText': ', Config'}, {'range': {'end': {'character': 12, 'line': 96}, 'start': {'character': 2, 'line': 94}}, 'newText': 'await newFunction(redis, imageKey, cover, config);'}, {'range': {'end': {'character': 2, 'line': 99}, 'start': {'character': 2, 'line': 99}}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@'}]}}}})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [{'description': 'applyEdit', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 28, 'line': 8}, 'newText': ', Config', 'start': {'offset': 28, 'line': 8}}, {'end': {'offset': 13, 'line': 97}, 'newText': 'await newFunction(redis, imageKey, cover, config);', 'start': {'offset': 3, 'line': 95}}, {'end': {'offset': 3, 'line': 100}, 'newText': '^@async function newFunction(redis: IRedis, imageKey: string, cover: Buffer, config: Config) {^@ try {^@ await redis.set(imageKey, cover, ''ex'', parseInt(config.coverKeyTTL, 10));^@ }^@ catch { }^@}^@', 'start': {'offset': 3, 'line': 100}}]}]}],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(Code Actions from LSP should be handled with user input if there are more than one action):
|
||||
call ale#codefix#SetMap({2: {}})
|
||||
call ale#codefix#HandleLSPResponse(1,
|
||||
\ {'id': 2, 'jsonrpc': '2.0', 'result': [{'title': 'fake for testing'}, {'arguments': [{'documentChanges': [{'edits': [{'range': {'end': {'character': 31, 'line': 2}, 'start': {'character': 31, 'line': 2}}, 'newText': ', createVideo'}], 'textDocument': {'uri': 'file:///foo/bar/file.ts', 'version': 1}}]}], 'title': 'Add ''createVideo'' to existing import declaration from "./video"', 'command': '_typescript.applyWorkspaceEdit'}]})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/file.ts', 'textChanges': [{'end': {'offset': 32, 'line': 3}, 'newText': ', createVideo', 'start': {'offset': 32, 'line': 3}}]}]}],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(Code Actions from LSP should be handled when returned with documentChanges):
|
||||
call ale#codefix#SetMap({2: {}})
|
||||
call ale#codefix#HandleLSPResponse(1,
|
||||
\ {'id': 2, 'jsonrpc': '2.0', 'result': [{'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 4, 'line': 2}, 'start': {'character': 4, 'line': 1}}, 'newText': ''}, {'range': {'end': {'character': 9, 'line': 2}, 'start': {'character': 8, 'line': 2}}, 'newText': '(1)'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.inline', 'title': 'Inline variable', 'command': v:null}, {'diagnostics': v:null, 'edit': {'changes': v:null, 'documentChanges': [{'edits': [{'range': {'end': {'character': 0, 'line': 0}, 'start': {'character': 0, 'line': 0}}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@'}, {'range': {'end': {'character': 9, 'line': 1}, 'start': {'character': 8, 'line': 1}}, 'newText': 'func_bomdjnxh()^@'}], 'textDocument': {'uri': 'file:///foo/bar/test.py', 'version': v:null}}]}, 'kind': 'refactor.extract', 'title': 'Extract expression into function ''func_bomdjnxh''', 'command': v:null}]})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 1
|
||||
AssertEqual
|
||||
\ [{'description': 'codeaction', 'changes': [{'fileName': '/foo/bar/test.py', 'textChanges': [{'end': {'offset': 1, 'line': 1}, 'newText': 'def func_bomdjnxh():^@ a = 1return a^@^@^@', 'start': {'offset': 1, 'line': 1}}, {'end': {'offset': 10, 'line': 2}, 'newText': 'func_bomdjnxh()^@', 'start': {'offset': 9, 'line': 2}}]}]}],
|
||||
\ g:code_actions
|
||||
|
||||
Execute(LSP Code Actions handles command responses):
|
||||
call ale#codefix#SetMap({3: {
|
||||
\ 'connection_id': 0,
|
||||
\}})
|
||||
call ale#codefix#HandleLSPResponse(1,
|
||||
\ {'id': 3, 'jsonrpc': '2.0', 'result': [{'kind': 'refactor', 'title': 'Extract to inner function in function ''getVideo''', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_0', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to inner function in function ''getVideo''', 'command': '_typescript.applyRefactoring'}}, {'kind': 'refactor', 'title': 'Extract to function in module scope', 'command': {'arguments': [{'file': '/foo/bar/file.ts', 'endOffset': 0, 'action': 'function_scope_1', 'startOffset': 1, 'startLine': 65, 'refactor': 'Extract Symbol', 'endLine': 68}], 'title': 'Extract to function in module scope', 'command': '_typescript.applyRefactoring'}}]})
|
||||
|
||||
AssertEqual
|
||||
\ [[0, 'workspace/executeCommand', {'arguments': [{'file': '/foo/bar/file.ts', 'action': 'function_scope_1', 'endOffset': 0, 'refactor': 'Extract Symbol', 'endLine': 68, 'startLine': 65, 'startOffset': 1}], 'command': '_typescript.applyRefactoring'}]],
|
||||
\ g:message_list
|
||||
|
||||
|
||||
Execute(Prints message when LSP code action returns no results):
|
||||
call ale#codefix#SetMap({3: {}})
|
||||
call ale#codefix#HandleLSPResponse(1,
|
||||
\ {'id': 3, 'jsonrpc': '2.0', 'result': []})
|
||||
|
||||
AssertEqual g:handle_code_action_called, 0
|
||||
AssertEqual ['echom ''No code actions received from server'''], g:expr_list
|
||||
|
||||
Execute(LSP code action requests should be sent):
|
||||
call ale#linter#Reset()
|
||||
|
||||
runtime ale_linters/python/jedils.vim
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}}
|
||||
call setpos('.', [bufnr(''), 2, 5, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
||||
" 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#HandleLSPResponse'')',
|
||||
\ string(g:Callback)
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'textDocument/codeAction', {
|
||||
\ 'context': {
|
||||
\ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
|
||||
\ },
|
||||
\ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}},
|
||||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list[-1:]
|
||||
|
||||
Execute(LSP code action requests should be sent only for error with code):
|
||||
call ale#linter#Reset()
|
||||
|
||||
runtime ale_linters/python/jedils.vim
|
||||
let g:ale_buffer_info = {bufnr(''): {'loclist': [{'lnum': 2, 'col': 5, 'end_lnum': 2, 'end_col': 6, 'code': 2304, 'text': 'oops'}]}}
|
||||
call setpos('.', [bufnr(''), 2, 5, 0])
|
||||
|
||||
" ALECodeAction
|
||||
call ale#codefix#Execute(0)
|
||||
|
||||
" 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#HandleLSPResponse'')',
|
||||
\ string(g:Callback)
|
||||
AssertEqual
|
||||
\ [
|
||||
\ [0, 'textDocument/codeAction', {
|
||||
\ 'context': {
|
||||
\ 'diagnostics': [{'range': {'end': {'character': 6, 'line': 1}, 'start': {'character': 4, 'line': 1}}, 'code': 2304, 'message': 'oops'}]
|
||||
\ },
|
||||
\ 'range': {'end': {'character': 5, 'line': 1}, 'start': {'character': 4, 'line': 1}},
|
||||
\ 'textDocument': {'uri': ale#path#ToURI(expand('%:p'))}
|
||||
\ }]
|
||||
\ ],
|
||||
\ g:message_list[-1:]
|
|
@ -101,7 +101,7 @@ Execute(tsserver quickinfo responses will null missing bodies should be handled)
|
|||
AssertEqual {}, ale#hover#GetMap()
|
||||
|
||||
Execute(tsserver quickinfo displayString values should be displayed):
|
||||
call ale#hover#SetMap({3: {}})
|
||||
call ale#hover#SetMap({3: {'buffer': bufnr('')}})
|
||||
call ale#hover#HandleTSServerResponse(
|
||||
\ 1,
|
||||
\ {
|
||||
|
@ -169,7 +169,7 @@ Execute(LSP hover response with lists of strings and marked strings should be ha
|
|||
AssertEqual {}, ale#hover#GetMap()
|
||||
|
||||
Execute(tsserver responses for documentation requests should be handled):
|
||||
call ale#hover#SetMap({3: {'show_documentation': 1}})
|
||||
call ale#hover#SetMap({3: {'show_documentation': 1, 'buffer': bufnr('')}})
|
||||
|
||||
call ale#hover#HandleTSServerResponse(
|
||||
\ 1,
|
||||
|
|
|
@ -35,7 +35,7 @@ After:
|
|||
Execute(The file changed event function should set b:ale_file_changed):
|
||||
let g:ale_lint_on_enter = 0
|
||||
|
||||
if has('gui')
|
||||
if has('gui_running')
|
||||
new
|
||||
else
|
||||
e test
|
||||
|
|
|
@ -269,7 +269,7 @@ Execute(tsserver rename requests should be sent):
|
|||
\ }]
|
||||
\ ],
|
||||
\ g:message_list
|
||||
AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name', 'force_save': 0}},
|
||||
AssertEqual {'42': {'old_name': 'somelongerline', 'new_name': 'a-new-name'}},
|
||||
\ ale#rename#GetMap()
|
||||
|
||||
Given python(Some Python file):
|
||||
|
@ -470,7 +470,7 @@ Execute(LSP rename requests should be sent):
|
|||
let b:ale_linters = ['pyls']
|
||||
call setpos('.', [bufnr(''), 1, 5, 0])
|
||||
|
||||
ALERename!
|
||||
ALERename
|
||||
|
||||
" We shouldn't register the callback yet.
|
||||
AssertEqual '''''', string(g:Callback)
|
||||
|
@ -500,5 +500,5 @@ Execute(LSP rename requests should be sent):
|
|||
\ ],
|
||||
\ g:message_list
|
||||
|
||||
AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name', 'force_save': 1}},
|
||||
AssertEqual {'42': {'old_name': 'foo', 'new_name': 'a-new-name'}},
|
||||
\ ale#rename#GetMap()
|
||||
|
|
|
@ -127,3 +127,51 @@ Execute(The dash dialect should be used for the shell and the base function):
|
|||
|
||||
Execute(dash should be used for shellcheck):
|
||||
AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a Bash shellcheck shell directive):
|
||||
# shellcheck shell=bash
|
||||
|
||||
Execute(bash dialect should be detected appropriately):
|
||||
AssertEqual 'bash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a sh shellcheck shell directive):
|
||||
#shellcheck shell=sh
|
||||
|
||||
Execute(sh dialect should be detected appropriately):
|
||||
AssertEqual 'sh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a tcsh shellcheck shell directive):
|
||||
# shellcheck shell=tcsh
|
||||
|
||||
Execute(tcsh dialect should be detected appropriately):
|
||||
AssertEqual 'tcsh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a zsh shellcheck shell directive):
|
||||
# shellcheck shell=zsh
|
||||
|
||||
Execute(zsh dialect should be detected appropriately):
|
||||
AssertEqual 'zsh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a csh shellcheck shell directive):
|
||||
# shellcheck shell=csh
|
||||
|
||||
Execute(zsh dialect should be detected appropriately):
|
||||
AssertEqual 'csh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a ksh shellcheck shell directive):
|
||||
# shellcheck shell=ksh
|
||||
|
||||
Execute(ksh dialect should be detected appropriately):
|
||||
AssertEqual 'ksh', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a dash shellcheck shell directive):
|
||||
# shellcheck shell=dash
|
||||
|
||||
Execute(dash dialect should be detected appropriately):
|
||||
AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
||||
Given(A file with a ash shellcheck shell directive):
|
||||
# shellcheck shell=ash
|
||||
|
||||
Execute(dash dialect should be detected for ash that shellcheck does not support):
|
||||
AssertEqual 'dash', ale#handlers#shellcheck#GetDialectArgument(bufnr(''))
|
||||
|
|
Reference in a new issue