Add support for Bash and other shells. Add support for reading from stderr, and for generating the executable from functions. Both were needed to support shell linting.
This commit is contained in:
parent
d3047c9cf6
commit
8cc28cdfbd
5 changed files with 133 additions and 14 deletions
|
@ -19,6 +19,8 @@ follow later.
|
|||
|
||||
| Language | Tools |
|
||||
| -------- | ----- |
|
||||
| Bash | -n flag |
|
||||
| Bourne Shell | -n flag |
|
||||
| JavaScript | [eslint](http://eslint.org/) |
|
||||
| Python | [flake8](http://flake8.pycqa.org/en/latest/) |
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ function! ale_linters#javascript#eslint#Handle(buffer, lines)
|
|||
endfunction
|
||||
|
||||
call ALEAddLinter('javascript', {
|
||||
\ 'name': 'eslint',
|
||||
\ 'executable': 'eslint',
|
||||
\ 'command': 'eslint -f unix --stdin',
|
||||
\ 'callback': 'ale_linters#javascript#eslint#Handle',
|
||||
|
|
|
@ -45,6 +45,7 @@ function! ale_linters#python#flake8#Handle(buffer, lines)
|
|||
endfunction
|
||||
|
||||
call ALEAddLinter('python', {
|
||||
\ 'name': 'flake8',
|
||||
\ 'executable': 'flake8',
|
||||
\ 'command': 'flake8 -',
|
||||
\ 'callback': 'ale_linters#python#flake8#Handle',
|
||||
|
|
75
ale_linters/sh/shell.vim
Normal file
75
ale_linters/sh/shell.vim
Normal file
|
@ -0,0 +1,75 @@
|
|||
if exists('g:loaded_ale_linters_sh_shell')
|
||||
finish
|
||||
endif
|
||||
|
||||
let g:loaded_ale_linters_sh_shell = 1
|
||||
|
||||
" This option can be changed to change the default shell when the shell
|
||||
" cannot be taken from the hashbang line.
|
||||
if !exists('g:ale_linters_sh_shell_default_shell')
|
||||
let g:ale_linters_sh_shell_default_shell = 'bash'
|
||||
endif
|
||||
|
||||
function! ale_linters#sh#shell#GetExecutable(buffer)
|
||||
let shell = g:ale_linters_sh_shell_default_shell
|
||||
|
||||
let banglines = getbufline(a:buffer, 1)
|
||||
|
||||
" Take the shell executable from the hashbang, if we can.
|
||||
if len(banglines) == 1
|
||||
let bangmatch = matchlist(banglines[0], '^#!\([^ ]\+\)')
|
||||
|
||||
if len(bangmatch) > 0
|
||||
let shell = bangmatch[1]
|
||||
endif
|
||||
endif
|
||||
|
||||
return shell
|
||||
endfunction
|
||||
|
||||
function! ale_linters#sh#shell#GetCommand(buffer)
|
||||
return ale_linters#sh#shell#GetExecutable(a:buffer) . ' -n'
|
||||
endfunction
|
||||
|
||||
function! ale_linters#sh#shell#Handle(buffer, lines)
|
||||
" Matches patterns line the following:
|
||||
"
|
||||
" bash: line 13: syntax error near unexpected token `d'
|
||||
" sh: 11: Syntax error: "(" unexpected
|
||||
let pattern = '^[^:]\+: \%(line \|\)\(\d\+\): \(.\+\)'
|
||||
let output = []
|
||||
|
||||
for line in a:lines
|
||||
let l:match = matchlist(line, pattern)
|
||||
|
||||
if len(l:match) == 0
|
||||
continue
|
||||
endif
|
||||
|
||||
let line = l:match[1] + 0
|
||||
let column = 1
|
||||
let text = l:match[2]
|
||||
let type = 'E'
|
||||
|
||||
" vcol is Needed to indicate that the column is a character.
|
||||
call add(output, {
|
||||
\ 'bufnr': a:buffer,
|
||||
\ 'lnum': line,
|
||||
\ 'vcol': 0,
|
||||
\ 'col': column,
|
||||
\ 'text': text,
|
||||
\ 'type': type,
|
||||
\ 'nr': -1,
|
||||
\})
|
||||
endfor
|
||||
|
||||
return output
|
||||
endfunction
|
||||
|
||||
call ALEAddLinter('sh', {
|
||||
\ 'name': 'shell',
|
||||
\ 'output_stream': 'stderr',
|
||||
\ 'executable_callback': 'ale_linters#sh#shell#GetExecutable',
|
||||
\ 'command_callback': 'ale_linters#sh#shell#GetCommand',
|
||||
\ 'callback': 'ale_linters#sh#shell#Handle',
|
||||
\})
|
|
@ -141,20 +141,36 @@ function! s:ApplyLinter(buffer, linter)
|
|||
endif
|
||||
|
||||
if has('nvim')
|
||||
let a:linter.job = jobstart(command, {
|
||||
\ 'on_stdout': 's:GatherOutputNeoVim',
|
||||
\ 'on_exit': 's:HandleExitNeoVim',
|
||||
\})
|
||||
if a:linter.output_stream ==# 'stderr'
|
||||
" Read from stderr instead of stdout.
|
||||
let a:linter.job = jobstart(command, {
|
||||
\ 'on_stderr': 's:GatherOutputNeoVim',
|
||||
\ 'on_exit': 's:HandleExitNeoVim',
|
||||
\})
|
||||
else
|
||||
let a:linter.job = jobstart(command, {
|
||||
\ 'on_stdout': 's:GatherOutputNeoVim',
|
||||
\ 'on_exit': 's:HandleExitNeoVim',
|
||||
\})
|
||||
endif
|
||||
else
|
||||
" Vim 8 will read the stdin from the file's buffer.
|
||||
let a:linter.job = job_start(command, {
|
||||
let job_options = {
|
||||
\ 'out_mode': 'nl',
|
||||
\ 'err_mode': 'nl',
|
||||
\ 'out_cb': function('s:GatherOutputVim'),
|
||||
\ 'close_cb': function('s:HandleExitVim'),
|
||||
\ 'in_io': 'buffer',
|
||||
\ 'in_buf': a:buffer,
|
||||
\})
|
||||
\}
|
||||
|
||||
if a:linter.output_stream ==# 'stderr'
|
||||
" Read from stderr instead of stdout.
|
||||
let job_options.err_cb = function('s:GatherOutputVim')
|
||||
else
|
||||
let job_options.out_cb = function('s:GatherOutputVim')
|
||||
endif
|
||||
|
||||
" Vim 8 will read the stdin from the file's buffer.
|
||||
let a:linter.job = job_start(l:command, l:job_options)
|
||||
|
||||
call ch_close_in(job_getchannel(a:linter.job))
|
||||
endif
|
||||
|
@ -182,6 +198,18 @@ function! s:TimerHandler(...)
|
|||
let g:ale_buffer_should_reset_map[buffer] = 1
|
||||
|
||||
for linter in linters
|
||||
" Check if a given linter has a program which can be executed.
|
||||
if has_key(linter, 'executable_callback')
|
||||
let l:executable = s:GetFunction(linter.executable_callback)(buffer)
|
||||
else
|
||||
let l:executable = linter.executable
|
||||
endif
|
||||
|
||||
if !executable(l:executable)
|
||||
" The linter's program cannot be executed, so skip it.
|
||||
continue
|
||||
endif
|
||||
|
||||
call s:ApplyLinter(buffer, linter)
|
||||
endfor
|
||||
endfunction
|
||||
|
@ -197,16 +225,20 @@ function s:BufferCleanup(buffer)
|
|||
endfunction
|
||||
|
||||
function! ALEAddLinter(filetype, linter)
|
||||
" Check if the linter program is executable before adding it.
|
||||
if !executable(a:linter.executable)
|
||||
return
|
||||
endif
|
||||
|
||||
if !has_key(s:linters, a:filetype)
|
||||
let s:linters[a:filetype] = []
|
||||
endif
|
||||
|
||||
let new_linter = {'callback': a:linter.callback}
|
||||
let new_linter = {
|
||||
\ 'name': a:linter.name,
|
||||
\ 'callback': a:linter.callback,
|
||||
\}
|
||||
|
||||
if has_key(a:linter, 'executable_callback')
|
||||
let new_linter.executable_callback = a:linter.executable_callback
|
||||
else
|
||||
let new_linter.executable = a:linter.executable
|
||||
endif
|
||||
|
||||
if has_key(a:linter, 'command_callback')
|
||||
let new_linter.command_callback = a:linter.command_callback
|
||||
|
@ -214,6 +246,14 @@ function! ALEAddLinter(filetype, linter)
|
|||
let new_linter.command = a:linter.command
|
||||
endif
|
||||
|
||||
if has_key(a:linter, 'output_stream')
|
||||
let new_linter.output_stream = a:linter.output_stream
|
||||
else
|
||||
let new_linter.output_stream = 'stdout'
|
||||
endif
|
||||
|
||||
" TODO: Assert the value of the output_stream to be something sensible.
|
||||
|
||||
call add(s:linters[a:filetype], new_linter)
|
||||
endfunction
|
||||
|
||||
|
|
Reference in a new issue