Fix #1115 - Add support for wrapping all commands with an option

This commit is contained in:
w0rp 2017-12-20 12:20:38 +00:00
parent 2495744fc3
commit e43e7065da
9 changed files with 148 additions and 30 deletions

View File

@ -512,7 +512,7 @@ function! s:RunJob(options) abort
endif
endif
let l:command = ale#job#PrepareCommand(l:command)
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
let l:job_options = {
\ 'mode': 'nl',
\ 'exit_cb': function('s:HandleExit'),

View File

@ -222,7 +222,7 @@ function! s:RunJob(options) abort
\)
call s:CreateTemporaryFileForJob(l:buffer, l:temporary_file, l:input)
let l:command = ale#job#PrepareCommand(l:command)
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
let l:job_options = {
\ 'mode': 'nl',
\ 'exit_cb': function('s:HandleExit'),

View File

@ -165,23 +165,54 @@ function! ale#job#ValidateArguments(command, options) abort
endif
endfunction
function! ale#job#PrepareCommand(command) abort
function! s:PrepareWrappedCommand(original_wrapper, command) abort
let l:match = matchlist(a:command, '\v^(.*(\&\&|;)) *(.*)$')
let l:prefix = ''
let l:command = a:command
if !empty(l:match)
let l:prefix = l:match[1] . ' '
let l:command = l:match[3]
endif
let l:format = a:original_wrapper
if l:format =~# '%@'
let l:wrapped = substitute(l:format, '%@', ale#Escape(l:command), '')
else
if l:format !~# '%\*'
let l:format .= ' %*'
endif
let l:wrapped = substitute(l:format, '%\*', l:command, '')
endif
return l:prefix . l:wrapped
endfunction
function! ale#job#PrepareCommand(buffer, command) abort
let l:wrapper = ale#Var(a:buffer, 'command_wrapper')
let l:command = !empty(l:wrapper)
\ ? s:PrepareWrappedCommand(l:wrapper, a:command)
\ : a:command
" The command will be executed in a subshell. This fixes a number of
" issues, including reading the PATH variables correctly, %PATHEXT%
" expansion on Windows, etc.
"
" NeoVim handles this issue automatically if the command is a String,
" but we'll do this explicitly, so we use thes same exact command for both
" but we'll do this explicitly, so we use the same exact command for both
" versions.
if ale#Has('win32')
return 'cmd /c ' . a:command
if has('win32')
return 'cmd /c ' . l:command
endif
if &shell =~? 'fish$'
return ['/bin/sh', '-c', a:command]
return ['/bin/sh', '-c', l:command]
endif
return split(&shell) + split(&shellcmdflag) + [a:command]
return split(&shell) + split(&shellcmdflag) + [l:command]
endfunction
" Start a job with options which are agnostic to Vim and NeoVim.

View File

@ -1,3 +1,4 @@
call ale#Set('wrap_command_as_one_argument', 0)
" Author: w0rp <devw0rp@gmail.com>
" Description: Linter registration and lazy-loading
" Retrieves linters as requested by the engine, loading them if needed.
@ -432,6 +433,7 @@ function! ale#linter#StartLSP(buffer, linter, callback) abort
endif
let l:command = ale#job#PrepareCommand(
\ a:buffer,
\ ale#linter#GetCommand(a:buffer, a:linter),
\)
let l:conn_id = ale#lsp#StartProgram(

View File

@ -646,6 +646,39 @@ g:ale_change_sign_column_color *g:ale_change_sign_column_color*
windows.
g:ale_command_wrapper *g:ale_command_wrapper*
*b:ale_command_wrapper*
Type: |String|
Default: `''`
An option for wrapping all commands that ALE runs, for linters, fixers,
and LSP commands. This option can be set globally, or for specific buffers.
This option can be used to apply nice to all commands. For example: >
" Prefix all commands with nice.
let g:ale_command_wrapper = 'nice -n5'
<
Use the |ALEInfo| command to view the commands that are run. All of the
arguments for commands will be put on the end of the wrapped command by
default. A `%*` marker can be used to spread the arguments in the wrapped
command. >
" Has the same effect as the above.
let g:ale_command_wrapper = 'nice -n5 %*'
<
For passing all of the arguments for a command as one argument to a wrapper,
`%@` can be used instead. >
" Will result in say: /bin/bash -c 'other-wrapper -c "some command" -x'
let g:ale_command_wrapper = 'other-wrapper -c %@ -x'
<
For commands including `&&` or `;`, only the last command in the list will
be passed to the wrapper. `&&` is most commonly used in ALE to change the
working directory before running a command.
g:ale_completion_delay *g:ale_completion_delay*
Type: |Number|

View File

@ -209,6 +209,9 @@ call ale#Set('completion_enabled', 0)
call ale#Set('completion_delay', 100)
call ale#Set('completion_max_suggestions', 50)
" A setting for wrapping commands.
call ale#Set('command_wrapper', '')
if g:ale_set_balloons
call ale#balloon#Enable()
endif

View File

@ -581,8 +581,8 @@ Execute(Test fixing with chained callbacks):
" The buffer shouldn't be piped in for earlier commands in the chain.
AssertEqual
\ [
\ string(ale#job#PrepareCommand('echo echoline')),
\ string(ale#job#PrepareCommand('echo echoline')),
\ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')),
\ string(ale#job#PrepareCommand(bufnr(''), 'echo echoline')),
\ ],
\ map(ale#history#Get(bufnr(''))[-2:-1], 'string(v:val.command)')
@ -635,7 +635,7 @@ Execute(A temporary file shouldn't be piped into the command when disabled):
ALEFix
AssertEqual
\ string(ale#job#PrepareCommand('echo new line')),
\ string(ale#job#PrepareCommand(bufnr(''), 'echo new line')),
\ string(ale#history#Get(bufnr(''))[-1].command)
" Remove trailing whitespace for Windows.

View File

@ -4,35 +4,36 @@ Before:
After:
Restore
let g:ale_has_override = {}
Execute(sh should be used when the shell is fish):
" Set something else, so we will replace that too.
let &shellcmdflag = '-f'
let g:ale_has_override = {'win32': 0}
if !has('win32')
" Set something else, so we will replace that too.
let &shellcmdflag = '-f'
let &shell = 'fish'
let &shell = 'fish'
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar')
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar')
let &shell = '/usr/bin/fish'
let &shell = '/usr/bin/fish'
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar')
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar')
let &shell = '/usr/local/bin/fish'
let &shell = '/usr/local/bin/fish'
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand('foobar')
AssertEqual ['/bin/sh', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar')
endif
Execute(Other shells should be used when set):
let &shell = '/bin/bash'
let &shellcmdflag = '-c'
let g:ale_has_override = {'win32': 0}
if !has('win32')
let &shell = '/bin/bash'
let &shellcmdflag = '-c'
AssertEqual ['/bin/bash', '-c', 'foobar'], ale#job#PrepareCommand('foobar')
AssertEqual ['/bin/bash', '-c', 'foobar'], ale#job#PrepareCommand(bufnr(''), 'foobar')
endif
Execute(cmd /c as a string should be used on Windows):
let &shell = 'who cares'
let &shellcmdflag = 'whatever'
let g:ale_has_override = {'win32': 1}
if has('win32')
let &shell = 'who cares'
let &shellcmdflag = 'whatever'
AssertEqual 'cmd /c foobar', ale#job#PrepareCommand('foobar')
AssertEqual 'cmd /c foobar', ale#job#PrepareCommand(bufnr(''), 'foobar')
endif

View File

@ -0,0 +1,48 @@
Before:
Save g:ale_command_wrapper
let g:ale_command_wrapper = ''
function! TestCommand(expected_part, input) abort
let l:expected = has('win32')
\ ? 'cmd /c ' . a:expected_part
\ : split(&shell) + split(&shellcmdflag) + [a:expected_part]
AssertEqual l:expected, ale#job#PrepareCommand(bufnr(''), a:input)
endfunction
After:
Restore
unlet! b:ale_command_wrapper
delfunction TestCommand
Execute(The command wrapper should work with a nice command):
let b:ale_command_wrapper = 'nice -n 5'
call TestCommand('nice -n 5 foo bar', 'foo bar')
Execute(The command wrapper should work with a nice command with an explicit marker):
let b:ale_command_wrapper = 'nice -n 5 %*'
call TestCommand('nice -n 5 foo bar', 'foo bar')
Execute(Wrappers with spread arguments in the middle should be suppported):
let b:ale_command_wrapper = 'wrap %* --'
call TestCommand('wrap foo bar --', 'foo bar')
Execute(Wrappers with the command as one argument should be supported):
let b:ale_command_wrapper = 'wrap -c %@ -x'
call TestCommand('wrap -c ' . ale#Escape('foo bar') . ' -x', 'foo bar')
Execute(&& and ; should be moved to the front):
let b:ale_command_wrapper = 'wrap -c %@ -x'
call TestCommand('foo && bar; wrap -c ' . ale#Escape('baz') . ' -x', 'foo && bar;baz')
let b:ale_command_wrapper = 'nice -n 5'
call TestCommand('foo && bar; nice -n 5 baz -z', 'foo && bar;baz -z')