Close #2556 - Support filename mapping
ALE now supports mapping files between different systems for running linters and fixers with Docker, in virtual machines, in servers, etc.
This commit is contained in:
parent
2b785688ea
commit
ba3dd0d027
15 changed files with 483 additions and 74 deletions
12
README.md
12
README.md
|
@ -79,6 +79,7 @@ other content at [w0rp.com](https://w0rp.com).
|
||||||
17. [How can I configure my C or C++ project?](#faq-c-configuration)
|
17. [How can I configure my C or C++ project?](#faq-c-configuration)
|
||||||
18. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
|
18. [How can I configure ALE differently for different buffers?](#faq-buffer-configuration)
|
||||||
19. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height)
|
19. [How can I configure the height of the list in which ALE displays errors?](#faq-list-window-height)
|
||||||
|
20. [How can I run linters or fixers via Docker or a VM?](#faq-vm)
|
||||||
|
|
||||||
<a name="supported-languages"></a>
|
<a name="supported-languages"></a>
|
||||||
|
|
||||||
|
@ -877,3 +878,14 @@ To set a default height for the error list, use the `g:ale_list_window_size` var
|
||||||
" Show 5 lines of errors (default: 10)
|
" Show 5 lines of errors (default: 10)
|
||||||
let g:ale_list_window_size = 5
|
let g:ale_list_window_size = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<a name="faq-vm"></a>
|
||||||
|
|
||||||
|
### 5.xx. How can I run linters or fixers via Docker or a VM?
|
||||||
|
|
||||||
|
ALE supports running linters or fixers via Docker, virtual machines, or in
|
||||||
|
combination with any remote machine with a different file system, so long as the
|
||||||
|
tools are well-integrated with ALE, and ALE is properly configured to run the
|
||||||
|
correct commands and map filename paths between different file systems. See
|
||||||
|
`:help ale-lint-other-machines` for the full documentation on how to configure
|
||||||
|
ALE to support this.
|
||||||
|
|
|
@ -266,3 +266,23 @@ function! ale#GetLocItemMessage(item, format_string) abort
|
||||||
|
|
||||||
return l:msg
|
return l:msg
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Given a buffer and a linter or fixer name, return an Array of two-item
|
||||||
|
" Arrays describing how to map filenames to and from the local to foreign file
|
||||||
|
" systems.
|
||||||
|
function! ale#GetFilenameMappings(buffer, name) abort
|
||||||
|
let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings')
|
||||||
|
|
||||||
|
if type(l:linter_mappings) is v:t_list
|
||||||
|
return l:linter_mappings
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:name = a:name
|
||||||
|
|
||||||
|
if !has_key(l:linter_mappings, l:name)
|
||||||
|
" Use * as a default setting for all tools.
|
||||||
|
let l:name = '*'
|
||||||
|
endif
|
||||||
|
|
||||||
|
return get(l:linter_mappings, l:name, [])
|
||||||
|
endfunction
|
||||||
|
|
|
@ -133,11 +133,30 @@ function! ale#command#EscapeCommandPart(command_part) abort
|
||||||
return substitute(a:command_part, '%', '%%', 'g')
|
return substitute(a:command_part, '%', '%%', 'g')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Format a filename, converting it with filename mappings, if non-empty,
|
||||||
|
" and escaping it for putting into a command string.
|
||||||
|
function! s:FormatFilename(filename, mappings) abort
|
||||||
|
let l:filename = a:filename
|
||||||
|
|
||||||
|
if !empty(a:mappings)
|
||||||
|
let l:filename = ale#filename_mapping#Map(l:filename, a:mappings)
|
||||||
|
endif
|
||||||
|
|
||||||
|
return ale#Escape(l:filename)
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Given a command string, replace every...
|
" Given a command string, replace every...
|
||||||
" %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, executable, command, pipe_file_if_needed, input) abort
|
function! ale#command#FormatCommand(
|
||||||
|
\ buffer,
|
||||||
|
\ executable,
|
||||||
|
\ command,
|
||||||
|
\ pipe_file_if_needed,
|
||||||
|
\ input,
|
||||||
|
\ filename_mappings,
|
||||||
|
\) abort
|
||||||
let l:temporary_file = ''
|
let l:temporary_file = ''
|
||||||
let l:command = a:command
|
let l:command = a:command
|
||||||
|
|
||||||
|
@ -154,14 +173,14 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne
|
||||||
" file.
|
" file.
|
||||||
if l:command =~# '%s'
|
if l:command =~# '%s'
|
||||||
let l:filename = fnamemodify(bufname(a:buffer), ':p')
|
let l:filename = fnamemodify(bufname(a:buffer), ':p')
|
||||||
let l:command = substitute(l:command, '%s', '\=ale#Escape(l:filename)', 'g')
|
let l:command = substitute(l:command, '%s', '\=s:FormatFilename(l:filename, a:filename_mappings)', 'g')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if a:input isnot v:false && l:command =~# '%t'
|
if a:input isnot v:false && l:command =~# '%t'
|
||||||
" Create a temporary filename, <temp_dir>/<original_basename>
|
" Create a temporary filename, <temp_dir>/<original_basename>
|
||||||
" The file itself will not be created by this function.
|
" The file itself will not be created by this function.
|
||||||
let l:temporary_file = s:TemporaryFilename(a:buffer)
|
let l:temporary_file = s:TemporaryFilename(a:buffer)
|
||||||
let l:command = substitute(l:command, '%t', '\=ale#Escape(l:temporary_file)', 'g')
|
let l:command = substitute(l:command, '%t', '\=s:FormatFilename(l:temporary_file, a:filename_mappings)', 'g')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Finish formatting so %% becomes %.
|
" Finish formatting so %% becomes %.
|
||||||
|
@ -265,6 +284,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
|
||||||
\ a:command,
|
\ a:command,
|
||||||
\ get(l:options, 'read_buffer', 0),
|
\ get(l:options, 'read_buffer', 0),
|
||||||
\ get(l:options, 'input', v:null),
|
\ get(l:options, 'input', v:null),
|
||||||
|
\ get(l:options, 'filename_mappings', []),
|
||||||
\)
|
\)
|
||||||
let l:command = ale#job#PrepareCommand(a:buffer, l:command)
|
let l:command = ale#job#PrepareCommand(a:buffer, l:command)
|
||||||
let l:job_options = {
|
let l:job_options = {
|
||||||
|
|
|
@ -256,6 +256,13 @@ function! s:RemapItemTypes(type_map, loclist) abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort
|
function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort
|
||||||
|
let l:mappings = ale#GetFilenameMappings(a:buffer, a:linter_name)
|
||||||
|
|
||||||
|
if !empty(l:mappings)
|
||||||
|
" We need to apply reverse filename mapping here.
|
||||||
|
let l:mappings = ale#filename_mapping#Invert(l:mappings)
|
||||||
|
endif
|
||||||
|
|
||||||
let l:bufnr_map = {}
|
let l:bufnr_map = {}
|
||||||
let l:new_loclist = []
|
let l:new_loclist = []
|
||||||
|
|
||||||
|
@ -296,13 +303,19 @@ function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist)
|
||||||
let l:item.code = l:old_item.code
|
let l:item.code = l:old_item.code
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if has_key(l:old_item, 'filename')
|
let l:old_name = get(l:old_item, 'filename', '')
|
||||||
\&& !ale#path#IsTempName(l:old_item.filename)
|
|
||||||
|
" Map parsed from output to local filesystem files.
|
||||||
|
if !empty(l:old_name) && !empty(l:mappings)
|
||||||
|
let l:old_name = ale#filename_mapping#Map(l:old_name, l:mappings)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !empty(l:old_name) && !ale#path#IsTempName(l:old_name)
|
||||||
" Use the filename given.
|
" Use the filename given.
|
||||||
" Temporary files are assumed to be for this buffer,
|
" Temporary files are assumed to be for this buffer,
|
||||||
" and the filename is not included then, because it looks bad
|
" and the filename is not included then, because it looks bad
|
||||||
" in the loclist window.
|
" in the loclist window.
|
||||||
let l:filename = l:old_item.filename
|
let l:filename = l:old_name
|
||||||
let l:item.filename = l:filename
|
let l:item.filename = l:filename
|
||||||
|
|
||||||
if has_key(l:old_item, 'bufnr')
|
if has_key(l:old_item, 'bufnr')
|
||||||
|
@ -415,6 +428,7 @@ function! s:RunJob(command, options) abort
|
||||||
\ 'executable': l:executable,
|
\ 'executable': l:executable,
|
||||||
\ 'read_buffer': l:read_buffer,
|
\ 'read_buffer': l:read_buffer,
|
||||||
\ 'log_output': 1,
|
\ 'log_output': 1,
|
||||||
|
\ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:linter.name),
|
||||||
\})
|
\})
|
||||||
|
|
||||||
" Only proceed if the job is being run.
|
" Only proceed if the job is being run.
|
||||||
|
|
22
autoload/ale/filename_mapping.vim
Normal file
22
autoload/ale/filename_mapping.vim
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
" Author: w0rp <devw0rp@gmail.com>
|
||||||
|
" Description: Logic for handling mappings between files
|
||||||
|
|
||||||
|
" Invert filesystem mappings so they can be mapped in reverse.
|
||||||
|
function! ale#filename_mapping#Invert(filename_mappings) abort
|
||||||
|
return map(copy(a:filename_mappings), '[v:val[1], v:val[0]]')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Given a filename and some filename_mappings, map a filename.
|
||||||
|
function! ale#filename_mapping#Map(filename, filename_mappings) abort
|
||||||
|
let l:simplified_filename = ale#path#Simplify(a:filename)
|
||||||
|
|
||||||
|
for [l:mapping_from, l:mapping_to] in a:filename_mappings
|
||||||
|
let l:mapping_from = ale#path#Simplify(l:mapping_from)
|
||||||
|
|
||||||
|
if l:simplified_filename[:len(l:mapping_from) - 1] is# l:mapping_from
|
||||||
|
return l:mapping_to . l:simplified_filename[len(l:mapping_from):]
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return a:filename
|
||||||
|
endfunction
|
|
@ -1,4 +1,8 @@
|
||||||
|
" Author: w0rp <devw0rp@gmail.com>
|
||||||
|
" Description: Functions for fixing code with programs, or other means.
|
||||||
|
|
||||||
call ale#Set('fix_on_save_ignore', {})
|
call ale#Set('fix_on_save_ignore', {})
|
||||||
|
call ale#Set('filename_mappings', {})
|
||||||
|
|
||||||
" Apply fixes queued up for buffers which may be hidden.
|
" Apply fixes queued up for buffers which may be hidden.
|
||||||
" Vim doesn't let you modify hidden buffers.
|
" Vim doesn't let you modify hidden buffers.
|
||||||
|
@ -110,7 +114,6 @@ function! s:HandleExit(job_info, buffer, job_output, data) abort
|
||||||
call s:RunFixer({
|
call s:RunFixer({
|
||||||
\ 'buffer': a:buffer,
|
\ 'buffer': a:buffer,
|
||||||
\ 'input': l:input,
|
\ 'input': l:input,
|
||||||
\ 'output': l:output,
|
|
||||||
\ 'callback_list': a:job_info.callback_list,
|
\ 'callback_list': a:job_info.callback_list,
|
||||||
\ 'callback_index': a:job_info.callback_index + 1,
|
\ 'callback_index': a:job_info.callback_index + 1,
|
||||||
\})
|
\})
|
||||||
|
@ -125,6 +128,7 @@ function! s:RunJob(result, options) abort
|
||||||
|
|
||||||
let l:buffer = a:options.buffer
|
let l:buffer = a:options.buffer
|
||||||
let l:input = a:options.input
|
let l:input = a:options.input
|
||||||
|
let l:fixer_name = a:options.fixer_name
|
||||||
|
|
||||||
if a:result is 0 || type(a:result) is v:t_list
|
if a:result is 0 || type(a:result) is v:t_list
|
||||||
if type(a:result) is v:t_list
|
if type(a:result) is v:t_list
|
||||||
|
@ -150,7 +154,6 @@ function! s:RunJob(result, options) abort
|
||||||
\ 'input': l:input,
|
\ 'input': l:input,
|
||||||
\ 'callback_index': a:options.callback_index,
|
\ 'callback_index': a:options.callback_index,
|
||||||
\ 'callback_list': a:options.callback_list,
|
\ 'callback_list': a:options.callback_list,
|
||||||
\ 'output': [],
|
|
||||||
\})
|
\})
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -177,6 +180,7 @@ function! s:RunJob(result, options) abort
|
||||||
\ 'read_buffer': l:read_buffer,
|
\ 'read_buffer': l:read_buffer,
|
||||||
\ 'input': l:input,
|
\ 'input': l:input,
|
||||||
\ 'log_output': 0,
|
\ 'log_output': 0,
|
||||||
|
\ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name),
|
||||||
\})
|
\})
|
||||||
|
|
||||||
if empty(l:run_result)
|
if empty(l:run_result)
|
||||||
|
@ -200,32 +204,22 @@ function! s:RunFixer(options) abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:ChainCallback = get(a:options, 'chain_callback', v:null)
|
let [l:fixer_name, l:Function] = a:options.callback_list[l:index]
|
||||||
|
|
||||||
let l:Function = l:ChainCallback isnot v:null
|
|
||||||
\ ? ale#util#GetFunction(l:ChainCallback)
|
|
||||||
\ : a:options.callback_list[l:index]
|
|
||||||
|
|
||||||
" Record new jobs started as fixer jobs.
|
" Record new jobs started as fixer jobs.
|
||||||
call setbufvar(l:buffer, 'ale_job_type', 'fixer')
|
call setbufvar(l:buffer, 'ale_job_type', 'fixer')
|
||||||
|
|
||||||
if l:ChainCallback isnot v:null
|
" Regular fixer commands accept (buffer, [input])
|
||||||
" Chained commands accept (buffer, output, [input])
|
let l:result = ale#util#FunctionArgCount(l:Function) == 1
|
||||||
let l:result = ale#util#FunctionArgCount(l:Function) == 2
|
\ ? call(l:Function, [l:buffer])
|
||||||
\ ? call(l:Function, [l:buffer, a:options.output])
|
\ : call(l:Function, [l:buffer, copy(l:input)])
|
||||||
\ : call(l:Function, [l:buffer, a:options.output, copy(l:input)])
|
|
||||||
else
|
|
||||||
" Regular fixer commands accept (buffer, [input])
|
|
||||||
let l:result = ale#util#FunctionArgCount(l:Function) == 1
|
|
||||||
\ ? call(l:Function, [l:buffer])
|
|
||||||
\ : call(l:Function, [l:buffer, copy(l:input)])
|
|
||||||
endif
|
|
||||||
|
|
||||||
call s:RunJob(l:result, {
|
call s:RunJob(l:result, {
|
||||||
\ 'buffer': l:buffer,
|
\ 'buffer': l:buffer,
|
||||||
\ 'input': l:input,
|
\ 'input': l:input,
|
||||||
\ 'callback_list': a:options.callback_list,
|
\ 'callback_list': a:options.callback_list,
|
||||||
\ 'callback_index': l:index,
|
\ 'callback_index': l:index,
|
||||||
|
\ 'fixer_name': l:fixer_name,
|
||||||
\})
|
\})
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -293,16 +287,24 @@ function! s:GetCallbacks(buffer, fixing_flag, fixers) abort
|
||||||
" Variables with capital characters are needed, or Vim will complain about
|
" Variables with capital characters are needed, or Vim will complain about
|
||||||
" funcref variables.
|
" funcref variables.
|
||||||
for l:Item in l:callback_list
|
for l:Item in l:callback_list
|
||||||
|
" Try to capture the names of registered fixer names, so we can use
|
||||||
|
" them for filename mapping or other purposes later.
|
||||||
|
let l:fixer_name = v:null
|
||||||
|
|
||||||
if type(l:Item) is v:t_string
|
if type(l:Item) is v:t_string
|
||||||
let l:Func = ale#fix#registry#GetFunc(l:Item)
|
let l:Func = ale#fix#registry#GetFunc(l:Item)
|
||||||
|
|
||||||
if !empty(l:Func)
|
if !empty(l:Func)
|
||||||
|
let l:fixer_name = l:Item
|
||||||
let l:Item = l:Func
|
let l:Item = l:Func
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
try
|
try
|
||||||
call add(l:corrected_list, ale#util#GetFunction(l:Item))
|
call add(l:corrected_list, [
|
||||||
|
\ l:fixer_name,
|
||||||
|
\ ale#util#GetFunction(l:Item)
|
||||||
|
\])
|
||||||
catch /E475/
|
catch /E475/
|
||||||
" Rethrow exceptions for failing to get a function so we can print
|
" Rethrow exceptions for failing to get a function so we can print
|
||||||
" a friendly message about it.
|
" a friendly message about it.
|
||||||
|
|
|
@ -160,11 +160,11 @@ let s:default_registry = {
|
||||||
\ 'suggested_filetypes': ['php'],
|
\ 'suggested_filetypes': ['php'],
|
||||||
\ 'description': 'Fix PHP files with php-cs-fixer.',
|
\ 'description': 'Fix PHP files with php-cs-fixer.',
|
||||||
\ },
|
\ },
|
||||||
\ 'astyle': {
|
\ 'astyle': {
|
||||||
\ 'function': 'ale#fixers#astyle#Fix',
|
\ 'function': 'ale#fixers#astyle#Fix',
|
||||||
\ 'suggested_filetypes': ['c', 'cpp'],
|
\ 'suggested_filetypes': ['c', 'cpp'],
|
||||||
\ 'description': 'Fix C/C++ with astyle.',
|
\ 'description': 'Fix C/C++ with astyle.',
|
||||||
\ },
|
\ },
|
||||||
\ 'clangtidy': {
|
\ 'clangtidy': {
|
||||||
\ 'function': 'ale#fixers#clangtidy#Fix',
|
\ 'function': 'ale#fixers#clangtidy#Fix',
|
||||||
\ 'suggested_filetypes': ['c', 'cpp', 'objc'],
|
\ 'suggested_filetypes': ['c', 'cpp', 'objc'],
|
||||||
|
|
|
@ -265,7 +265,14 @@ function! s:StartLSP(options, address, executable, command) abort
|
||||||
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
|
call ale#lsp#MarkConnectionAsTsserver(l:conn_id)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:command = ale#command#FormatCommand(l:buffer, a:executable, a:command, 0, v:false)[1]
|
let l:command = ale#command#FormatCommand(
|
||||||
|
\ l:buffer,
|
||||||
|
\ a:executable,
|
||||||
|
\ a:command,
|
||||||
|
\ 0,
|
||||||
|
\ v:false,
|
||||||
|
\ [],
|
||||||
|
\)[1]
|
||||||
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
|
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
|
||||||
let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command)
|
let l:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -95,7 +95,7 @@ function! ale#path#IsAbsolute(filename) abort
|
||||||
return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
|
return a:filename[:0] is# '/' || a:filename[1:2] is# ':\'
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h'))
|
let s:temp_dir = ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h'))
|
||||||
|
|
||||||
" Given a filename, return 1 if the file represents some temporary file
|
" Given a filename, return 1 if the file represents some temporary file
|
||||||
" created by Vim.
|
" created by Vim.
|
||||||
|
|
197
doc/ale.txt
197
doc/ale.txt
|
@ -9,8 +9,9 @@ CONTENTS *ale-contents*
|
||||||
1. Introduction.........................|ale-introduction|
|
1. Introduction.........................|ale-introduction|
|
||||||
2. Supported Languages & Tools..........|ale-support|
|
2. Supported Languages & Tools..........|ale-support|
|
||||||
3. Linting..............................|ale-lint|
|
3. Linting..............................|ale-lint|
|
||||||
3.1 Adding Language Servers...........|ale-lint-language-servers|
|
3.1 Linting On Other Machines.........|ale-lint-other-machines|
|
||||||
3.2 Other Sources.....................|ale-lint-other-sources|
|
3.2 Adding Language Servers...........|ale-lint-language-servers|
|
||||||
|
3.3 Other Sources.....................|ale-lint-other-sources|
|
||||||
4. Fixing Problems......................|ale-fix|
|
4. Fixing Problems......................|ale-fix|
|
||||||
5. Language Server Protocol Support.....|ale-lsp|
|
5. Language Server Protocol Support.....|ale-lsp|
|
||||||
5.1 Completion........................|ale-completion|
|
5.1 Completion........................|ale-completion|
|
||||||
|
@ -148,7 +149,61 @@ ALE offers several options for controlling which linters are run.
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
3.1 Adding Language Servers *ale-lint-language-servers*
|
3.1 Linting On Other Machines *ale-lint-other-machines*
|
||||||
|
|
||||||
|
ALE offers support for running linters or fixers on files you are editing
|
||||||
|
locally on other machines, so long as the other machine has access the file
|
||||||
|
you are editing. This could be a linter or fixer run inside of a Docker image,
|
||||||
|
running in a virtual machine, running on a remote server, etc.
|
||||||
|
|
||||||
|
In order to run tools on other machines, you will need to configure your tools
|
||||||
|
to run via scripts that execute commands on those machines, such as by setting
|
||||||
|
the ALE `_executable` options for those tools to a path for a script to run,
|
||||||
|
or by using |g:ale_command_wrapper| to specify a script to wrap all commands
|
||||||
|
that are run by ALE, before they are executed. For tools that ALE runs where
|
||||||
|
ALE looks for locally installed executables first, you may need to set the
|
||||||
|
`_use_global` options for those tools to `1`, or you can set
|
||||||
|
|g:ale_use_global_executables| to `1` before ALE is loaded to only use global
|
||||||
|
executables for all tools.
|
||||||
|
|
||||||
|
In order for ALE to properly lint or fix files which are running on another
|
||||||
|
file system, you must provide ALE with |List|s of strings for mapping paths to
|
||||||
|
and from your local file system and the remote file system, such as the file
|
||||||
|
system of your Docker container. See |g:ale_filename_mappings| for all of the
|
||||||
|
different ways these filename mappings can be configured.
|
||||||
|
|
||||||
|
For example, you might configure `pylint` to run via Docker by creating a
|
||||||
|
script like so. >
|
||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
exec docker run --rm -v "$(pwd):/data" cytopia/pylint "$@"
|
||||||
|
<
|
||||||
|
With the above script in mind, you might configure ALE to lint your Python
|
||||||
|
project with `pylint` by providing the path to the script to execute, and
|
||||||
|
mappings which describe how to between the two file systems in your
|
||||||
|
`python.vim` |ftplugin| file, like so: >
|
||||||
|
|
||||||
|
if expand('%:p') =~# '^/home/w0rp/git/test-pylint/'
|
||||||
|
let b:ale_linters = ['pylint']
|
||||||
|
let b:ale_python_pylint_use_global = 1
|
||||||
|
" This is the path to the script above.
|
||||||
|
let b:ale_python_pylint_executable = '/home/w0rp/git/test-pylint/pylint.sh'
|
||||||
|
" /data matches the path in Docker.
|
||||||
|
let b:ale_filename_mappings = {
|
||||||
|
\ 'pylint': [
|
||||||
|
\ ['/home/w0rp/git/test-pylint', '/data'],
|
||||||
|
\ ],
|
||||||
|
\}
|
||||||
|
endif
|
||||||
|
<
|
||||||
|
|
||||||
|
You might consider using a Vim plugin for loading Vim configuration files
|
||||||
|
specific to each project, if you have a lot of projects to manage.
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
3.2 Adding Language Servers *ale-lint-language-servers*
|
||||||
|
|
||||||
ALE comes with many default configurations for language servers, so they can
|
ALE comes with many default configurations for language servers, so they can
|
||||||
be detected and run automatically. ALE can connect to other language servers
|
be detected and run automatically. ALE can connect to other language servers
|
||||||
|
@ -189,7 +244,7 @@ address to connect to instead. >
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
3.2 Other Sources *ale-lint-other-sources*
|
3.3 Other Sources *ale-lint-other-sources*
|
||||||
|
|
||||||
Problems for a buffer can be taken from other sources and rendered by ALE.
|
Problems for a buffer can be taken from other sources and rendered by ALE.
|
||||||
This allows ALE to be used in combination with other plugins which also want
|
This allows ALE to be used in combination with other plugins which also want
|
||||||
|
@ -356,6 +411,10 @@ by default.
|
||||||
Fixers can be disabled on save with |g:ale_fix_on_save_ignore|. They will
|
Fixers can be disabled on save with |g:ale_fix_on_save_ignore|. They will
|
||||||
still be run when you manually run |ALEFix|.
|
still be run when you manually run |ALEFix|.
|
||||||
|
|
||||||
|
Fixers can be run on another machines, just like linters, such as fixers run
|
||||||
|
from a Docker container, running in a virtual machine, running a remote
|
||||||
|
server, etc. See |ale-lint-other-machines|.
|
||||||
|
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
5. Language Server Protocol Support *ale-lsp*
|
5. Language Server Protocol Support *ale-lsp*
|
||||||
|
@ -1286,6 +1345,90 @@ g:ale_linter_aliases *g:ale_linter_aliases*
|
||||||
<
|
<
|
||||||
No linters will be loaded when the buffer's filetype is empty.
|
No linters will be loaded when the buffer's filetype is empty.
|
||||||
|
|
||||||
|
|
||||||
|
g:ale_filename_mappings *g:ale_filename_mappings*
|
||||||
|
*b:ale_filename_mappings*
|
||||||
|
|
||||||
|
Type: |Dictionary| or |List|
|
||||||
|
Default: `{}`
|
||||||
|
|
||||||
|
Either a |Dictionary| mapping a linter or fixer name, as displayed in
|
||||||
|
|:ALEInfo|, to a |List| of two-item |List|s for filename mappings, or just a
|
||||||
|
|List| of two-item |List|s. When given some paths to files, the value of
|
||||||
|
this setting will be used to convert filenames on a local file system to
|
||||||
|
filenames on some remote file system, such as paths in a Docker image,
|
||||||
|
virtual machine, or network drive.
|
||||||
|
|
||||||
|
For example: >
|
||||||
|
|
||||||
|
let g:ale_filename_mappings = {
|
||||||
|
\ 'pylint': [
|
||||||
|
\ ['/home/john/proj', '/data'],
|
||||||
|
\ ],
|
||||||
|
\}
|
||||||
|
<
|
||||||
|
With the above configuration, a filename such as `/home/john/proj/foo.py`
|
||||||
|
will be provided to the linter/fixer as `/data/foo.py`, and paths parsed
|
||||||
|
from linter results such as `/data/foo.py` will be converted back to
|
||||||
|
`/home/john/proj/foo.py`.
|
||||||
|
|
||||||
|
You can use `*` as to apply a |List| of filename mappings to all other
|
||||||
|
linters or fixers not otherwise matched. >
|
||||||
|
|
||||||
|
" Use one List of paths for pylint.
|
||||||
|
" Use another List of paths for everything else.
|
||||||
|
let g:ale_filename_mappings = {
|
||||||
|
\ 'pylint': [
|
||||||
|
\ ['/home/john/proj', '/data'],
|
||||||
|
\ ],
|
||||||
|
\ '*': [
|
||||||
|
\ ['/home/john/proj', '/other-data'],
|
||||||
|
\ ],
|
||||||
|
\}
|
||||||
|
<
|
||||||
|
If you just want every single linter or fixer to use the same filename
|
||||||
|
mapping, you can just use a |List|. >
|
||||||
|
|
||||||
|
" Same as above, but for ALL linters and fixers.
|
||||||
|
let g:ale_filename_mappings = [
|
||||||
|
\ ['/home/john/proj', '/data'],
|
||||||
|
\]
|
||||||
|
<
|
||||||
|
You can provide many such filename paths for multiple projects. Paths are
|
||||||
|
matched by checking if the start of a file path matches the given strings,
|
||||||
|
in a case-sensitive manner. Earlier entries in the |List| will be tried
|
||||||
|
before later entries when mapping to a given file system.
|
||||||
|
|
||||||
|
Buffer-local options can be set to the same values to override the global
|
||||||
|
options, such as in |ftplugin| files.
|
||||||
|
|
||||||
|
NOTE: Only fixers registered with a short name can support filename mapping
|
||||||
|
by their fixer names. See |ale-fix|. Filename mappings set for all tools by
|
||||||
|
using only a |List| for the setting will also be applied to fixers not in
|
||||||
|
the registry.
|
||||||
|
|
||||||
|
NOTE: In order for this filename mapping to work correctly, linters and
|
||||||
|
fixers must exclusively determine paths to files to lint or fix via ALE
|
||||||
|
command formatting as per |ale-command-format-strings|, and paths parsed
|
||||||
|
from linter files must be provided in `filename` keys if a linter returns
|
||||||
|
results for more than one file at a time, as per |ale-loclist-format|. If
|
||||||
|
you discover a linter or fixer which does not behave properly, please report
|
||||||
|
it as an issue.
|
||||||
|
|
||||||
|
If you are running a linter or fixer through Docker or another remote file
|
||||||
|
system, you may have to mount your temporary directory, which you can
|
||||||
|
discover with the following command: >
|
||||||
|
|
||||||
|
:echo fnamemodify(tempname(), ':h:h')
|
||||||
|
<
|
||||||
|
You should provide a mapping from this temporary directory to whatever you
|
||||||
|
mount this directory to in Docker, or whatever remote file system you are
|
||||||
|
working with.
|
||||||
|
|
||||||
|
You can inspect the filename mappings ALE will use with the
|
||||||
|
|ale#GetFilenameMappings()| function.
|
||||||
|
|
||||||
|
|
||||||
g:ale_linters *g:ale_linters*
|
g:ale_linters *g:ale_linters*
|
||||||
*b:ale_linters*
|
*b:ale_linters*
|
||||||
Type: |Dictionary|
|
Type: |Dictionary|
|
||||||
|
@ -3073,6 +3216,15 @@ ale#Env(variable_name, value) *ale#Env()*
|
||||||
'set VAR="some value" && command' # On Windows
|
'set VAR="some value" && command' # On Windows
|
||||||
|
|
||||||
|
|
||||||
|
ale#GetFilenameMappings(buffer, name) *ale#GetFilenameMappings()*
|
||||||
|
|
||||||
|
Given a `buffer` and the `name` of either a linter for fixer, return a
|
||||||
|
|List| of two-item |List|s that describe mapping to and from the local and
|
||||||
|
foreign file systems for running a particular linter or fixer.
|
||||||
|
|
||||||
|
See |g:ale_filename_mappings| for details on filename mapping.
|
||||||
|
|
||||||
|
|
||||||
ale#Has(feature) *ale#Has()*
|
ale#Has(feature) *ale#Has()*
|
||||||
|
|
||||||
Return `1` if ALE supports a given feature, like |has()| for Vim features.
|
Return `1` if ALE supports a given feature, like |has()| for Vim features.
|
||||||
|
@ -3187,23 +3339,36 @@ ale#command#Run(buffer, command, callback, [options]) *ale#command#Run()*
|
||||||
<
|
<
|
||||||
The following `options` can be provided.
|
The following `options` can be provided.
|
||||||
|
|
||||||
`output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or `'none`' for
|
`output_stream` - Either `'stdout'`, `'stderr'`, `'both'`, or
|
||||||
selecting which output streams to read lines from.
|
`'none`' for selecting which output streams to read
|
||||||
|
lines from.
|
||||||
|
|
||||||
The default is `'stdout'`
|
The default is `'stdout'`
|
||||||
|
|
||||||
`executable` - An executable for formatting into `%e` in the command.
|
`executable` - An executable for formatting into `%e` in the
|
||||||
If this option is not provided, formatting commands with
|
command. If this option is not provided, formatting
|
||||||
`%e` will not work.
|
commands with `%e` will not work.
|
||||||
|
|
||||||
`read_buffer` - If set to `1`, the buffer will be piped into the
|
`read_buffer` - If set to `1`, the buffer will be piped into the
|
||||||
command.
|
command.
|
||||||
|
|
||||||
The default is `0`.
|
The default is `0`.
|
||||||
|
|
||||||
|
`input` - When creating temporary files with `%t` or piping
|
||||||
|
text into a command `input` can be set to a |List| of
|
||||||
|
text to use instead of the buffer's text.
|
||||||
|
|
||||||
|
`filename_mappings` - A |List| of two-item |List|s describing filename
|
||||||
|
mappings to apply for formatted filenames in the
|
||||||
|
command string, as per |g:ale_filename_mappings|.
|
||||||
|
|
||||||
|
If the call to this function is being used for a
|
||||||
|
linter or fixer, the mappings should be provided with
|
||||||
|
this option, and can be retrieved easily with
|
||||||
|
|ale#GetFilenameMappings()|.
|
||||||
|
|
||||||
|
The default is `[]`.
|
||||||
|
|
||||||
`input` - When creating temporary files with `%t` or piping text
|
|
||||||
into a command `input` can be set to a |List| of text to
|
|
||||||
use instead of the buffer's text.
|
|
||||||
|
|
||||||
|
|
||||||
ale#command#EscapeCommandPart(command_part) *ale#command#EscapeCommandPart()*
|
ale#command#EscapeCommandPart(command_part) *ale#command#EscapeCommandPart()*
|
||||||
|
|
|
@ -97,6 +97,10 @@ let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 0)
|
||||||
" should be used instead.
|
" should be used instead.
|
||||||
let g:ale_enabled = get(g:, 'ale_enabled', 1)
|
let g:ale_enabled = get(g:, 'ale_enabled', 1)
|
||||||
|
|
||||||
|
" A Dictionary mapping linter or fixer names to Arrays of two-item Arrays
|
||||||
|
" mapping filename paths from one system to another.
|
||||||
|
let g:ale_filename_mappings = get(g:, 'ale_filename_mappings', {})
|
||||||
|
|
||||||
" These flags dictates if ale uses the quickfix or the loclist (loclist is the
|
" These flags dictates if ale uses the quickfix or the loclist (loclist is the
|
||||||
" default, quickfix overrides loclist).
|
" default, quickfix overrides loclist).
|
||||||
let g:ale_set_loclist = get(g:, 'ale_set_loclist', 1)
|
let g:ale_set_loclist = get(g:, 'ale_set_loclist', 1)
|
||||||
|
|
|
@ -6,6 +6,7 @@ Before:
|
||||||
Save g:ale_lint_on_save
|
Save g:ale_lint_on_save
|
||||||
Save g:ale_echo_cursor
|
Save g:ale_echo_cursor
|
||||||
Save g:ale_command_wrapper
|
Save g:ale_command_wrapper
|
||||||
|
Save g:ale_filename_mappings
|
||||||
|
|
||||||
silent! cd /testplugin/test/fix
|
silent! cd /testplugin/test/fix
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ Before:
|
||||||
let g:ale_fixers = {
|
let g:ale_fixers = {
|
||||||
\ 'testft': [],
|
\ 'testft': [],
|
||||||
\}
|
\}
|
||||||
|
let g:ale_filename_mappings = {}
|
||||||
|
|
||||||
let g:pre_success = 0
|
let g:pre_success = 0
|
||||||
let g:post_success = 0
|
let g:post_success = 0
|
||||||
|
@ -72,6 +74,10 @@ Before:
|
||||||
return {'command': 'cat %t <(echo d)'}
|
return {'command': 'cat %t <(echo d)'}
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function EchoFilename(buffer, lines) abort
|
||||||
|
return {'command': 'echo %s'}
|
||||||
|
endfunction
|
||||||
|
|
||||||
function RemoveLastLine(buffer, lines) abort
|
function RemoveLastLine(buffer, lines) abort
|
||||||
return ['a', 'b']
|
return ['a', 'b']
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -155,6 +161,7 @@ After:
|
||||||
delfunction CatLineDeferred
|
delfunction CatLineDeferred
|
||||||
delfunction ReplaceWithTempFile
|
delfunction ReplaceWithTempFile
|
||||||
delfunction CatWithTempFile
|
delfunction CatWithTempFile
|
||||||
|
delfunction EchoFilename
|
||||||
delfunction RemoveLastLine
|
delfunction RemoveLastLine
|
||||||
delfunction RemoveLastLineOneArg
|
delfunction RemoveLastLineOneArg
|
||||||
delfunction TestCallback
|
delfunction TestCallback
|
||||||
|
@ -209,6 +216,23 @@ Expect(The first function should be used):
|
||||||
^b
|
^b
|
||||||
^c
|
^c
|
||||||
|
|
||||||
|
Execute(Should apply filename mpapings):
|
||||||
|
" The command echos %s, and we'll map the current path so we can check
|
||||||
|
" that ALEFix applies filename mappings, end-to-end.
|
||||||
|
let g:ale_filename_mappings = {
|
||||||
|
\ 'echo_filename': [
|
||||||
|
\ [expand('%:p:h'), '/some/fake/path'],
|
||||||
|
\ ],
|
||||||
|
\}
|
||||||
|
|
||||||
|
call ale#fix#registry#Add('echo_filename', 'EchoFilename', [], 'echo filename')
|
||||||
|
let g:ale_fixers.testft = ['echo_filename']
|
||||||
|
ALEFix
|
||||||
|
call ale#test#FlushJobs()
|
||||||
|
|
||||||
|
Expect(The mapped filename should be printed):
|
||||||
|
/some/fake/path/test.txt
|
||||||
|
|
||||||
Execute(ALEFix should apply simple functions in a chain):
|
Execute(ALEFix should apply simple functions in a chain):
|
||||||
let g:ale_fixers.testft = ['AddCarets', 'Capitalize']
|
let g:ale_fixers.testft = ['AddCarets', 'Capitalize']
|
||||||
ALEFix
|
ALEFix
|
||||||
|
|
62
test/test_filename_mapping.vader
Normal file
62
test/test_filename_mapping.vader
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
Before:
|
||||||
|
Save g:ale_filename_mappings
|
||||||
|
Save b:ale_filename_mappings
|
||||||
|
|
||||||
|
let g:ale_filename_mappings = {}
|
||||||
|
unlet! b:ale_filename_mappings
|
||||||
|
|
||||||
|
After:
|
||||||
|
Restore
|
||||||
|
|
||||||
|
Execute(ale#GetFilenameMappings should return the correct mappings for given linters/fixers):
|
||||||
|
let g:ale_filename_mappings = {'a': [['foo', 'bar']], 'b': [['baz', 'foo']]}
|
||||||
|
|
||||||
|
AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
|
||||||
|
AssertEqual [['baz', 'foo']], ale#GetFilenameMappings(bufnr(''), 'b')
|
||||||
|
AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c')
|
||||||
|
|
||||||
|
let b:ale_filename_mappings = {'b': [['abc', 'xyz']]}
|
||||||
|
|
||||||
|
AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'a')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b')
|
||||||
|
AssertEqual [], ale#GetFilenameMappings(bufnr(''), 'c')
|
||||||
|
|
||||||
|
Execute(ale#GetFilenameMappings should return Lists set for use with all tools):
|
||||||
|
let g:ale_filename_mappings = [['foo', 'bar']]
|
||||||
|
|
||||||
|
AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
|
||||||
|
AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), '')
|
||||||
|
AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), v:null)
|
||||||
|
|
||||||
|
let b:ale_filename_mappings = [['abc', 'xyz']]
|
||||||
|
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'a')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null)
|
||||||
|
|
||||||
|
Execute(ale#GetFilenameMappings should let you use * as a fallback):
|
||||||
|
let g:ale_filename_mappings = {'a': [['foo', 'bar']], '*': [['abc', 'xyz']]}
|
||||||
|
|
||||||
|
AssertEqual [['foo', 'bar']], ale#GetFilenameMappings(bufnr(''), 'a')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), 'b')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), '')
|
||||||
|
AssertEqual [['abc', 'xyz']], ale#GetFilenameMappings(bufnr(''), v:null)
|
||||||
|
|
||||||
|
Execute(ale#filename_mapping#Invert should invert filename mappings):
|
||||||
|
AssertEqual
|
||||||
|
\ [['b', 'a'], ['y', 'x']],
|
||||||
|
\ ale#filename_mapping#Invert([['a', 'b'], ['x', 'y']])
|
||||||
|
\
|
||||||
|
Execute(ale#filename_mapping#Map return the filename as-is if there are no mappings):
|
||||||
|
AssertEqual
|
||||||
|
\ '/foo//bar',
|
||||||
|
\ ale#filename_mapping#Map('/foo//bar', [['/bar', '/data/']])
|
||||||
|
|
||||||
|
Execute(ale#filename_mapping#Map should map filenames):
|
||||||
|
AssertEqual
|
||||||
|
\ '/data/bar',
|
||||||
|
\ ale#filename_mapping#Map('/foo//bar', [
|
||||||
|
\ ['/data', '/baz'],
|
||||||
|
\ ['/foo', '/data'],
|
||||||
|
\ ['/foo', '/xyz'],
|
||||||
|
\ ])
|
|
@ -25,12 +25,12 @@ After:
|
||||||
Execute(FormatCommand should do nothing to basic command strings):
|
Execute(FormatCommand should do nothing to basic command strings):
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', 'awesome-linter do something', 0],
|
\ ['', 'awesome-linter do something', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), '', 'awesome-linter do something', 0, v:null, [])
|
||||||
|
|
||||||
Execute(FormatCommand should handle %%, and ignore other percents):
|
Execute(FormatCommand should handle %%, and ignore other percents):
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '% %%d %%f %x %', 0],
|
\ ['', '% %%d %%f %x %', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), '', '%% %%%d %%%f %x %', 0, v:null, [])
|
||||||
|
|
||||||
Execute(FormatCommand should convert %s to the current filename):
|
Execute(FormatCommand should convert %s to the current filename):
|
||||||
AssertEqual
|
AssertEqual
|
||||||
|
@ -39,10 +39,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')),
|
||||||
\ 0,
|
\ 0,
|
||||||
\ ],
|
\ ],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %s bar %s', 0, v:null, [])
|
||||||
|
|
||||||
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, v:null)
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:null, [])
|
||||||
|
|
||||||
call CheckTempFile(g:result[0])
|
call CheckTempFile(g:result[0])
|
||||||
|
|
||||||
|
@ -56,21 +56,21 @@ 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 not convert %t to a new temporary filename when the input is given as v:false):
|
Execute(FormatCommand should not convert %t to a new temporary filename when the input is given as v:false):
|
||||||
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false)
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %t', 0, v:false, [])
|
||||||
|
|
||||||
AssertEqual ['', 'foo %t bar %t', 0], g:result
|
AssertEqual ['', 'foo %t bar %t', 0], g:result
|
||||||
|
|
||||||
Execute(FormatCommand should signal that files are created when temporary files are needed):
|
Execute(FormatCommand should signal that files are created when temporary files are needed):
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ 1,
|
\ 1,
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null)[2]
|
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %t', 0, v:null, [])[2]
|
||||||
|
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ 0,
|
\ 0,
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null)[2]
|
\ ale#command#FormatCommand(bufnr('%'), '', 'foo %s', 0, v:null, [])[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, v:null)
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo %t bar %s', 0, v:null, [])
|
||||||
|
|
||||||
call CheckTempFile(g:result[0])
|
call CheckTempFile(g:result[0])
|
||||||
|
|
||||||
|
@ -87,30 +87,30 @@ Execute(FormatCommand should replace %e with the escaped executable):
|
||||||
if has('win32')
|
if has('win32')
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', 'foo foo', 0],
|
\ ['', 'foo foo', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, [])
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '"foo bar"', 0],
|
\ ['', '"foo bar"', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, [])
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '%e %e', 0],
|
\ ['', '%e %e', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, [])
|
||||||
else
|
else
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '''foo'' ''foo''', 0],
|
\ ['', '''foo'' ''foo''', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), 'foo', '%e %e', 0, v:null, [])
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '''foo bar''', 0],
|
\ ['', '''foo bar''', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), 'foo bar', '%e', 0, v:null, [])
|
||||||
AssertEqual
|
AssertEqual
|
||||||
\ ['', '%e %e', 0],
|
\ ['', '%e %e', 0],
|
||||||
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null)
|
\ ale#command#FormatCommand(bufnr('%'), '', '%e %e', 0, v:null, [])
|
||||||
endif
|
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, v:null)
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar', 1, v:null, [])
|
||||||
|
|
||||||
call CheckTempFile(g:result[0])
|
call CheckTempFile(g:result[0])
|
||||||
|
|
||||||
|
@ -118,10 +118,24 @@ 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, v:null)
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', 'foo bar %t', 1, v:null, [])
|
||||||
|
|
||||||
call CheckTempFile(g:result[0])
|
call CheckTempFile(g:result[0])
|
||||||
|
|
||||||
let g:match = matchlist(g:result[1], '\v^foo bar (.*)$')
|
let g:match = matchlist(g:result[1], '\v^foo bar (.*)$')
|
||||||
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]
|
||||||
|
|
||||||
|
Execute(FormatCommand should apply filename mappings the current file):
|
||||||
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', '%s', 1, v:null, [
|
||||||
|
\ [expand('%:p:h'), '/foo/bar'],
|
||||||
|
\])
|
||||||
|
|
||||||
|
Assert g:result[1] =~# '/foo/bar'
|
||||||
|
|
||||||
|
Execute(FormatCommand should apply filename mappings to temporary files):
|
||||||
|
let g:result = ale#command#FormatCommand(bufnr('%'), '', '%t', 1, v:null, [
|
||||||
|
\ [fnamemodify(tempname(), ':h:h'), '/foo/bar']
|
||||||
|
\])
|
||||||
|
|
||||||
|
Assert g:result[1] =~# '/foo/bar'
|
||||||
|
|
|
@ -1,7 +1,50 @@
|
||||||
|
Before:
|
||||||
|
Save g:ale_filename_mappings
|
||||||
|
|
||||||
|
let g:ale_filename_mappings = {}
|
||||||
|
|
||||||
After:
|
After:
|
||||||
unlet! b:temp_name
|
unlet! b:temp_name
|
||||||
unlet! b:other_bufnr
|
unlet! b:other_bufnr
|
||||||
|
|
||||||
|
Restore
|
||||||
|
|
||||||
|
|
||||||
|
Execute(FixLocList should map filenames):
|
||||||
|
" Paths converted back into temporary filenames shouldn't be included.
|
||||||
|
let g:ale_filename_mappings = {
|
||||||
|
\ 'linter2': [['/xxx', '/data']],
|
||||||
|
\ 'linter1': [
|
||||||
|
\ ['/bar', '/data/special'],
|
||||||
|
\ ['/foo', '/data'],
|
||||||
|
\ [
|
||||||
|
\ ale#path#Simplify(fnamemodify(ale#util#Tempname(), ':h:h')),
|
||||||
|
\ '/x-tmp',
|
||||||
|
\ ],
|
||||||
|
\ ],
|
||||||
|
\}
|
||||||
|
|
||||||
|
AssertEqual
|
||||||
|
\ [
|
||||||
|
\ '/foo/file.txt',
|
||||||
|
\ v:null,
|
||||||
|
\ '/bar/file.txt',
|
||||||
|
\ ],
|
||||||
|
\ map(
|
||||||
|
\ ale#engine#FixLocList(
|
||||||
|
\ bufnr('%'),
|
||||||
|
\ 'linter1',
|
||||||
|
\ 0,
|
||||||
|
\ [
|
||||||
|
\ {'text': 'x', 'lnum': 1, 'filename': '/data/file.txt'},
|
||||||
|
\ {'text': 'x', 'lnum': 1, 'filename': '/x-tmp/file.txt'},
|
||||||
|
\ {'text': 'x', 'lnum': 1, 'filename': '/data/special/file.txt'},
|
||||||
|
\ ],
|
||||||
|
\ ),
|
||||||
|
\ 'get(v:val, ''filename'', v:null)',
|
||||||
|
\ )
|
||||||
|
|
||||||
|
|
||||||
Given foo (Some file with lines to count):
|
Given foo (Some file with lines to count):
|
||||||
foo12345678
|
foo12345678
|
||||||
bar12345678
|
bar12345678
|
||||||
|
@ -37,7 +80,7 @@ Execute(FixLocList should set all the default values correctly):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -58,7 +101,7 @@ Execute(FixLocList should use the values we supply):
|
||||||
\ 'nr': 42,
|
\ 'nr': 42,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -87,7 +130,7 @@ Execute(FixLocList should set items with lines beyond the end to the last line):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -108,7 +151,7 @@ Execute(FixLocList should move line 0 to line 1):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -130,7 +173,7 @@ Execute(FixLocList should convert line and column numbers correctly):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -164,7 +207,7 @@ Execute(FixLocList should pass on end_col values):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -202,7 +245,7 @@ Execute(FixLocList should pass on end_lnum values):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -227,7 +270,7 @@ Execute(FixLocList should allow subtypes to be set):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -289,7 +332,7 @@ Execute(FixLocList should accept filenames):
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -327,7 +370,7 @@ Execute(FixLocList should interpret temporary filenames as being the current buf
|
||||||
\ 'nr': -1,
|
\ 'nr': -1,
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr(''),
|
\ bufnr(''),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -352,7 +395,7 @@ Execute(The error code should be passed on):
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ 'code': 'some-code'
|
\ 'code': 'some-code'
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -385,7 +428,7 @@ Execute(FixLocList should mark problems as coming from other sources if requeste
|
||||||
\ 'linter_name': 'foobar',
|
\ 'linter_name': 'foobar',
|
||||||
\ 'from_other_source': 1,
|
\ 'from_other_source': 1,
|
||||||
\ },
|
\ },
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
@ -407,7 +450,7 @@ Execute(character positions should be converted to byte positions):
|
||||||
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 13, 'end_lnum': 1, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 13, 'end_lnum': 1, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
||||||
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 17, 'end_lnum': 2, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
\ {'lnum': 1, 'bufnr': bufnr(''), 'col': 7, 'end_col': 17, 'end_lnum': 2, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
||||||
\ {'lnum': 2, 'bufnr': bufnr(''), 'col': 17, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
\ {'lnum': 2, 'bufnr': bufnr(''), 'col': 17, 'linter_name': 'foobar', 'nr': -1, 'type': 'E', 'vcol': 0, 'text': 'a'},
|
||||||
\],
|
\ ],
|
||||||
\ ale#engine#FixLocList(
|
\ ale#engine#FixLocList(
|
||||||
\ bufnr('%'),
|
\ bufnr('%'),
|
||||||
\ 'foobar',
|
\ 'foobar',
|
||||||
|
|
Reference in a new issue