Support formatting escaped executable names into commands with %e

This commit is contained in:
w0rp 2018-07-05 20:56:31 +01:00
parent f002c2c9f0
commit da692b2e2d
No known key found for this signature in database
GPG Key ID: 0FC1ECAA8C81CD83
6 changed files with 68 additions and 19 deletions

View File

@ -20,7 +20,7 @@ endfunction
" %s -> with the current filename " %s -> with the current filename
" %t -> with the name of an unused file in a temporary directory " %t -> with the name of an unused file in a temporary directory
" %% -> with a literal % " %% -> with a literal %
function! ale#command#FormatCommand(buffer, command, pipe_file_if_needed) abort function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_needed) abort
let l:temporary_file = '' let l:temporary_file = ''
let l:command = a:command let l:command = a:command
@ -28,6 +28,11 @@ function! ale#command#FormatCommand(buffer, command, pipe_file_if_needed) abort
" with an ugly string. " with an ugly string.
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g') let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
" Replace %e with the escaped executable, if available.
if !empty(a:executable) && l:command =~# '%e'
let l:command = substitute(l:command, '%e', '\=ale#Escape(a:executable)', 'g')
endif
" Replace all %s occurrences in the string with the name of the current " Replace all %s occurrences in the string with the name of the current
" file. " file.
if l:command =~# '%s' if l:command =~# '%s'

View File

@ -189,6 +189,7 @@ function! s:HandleExit(job_id, exit_code) abort
let l:linter = l:job_info.linter let l:linter = l:job_info.linter
let l:output = l:job_info.output let l:output = l:job_info.output
let l:buffer = l:job_info.buffer let l:buffer = l:job_info.buffer
let l:executable = l:job_info.executable
let l:next_chain_index = l:job_info.next_chain_index let l:next_chain_index = l:job_info.next_chain_index
if g:ale_history_enabled if g:ale_history_enabled
@ -212,7 +213,7 @@ function! s:HandleExit(job_id, exit_code) abort
endif endif
if l:next_chain_index < len(get(l:linter, 'command_chain', [])) if l:next_chain_index < len(get(l:linter, 'command_chain', []))
call s:InvokeChain(l:buffer, l:linter, l:next_chain_index, l:output) call s:InvokeChain(l:buffer, l:executable, l:linter, l:next_chain_index, l:output)
return return
endif endif
@ -440,6 +441,12 @@ endfunction
" Returns 1 when the job was started successfully. " Returns 1 when the job was started successfully.
function! s:RunJob(options) abort function! s:RunJob(options) abort
let l:command = a:options.command let l:command = a:options.command
if empty(l:command)
return 0
endif
let l:executable = a:options.executable
let l:buffer = a:options.buffer let l:buffer = a:options.buffer
let l:linter = a:options.linter let l:linter = a:options.linter
let l:output_stream = a:options.output_stream let l:output_stream = a:options.output_stream
@ -447,11 +454,12 @@ function! s:RunJob(options) abort
let l:read_buffer = a:options.read_buffer let l:read_buffer = a:options.read_buffer
let l:info = g:ale_buffer_info[l:buffer] let l:info = g:ale_buffer_info[l:buffer]
if empty(l:command) let [l:temporary_file, l:command] = ale#command#FormatCommand(
return 0 \ l:buffer,
endif \ l:executable,
\ l:command,
let [l:temporary_file, l:command] = ale#command#FormatCommand(l:buffer, l:command, l:read_buffer) \ l:read_buffer,
\)
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file) if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
" If a temporary filename has been formatted in to the command, then " If a temporary filename has been formatted in to the command, then
@ -512,6 +520,7 @@ function! s:RunJob(options) abort
let s:job_info_map[l:job_id] = { let s:job_info_map[l:job_id] = {
\ 'linter': l:linter, \ 'linter': l:linter,
\ 'buffer': l:buffer, \ 'buffer': l:buffer,
\ 'executable': l:executable,
\ 'output': [], \ 'output': [],
\ 'next_chain_index': l:next_chain_index, \ 'next_chain_index': l:next_chain_index,
\} \}
@ -604,8 +613,9 @@ function! ale#engine#ProcessChain(buffer, linter, chain_index, input) abort
\} \}
endfunction endfunction
function! s:InvokeChain(buffer, linter, chain_index, input) abort function! s:InvokeChain(buffer, executable, linter, chain_index, input) abort
let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input) let l:options = ale#engine#ProcessChain(a:buffer, a:linter, a:chain_index, a:input)
let l:options.executable = a:executable
return s:RunJob(l:options) return s:RunJob(l:options)
endfunction endfunction
@ -699,7 +709,7 @@ function! s:RunLinter(buffer, linter) abort
let l:executable = ale#linter#GetExecutable(a:buffer, a:linter) let l:executable = ale#linter#GetExecutable(a:buffer, a:linter)
if ale#engine#IsExecutable(a:buffer, l:executable) if ale#engine#IsExecutable(a:buffer, l:executable)
return s:InvokeChain(a:buffer, a:linter, 0, []) return s:InvokeChain(a:buffer, l:executable, a:linter, 0, [])
endif endif
endif endif

View File

@ -212,6 +212,7 @@ function! s:RunJob(options) abort
let [l:temporary_file, l:command] = ale#command#FormatCommand( let [l:temporary_file, l:command] = ale#command#FormatCommand(
\ l:buffer, \ l:buffer,
\ '',
\ l:command, \ l:command,
\ l:read_buffer, \ l:read_buffer,
\) \)

View File

