summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2017-08-28 19:16:23 +0100
committerw0rp <devw0rp@gmail.com>2017-08-28 19:16:23 +0100
commitb031531e79d409af3b143a1f40431cd868cd78fb (patch)
tree43109aa362992890041ec486fdc2f64bdb14325d
parent73ec83d055a62019c41e699981e841e395219cca (diff)
downloadale-b031531e79d409af3b143a1f40431cd868cd78fb.zip
#869 - Detect the shell dialect from the hashbang for shellcheck
-rw-r--r--ale_linters/sh/shell.vim14
-rw-r--r--ale_linters/sh/shellcheck.vim30
-rw-r--r--autoload/ale/handlers/sh.vim20
-rw-r--r--test/command_callback/test_shellcheck_command_callback.vader47
-rw-r--r--test/test_shell_detection.vader83
5 files changed, 173 insertions, 21 deletions
diff --git a/ale_linters/sh/shell.vim b/ale_linters/sh/shell.vim
index 8f5ca3ae..cf5e4e6c 100644
--- a/ale_linters/sh/shell.vim
+++ b/ale_linters/sh/shell.vim
@@ -17,18 +17,10 @@ if !exists('g:ale_sh_shell_default_shell')
endif
function! ale_linters#sh#shell#GetExecutable(buffer) abort
- let l:banglines = getbufline(a:buffer, 1)
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
- " Take the shell executable from the hashbang, if we can.
- if len(l:banglines) == 1 && l:banglines[0] =~# '^#!'
- " Remove options like -e, etc.
- let l:line = substitute(l:banglines[0], '--\?[a-zA-Z0-9]\+', '', 'g')
-
- for l:possible_shell in ['bash', 'tcsh', 'csh', 'zsh', 'sh']
- if l:line =~# l:possible_shell . '\s*$'
- return l:possible_shell
- endif
- endfor
+ if !empty(l:shell_type)
+ return l:shell_type
endif
return ale#Var(a:buffer, 'sh_shell_default_shell')
diff --git a/ale_linters/sh/shellcheck.vim b/ale_linters/sh/shellcheck.vim
index 5353683d..3a2d33f2 100644
--- a/ale_linters/sh/shellcheck.vim
+++ b/ale_linters/sh/shellcheck.vim
@@ -19,25 +19,35 @@ function! ale_linters#sh#shellcheck#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'sh_shellcheck_executable')
endfunction
-function! s:GetDialectArgument() abort
- if exists('b:is_bash') && b:is_bash
- return '-s bash'
- elseif exists('b:is_sh') && b:is_sh
- return '-s sh'
- elseif exists('b:is_kornshell') && b:is_kornshell
- return '-s ksh'
+function! ale_linters#sh#shellcheck#GetDialectArgument(buffer) abort
+ let l:shell_type = ale#handlers#sh#GetShellType(a:buffer)
+
+ if !empty(l:shell_type)
+ return l:shell_type
+ endif
+
+ " If there's no hashbang, try using Vim's buffer variables.
+ if get(b:, 'is_bash')
+ return 'bash'
+ elseif get(b:, 'is_sh')
+ return 'sh'
+ elseif get(b:, 'is_kornshell')
+ return 'ksh'
endif
return ''
endfunction
function! ale_linters#sh#shellcheck#GetCommand(buffer) abort
+ let l:options = ale#Var(a:buffer, 'sh_shellcheck_options')
let l:exclude_option = ale#Var(a:buffer, 'sh_shellcheck_exclusions')
+ let l:dialect = ale_linters#sh#shellcheck#GetDialectArgument(a:buffer)
return ale_linters#sh#shellcheck#GetExecutable(a:buffer)
- \ . ' ' . ale#Var(a:buffer, 'sh_shellcheck_options')
- \ . ' ' . (!empty(l:exclude_option) ? '-e ' . l:exclude_option : '')
- \ . ' ' . s:GetDialectArgument() . ' -f gcc -'
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '')
+ \ . (!empty(l:dialect) ? ' -s ' . l:dialect : '')
+ \ . ' -f gcc -'
endfunction
call ale#linter#Define('sh', {
diff --git a/autoload/ale/handlers/sh.vim b/autoload/ale/handlers/sh.vim
new file mode 100644
index 00000000..894879ee
--- /dev/null
+++ b/autoload/ale/handlers/sh.vim
@@ -0,0 +1,20 @@
+" Author: w0rp <devw0rp@gmail.com>
+
+" Get the shell type for a buffer, based on the hashbang line.
+function! ale#handlers#sh#GetShellType(buffer) abort
+ let l:bang_line = get(getbufline(a:buffer, 1), 0, '')
+
+ " Take the shell executable from the hashbang, if we can.
+ if l:bang_line[:1] is# '#!'
+ " Remove options like -e, etc.
+ let l:command = substitute(l:bang_line, ' --\?[a-zA-Z0-9]\+', '', 'g')
+
+ for l:possible_shell in ['bash', 'tcsh', 'csh', 'zsh', 'sh']
+ if l:command =~# l:possible_shell . '\s*$'
+ return l:possible_shell
+ endif
+ endfor
+ endif
+
+ return ''
+endfunction
diff --git a/test/command_callback/test_shellcheck_command_callback.vader b/test/command_callback/test_shellcheck_command_callback.vader
new file mode 100644
index 00000000..0d8fef66
--- /dev/null
+++ b/test/command_callback/test_shellcheck_command_callback.vader
@@ -0,0 +1,47 @@
+Before:
+ Save g:ale_sh_shellcheck_exclusions
+ Save g:ale_sh_shellcheck_executable
+ Save g:ale_sh_shellcheck_options
+
+ unlet! g:ale_sh_shellcheck_exclusions
+ unlet! g:ale_sh_shellcheck_executable
+ unlet! g:ale_sh_shellcheck_options
+
+ runtime ale_linters/sh/shellcheck.vim
+
+After:
+ Restore
+
+ unlet! b:ale_sh_shellcheck_exclusions
+ unlet! b:ale_sh_shellcheck_executable
+ unlet! b:ale_sh_shellcheck_options
+ unlet! b:is_bash
+
+ call ale#linter#Reset()
+
+Execute(The default shellcheck command should be correct):
+ AssertEqual
+ \ 'shellcheck -f gcc -',
+ \ ale_linters#sh#shellcheck#GetCommand(bufnr(''))
+
+Execute(The shellcheck command should accept options):
+ let b:ale_sh_shellcheck_options = '--foobar'
+
+ AssertEqual
+ \ 'shellcheck --foobar -f gcc -',
+ \ ale_linters#sh#shellcheck#GetCommand(bufnr(''))
+
+Execute(The shellcheck command should accept options and exclusions):
+ let b:ale_sh_shellcheck_options = '--foobar'
+ let b:ale_sh_shellcheck_exclusions = 'foo,bar'
+
+ AssertEqual
+ \ 'shellcheck --foobar -e foo,bar -f gcc -',
+ \ ale_linters#sh#shellcheck#GetCommand(bufnr(''))
+
+Execute(The shellcheck command should include the dialect):
+ let b:is_bash = 1
+
+ AssertEqual
+ \ 'shellcheck -s bash -f gcc -',
+ \ ale_linters#sh#shellcheck#GetCommand(bufnr(''))
diff --git a/test/test_shell_detection.vader b/test/test_shell_detection.vader
new file mode 100644
index 00000000..37cf43ce
--- /dev/null
+++ b/test/test_shell_detection.vader
@@ -0,0 +1,83 @@
+Before:
+ runtime ale_linters/sh/shell.vim
+ runtime ale_linters/sh/shellcheck.vim
+
+After:
+ call ale#linter#Reset()
+
+ unlet! b:is_bash
+ unlet! b:is_sh
+ unlet! b:is_kornshell
+
+Given(A file with a Bash hashbang):
+ #!/bin/bash
+
+Execute(/bin/bash should be detected appropriately):
+ AssertEqual 'bash', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with /bin/sh):
+ #!/usr/bin/env sh -eu --foobar
+
+Execute(/bin/sh should be detected appropriately):
+ AssertEqual 'sh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with bash as an argument to env):
+ #!/usr/bin/env bash
+
+Execute(/usr/bin/env bash should be detected appropriately):
+ AssertEqual 'bash', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a tcsh hash bang and arguments):
+ #!/usr/bin/env tcsh -eu --foobar
+
+Execute(tcsh should be detected appropriately):
+ AssertEqual 'tcsh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'tcsh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'tcsh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a zsh hash bang and arguments):
+ #!/usr/bin/env zsh -eu --foobar
+
+Execute(zsh should be detected appropriately):
+ AssertEqual 'zsh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'zsh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'zsh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a csh hash bang and arguments):
+ #!/usr/bin/env csh -eu --foobar
+
+Execute(zsh should be detected appropriately):
+ AssertEqual 'csh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'csh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'csh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file with a sh hash bang and arguments):
+ #!/usr/bin/env sh -eu --foobar
+
+Execute(sh should be detected appropriately):
+ AssertEqual 'sh', ale#handlers#sh#GetShellType(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shell#GetExecutable(bufnr(''))
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Given(A file without a hashbang):
+
+Execute(The bash dialect should be used for shellcheck if b:is_bash is 1):
+ let b:is_bash = 1
+
+ AssertEqual 'bash', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Execute(The sh dialect should be used for shellcheck if b:is_sh is 1):
+ let b:is_sh = 1
+
+ AssertEqual 'sh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))
+
+Execute(The ksh dialect should be used for shellcheck if b:is_kornshell is 1):
+ let b:is_kornshell = 1
+
+ AssertEqual 'ksh', ale_linters#sh#shellcheck#GetDialectArgument(bufnr(''))