mirror of https://github.com/dense-analysis/ale
Add support for temporary filename substitution, for replacing stdin_wrapper
This commit is contained in:
parent
88192e8662
commit
03ab963d1a
|
@ -278,6 +278,66 @@ function! s:FixLocList(buffer, loclist) abort
|
||||||
endfor
|
endfor
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Given part of a command, replace any % with %%, so that no characters in
|
||||||
|
" the string will be replaced with filenames, etc.
|
||||||
|
function! ale#engine#EscapeCommandPart(command_part) abort
|
||||||
|
return substitute(a:command_part, '%', '%%', 'g')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Given a command string, replace every...
|
||||||
|
" %s -> with the current filename
|
||||||
|
" %t -> with the name of an unused file in a temporary directory
|
||||||
|
" %% -> with a literal %
|
||||||
|
function! ale#engine#FormatCommand(buffer, command) abort
|
||||||
|
let l:temporary_file = ''
|
||||||
|
let l:command = a:command
|
||||||
|
|
||||||
|
" First replace all uses of %%, used for literal percent characters,
|
||||||
|
" with an ugly string.
|
||||||
|
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
|
||||||
|
|
||||||
|
" Replace all %s occurences in the string with the name of the current
|
||||||
|
" file.
|
||||||
|
if l:command =~# '%s'
|
||||||
|
let l:filename = fnamemodify(bufname(a:buffer), ':p')
|
||||||
|
let l:command = substitute(l:command, '%s', fnameescape(l:filename), 'g')
|
||||||
|
endif
|
||||||
|
|
||||||
|
if l:command =~# '%t'
|
||||||
|
" Create a temporary filename, <temp_dir>/<original_basename>
|
||||||
|
" The file itself will not be created by this function.
|
||||||
|
let l:temporary_file =
|
||||||
|
\ tempname()
|
||||||
|
\ . (has('win32') ? '\' : '/')
|
||||||
|
\ . fnamemodify(bufname(a:buffer), ':t')
|
||||||
|
|
||||||
|
let l:command = substitute(l:command, '%t', fnameescape(l:temporary_file), 'g')
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Finish formatting so %% becomes %.
|
||||||
|
let l:command = substitute(l:command, '<<PERCENTS>>', '%', 'g')
|
||||||
|
|
||||||
|
return [l:temporary_file, l:command]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:CreateTemporaryFileForJob(buffer, temporary_file) abort
|
||||||
|
if empty(a:temporary_file)
|
||||||
|
" There is no file, so we didn't create anything.
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:temporary_directory = fnamemodify(a:temporary_file, ':h')
|
||||||
|
" Create the temporary directory for the file, unreadable by 'other'
|
||||||
|
" users.
|
||||||
|
call mkdir(l:temporary_directory, '', 0750)
|
||||||
|
" Automatically delete the directory later.
|
||||||
|
call ale#engine#ManageDirectory(a:buffer, l:temporary_directory)
|
||||||
|
" Write the buffer out to a file.
|
||||||
|
silent! exec 'write' a:temporary_file
|
||||||
|
|
||||||
|
return 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! s:RunJob(options) abort
|
function! s:RunJob(options) abort
|
||||||
let l:command = a:options.command
|
let l:command = a:options.command
|
||||||
let l:buffer = a:options.buffer
|
let l:buffer = a:options.buffer
|
||||||
|
@ -286,10 +346,12 @@ function! s:RunJob(options) abort
|
||||||
let l:next_chain_index = a:options.next_chain_index
|
let l:next_chain_index = a:options.next_chain_index
|
||||||
let l:read_buffer = a:options.read_buffer
|
let l:read_buffer = a:options.read_buffer
|
||||||
|
|
||||||
if l:command =~# '%s'
|
let [l:temporary_file, l:command] = ale#engine#FormatCommand(l:buffer, l:command)
|
||||||
" If there is a '%s' in the command string, replace it with the name
|
|
||||||
" of the file.
|
if s:CreateTemporaryFileForJob(l:buffer, l:temporary_file)
|
||||||
let l:command = printf(l:command, shellescape(fnamemodify(bufname(l:buffer), ':p')))
|
" If a temporary filename has been formatted in to the command, then
|
||||||
|
" we do not need to send the Vim buffer to the command.
|
||||||
|
let l:read_buffer = 0
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if has('nvim')
|
if has('nvim')
|
||||||
|
|
49
doc/ale.txt
49
doc/ale.txt
|
@ -1030,6 +1030,15 @@ ale#Queue(delay) *ale#Queue()*
|
||||||
again from the same buffer
|
again from the same buffer
|
||||||
|
|
||||||
|
|
||||||
|
ale#engine#EscapeCommandPart(command_part) *ale#engine#EscapeCommandPart()*
|
||||||
|
|
||||||
|
Given a |String|, return a |String| with all `%` characters replaced with
|
||||||
|
`%%` instead. This function can be used to escape strings which are
|
||||||
|
dynamically generated for commands before handing them over to ALE,
|
||||||
|
so that ALE doesn't treat any strings with `%` formatting sequences
|
||||||
|
specially.
|
||||||
|
|
||||||
|
|
||||||
ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*
|
ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()*
|
||||||
|
|
||||||
Given a buffer number, this function will rerurn the list of warnings and
|
Given a buffer number, this function will rerurn the list of warnings and
|
||||||
|
@ -1186,16 +1195,35 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()*
|
||||||
See |ale#engine#ManageFile()| and |ale#engine#ManageDirectory| for more
|
See |ale#engine#ManageFile()| and |ale#engine#ManageDirectory| for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
Some programs for checking for errors are not capable of receiving input
|
All command strings will be formatted for special character sequences.
|
||||||
from stdin, as is required by ALE. To remedy this, a wrapper script is
|
Any substring `%s` will be replaced with the full path to the current file
|
||||||
provided named in the variable |g:ale#util#stdin_wrapper|. This variable
|
being edited. This format option can be used to pass the exact filename
|
||||||
can be called with the regular arguments for any command to forward data
|
being edited to a program.
|
||||||
from stdin to the program, by way of creating a temporary file. The first
|
|
||||||
argument to the stdin wrapper must be a file extension to save the temporary
|
|
||||||
file with, and the following arguments are the command as normal.
|
|
||||||
For example: >
|
For example: >
|
||||||
'command': g:ale#util#stdin_wrapper . ' .hs ghc -fno-code -v0',
|
'command': 'eslint -f unix --stdin --stdin-filename %s'
|
||||||
<
|
<
|
||||||
|
Any substring `%t` will be replaced with a path to a temporary file. Merely
|
||||||
|
adding `%t` will cause ALE to create a temporary file containing the
|
||||||
|
contents of the the buffer being checked. All occurrences of `%t` in command
|
||||||
|
strings will reference the one temporary file. The temporary file will be
|
||||||
|
created inside a temporary directory, and the entire temporary directory
|
||||||
|
will be automatically deleted, following the behaviour of
|
||||||
|
|ale#engine#ManageDirectory|. This option can be used for some linters which
|
||||||
|
do not support reading from stdin.
|
||||||
|
|
||||||
|
For example: >
|
||||||
|
'command': 'ghc -fno-code -v0 %t',
|
||||||
|
<
|
||||||
|
The character sequence `%%` can be used to emit a literal `%` into a
|
||||||
|
command, so literal character sequences `%s` and `%t` can be escaped by
|
||||||
|
using `%%s` and `%%t` instead, etc.
|
||||||
|
|
||||||
|
If a callback for a command generates part of a command string which might
|
||||||
|
possibly contain `%%`, `%s`, or `%t` where the special formatting behaviour
|
||||||
|
is not desired, the |ale#engine#EscapeCommandPart()| function can be used to
|
||||||
|
replace those characters to avoid formatting issues.
|
||||||
|
|
||||||
|
|
||||||
ale#linter#Get(filetype) *ale#linter#Get()*
|
ale#linter#Get(filetype) *ale#linter#Get()*
|
||||||
Return all of linters configured for a given filetype as a |List| of
|
Return all of linters configured for a given filetype as a |List| of
|
||||||
|
@ -1217,11 +1245,6 @@ ale#statusline#Status() *ale#statusline#Status()*
|
||||||
%{ale#statusline#Status()}
|
%{ale#statusline#Status()}
|
||||||
|
|
||||||
|
|
||||||
g:ale#util#stdin_wrapper *g:ale#util#stdin_wrapper*
|
|
||||||
This variable names a wrapper script for sending stdin input to programs
|
|
||||||
which cannot accept input via stdin. See |ale#linter#Define()| for more.
|
|
||||||
|
|
||||||
|
|
||||||
ALELint *ALELint*
|
ALELint *ALELint*
|
||||||
This |User| autocommand is triggered by ALE every time it completes a lint
|
This |User| autocommand is triggered by ALE every time it completes a lint
|
||||||
operation. It can be used to update statuslines, send notifications, or
|
operation. It can be used to update statuslines, send notifications, or
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
Before:
|
||||||
|
silent! cd /testplugin/test
|
||||||
|
:e! top/middle/bottom/dummy.txt
|
||||||
|
|
||||||
|
After:
|
||||||
|
unlet! g:result
|
||||||
|
unlet! g:match
|
||||||
|
|
||||||
|
Execute(FormatCommand should do nothing to basic command strings):
|
||||||
|
AssertEqual ['', 'awesome-linter do something'], ale#engine#FormatCommand(bufnr('%'), 'awesome-linter do something')
|
||||||
|
|
||||||
|
Execute(FormatCommand should handle %%, and ignore other percents):
|
||||||
|
AssertEqual ['', '% %%d %%f %x %'], ale#engine#FormatCommand(bufnr('%'), '%% %%%d %%%f %x %')
|
||||||
|
|
||||||
|
Execute(FormatCommand should convert %s to the current filename):
|
||||||
|
AssertEqual ['', 'foo ' . fnameescape(expand('%:p')) . ' bar ' . fnameescape(expand('%:p'))], ale#engine#FormatCommand(bufnr('%'), 'foo %s bar %s')
|
||||||
|
|
||||||
|
Execute(FormatCommand should convert %t to a new temporary filename):
|
||||||
|
let g:result = ale#engine#FormatCommand(bufnr('%'), 'foo %t bar %t')
|
||||||
|
let g:match = matchlist(g:result[1], '\v^foo (/tmp/.*/dummy.txt) bar (/tmp/.*/dummy.txt)$')
|
||||||
|
|
||||||
|
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
|
||||||
|
" The first item of the result should be a temporary filename, and it should
|
||||||
|
" be the same as the escaped name in the command string.
|
||||||
|
AssertEqual g:result[0], fnameescape(g:match[1])
|
||||||
|
" The two temporary filenames formatted in should be the same.
|
||||||
|
AssertEqual g:match[1], g:match[2]
|
||||||
|
|
||||||
|
Execute(FormatCommand should let you combine %s and %t):
|
||||||
|
let g:result = ale#engine#FormatCommand(bufnr('%'), 'foo %t bar %s')
|
||||||
|
let g:match = matchlist(g:result[1], '\v^foo (/tmp/.*/dummy.txt) bar (.*/dummy.txt)$')
|
||||||
|
|
||||||
|
Assert !empty(g:match), 'No match found! Result was: ' . g:result[1]
|
||||||
|
" The first item of the result should be a temporary filename, and it should
|
||||||
|
" be the same as the escaped name in the command string.
|
||||||
|
AssertEqual g:result[0], fnameescape(g:match[1])
|
||||||
|
" The second item should be equal to the original filename.
|
||||||
|
AssertEqual fnameescape(expand('%:p')), g:match[2]
|
||||||
|
|
||||||
|
Execute(EscapeCommandPart should escape all percent signs):
|
||||||
|
AssertEqual '%%s %%t %%%% %%s %%t %%%%', ale#engine#EscapeCommandPart('%s %t %% %s %t %%')
|
|
@ -0,0 +1,33 @@
|
||||||
|
Before:
|
||||||
|
let g:output = []
|
||||||
|
|
||||||
|
function! TestCallback(buffer, output)
|
||||||
|
let g:output = a:output
|
||||||
|
|
||||||
|
return []
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call ale#linter#Define('foobar', {
|
||||||
|
\ 'name': 'testlinter',
|
||||||
|
\ 'callback': 'TestCallback',
|
||||||
|
\ 'executable': 'cat',
|
||||||
|
\ 'command': 'cat %t',
|
||||||
|
\})
|
||||||
|
|
||||||
|
After:
|
||||||
|
unlet! g:output
|
||||||
|
delfunction TestCallback
|
||||||
|
call ale#linter#Reset()
|
||||||
|
|
||||||
|
Given foobar (Some imaginary filetype):
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
baz
|
||||||
|
|
||||||
|
Execute(ALE should be able to read the %t file):
|
||||||
|
AssertEqual 'foobar', &filetype
|
||||||
|
|
||||||
|
call ale#Lint()
|
||||||
|
call ale#engine#WaitForJobs(2000)
|
||||||
|
|
||||||
|
AssertEqual ['foo', 'bar', 'baz'], g:output
|
Loading…
Reference in New Issue