@ -151,6 +151,8 @@ function! ale#lsp_linter#StartLSP(buffer, linter, callback) abort
return {} return {}
endif endif
" Format the command, so %e can be formatted into it.
let l:command = ale#command#FormatCommand(a:buffer, l:executable, l:command, 0)[1]
let l:command = ale#job#PrepareCommand( let l:command = ale#job#PrepareCommand(
\ a:buffer, \ a:buffer,
\ ale#linter#GetCommand(a:buffer, a:linter), \ ale#linter#GetCommand(a:buffer, a:linter),

View File

@ -2494,15 +2494,23 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
For example: > For example: >
'command': 'ghc -fno-code -v0 %t', 'command': 'ghc -fno-code -v0 %t',
<
Any substring `%e` will be replaced with the escaped executable supplied
with `executable` or `executable_callback`. This provides a convenient way
to define a command string which needs to include a dynamic executable name,
but which is otherwise static.
For example: >
'command': '%e --some-argument',
< <
The character sequence `%%` can be used to emit a literal `%` into a The character sequence `%%` can be used to emit a literal `%` into a
command, so literal character sequences `%s` and `%t` can be escaped by command, so literal character sequences `%s` and `%t` can be escaped by
using `%%s` and `%%t` instead, etc. using `%%s` and `%%t` instead, etc.
If a callback for a command generates part of a command string which might If a callback for a command generates part of a command string which might
possibly contain `%%`, `%s`, or `%t` where the special formatting behaviour possibly contain `%%`, `%s`, `%t`, or `%e`, where the special formatting
is not desired, the |ale#engine#EscapeCommandPart()| function can be used to behavior is not desired, the |ale#engine#EscapeCommandPart()| function can
replace those characters to avoid formatting issues. be used to replace those characters to avoid formatting issues.
*ale-linter-loading-behavior* *ale-linter-loading-behavior*
*ale-linter-loading-behaviour* *ale-linter-loading-behaviour*

View File

@ -15,10 +15,10 @@ After:
delfunction CheckTempFile delfunction CheckTempFile
Execute(FormatCommand should do nothing to basic command strings): Execute(FormatCommand should do nothing to basic command strings):
AssertEqual ['', 'awesome-linter do something'], ale#command#FormatCommand(bufnr('%'), 'awesome-linter do something', 0) AssertEqual ['', 'awesome-linter do something'], ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0)
Execute(FormatCommand should handle %%, and ignore other percents): Execute(FormatCommand should handle %%, and ignore other percents):
AssertEqual ['', '% %%d %%f %x %'], ale#command#FormatCommand(bufnr('%'), '%% %%%d %%%f %x %', 0) AssertEqual ['', '% %%d %%f %x %'], ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0)
Execute(FormatCommand should convert %s to the current filename): Execute(FormatCommand should convert %s to the current filename):
AssertEqual AssertEqual
@ -26,10 +26,10 @@ Execute(FormatCommand should convert %s to the current filename):
\ '', \ '',
\ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p')) \ 'foo ' . ale#Escape(expand('%:p')) . ' bar ' . ale#Escape(expand('%:p'))
\ ], \ ],
\ ale#command#FormatCommand(bufnr('%'), 'foo %s bar %s', 0) \ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0)
Execute(FormatCommand should convert %t to a new temporary filename): Execute(FormatCommand should convert %t to a new temporary filename):
let g:result = ale#command#FormatCommand(bufnr('%'), 'foo %t bar %t', 0) let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0)
call CheckTempFile(g:result[0]) call CheckTempFile(g:result[0])
@ -43,7 +43,7 @@ Execute(FormatCommand should convert %t to a new temporary filename):
AssertEqual g:match[1], g:match[2] AssertEqual g:match[1], g:match[2]
Execute(FormatCommand should let you combine %s and %t): Execute(FormatCommand should let you combine %s and %t):
let g:result = ale#command#FormatCommand(bufnr('%'), 'foo %t bar %s', 0) let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0)
call CheckTempFile(g:result[0]) call CheckTempFile(g:result[0])
@ -56,11 +56,34 @@ Execute(FormatCommand should let you combine %s and %t):
" The second item should be equal to the original filename. " The second item should be equal to the original filename.
AssertEqual ale#Escape(expand('%:p')), g:match[2] AssertEqual ale#Escape(expand('%:p')), g:match[2]
Execute(FormatCommand should replace %e with the escaped executable):
if has('win32')
AssertEqual
\ ['', 'foo foo'],
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0)
AssertEqual
\ ['', '"foo bar"'],
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0)
AssertEqual
\ ['', '%e %e'],
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0)
else
AssertEqual
\ ['', '''foo'' ''foo'''],
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0)
AssertEqual
\ ['', '''foo bar'''],
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0)
AssertEqual
\ ['', '%e %e'],
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0)
endif
Execute(EscapeCommandPart should escape all percent signs): Execute(EscapeCommandPart should escape all percent signs):
AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%') AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%')
Execute(EscapeCommandPart should pipe in temporary files appropriately): Execute(EscapeCommandPart should pipe in temporary files appropriately):
let g:result = ale#command#FormatCommand(bufnr('%'), 'foo bar', 1) let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1)
call CheckTempFile(g:result[0]) call CheckTempFile(g:result[0])
@ -68,7 +91,7 @@ Execute(EscapeCommandPart should pipe in temporary files appropriately):
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1] Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
AssertEqual ale#Escape(g:result[0]), g:match[1] AssertEqual ale#Escape(g:result[0]), g:match[1]
let g:result = ale#command#FormatCommand(bufnr('%'), 'foo bar %t', 1) let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1)
call CheckTempFile(g:result[0]) call CheckTempFile(g:result[0])