diff options
author | w0rp <devw0rp@gmail.com> | 2020-08-23 19:55:42 +0100 |
---|---|---|
committer | w0rp <devw0rp@gmail.com> | 2020-08-23 19:55:42 +0100 |
commit | ba3dd0d02735e7b23918200ae58410b0deed2f62 (patch) | |
tree | 83012a41e42d746629fbf07293a1b3e7b7ed0fe6 /autoload | |
parent | 2b785688ead505dcbc1007374d3dca9914aa247a (diff) | |
download | ale-ba3dd0d02735e7b23918200ae58410b0deed2f62.zip |
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.
Diffstat (limited to 'autoload')
-rw-r--r-- | autoload/ale.vim | 20 | ||||
-rw-r--r-- | autoload/ale/command.vim | 26 | ||||
-rw-r--r-- | autoload/ale/engine.vim | 20 | ||||
-rw-r--r-- | autoload/ale/filename_mapping.vim | 22 | ||||
-rw-r--r-- | autoload/ale/fix.vim | 40 | ||||
-rw-r--r-- | autoload/ale/fix/registry.vim | 4 | ||||
-rw-r--r-- | autoload/ale/lsp_linter.vim | 9 | ||||
-rw-r--r-- | autoload/ale/path.vim | 2 |
8 files changed, 114 insertions, 29 deletions
diff --git a/autoload/ale.vim b/autoload/ale.vim index 6251b47b..b75c9fc9 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -266,3 +266,23 @@ function! ale#GetLocItemMessage(item, format_string) abort return l:msg 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 diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim index 1bbc4f4c..ec264a36 100644 --- a/autoload/ale/command.vim +++ b/autoload/ale/command.vim @@ -133,11 +133,30 @@ function! ale#command#EscapeCommandPart(command_part) abort return substitute(a:command_part, '%', '%%', 'g') 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... " %s -> with the current filename " %t -> with the name of an unused file in a temporary directory " %% -> 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:command = a:command @@ -154,14 +173,14 @@ function! ale#command#FormatCommand(buffer, executable, command, pipe_file_if_ne " file. if l:command =~# '%s' 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 if a:input isnot v:false && 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 = 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 " Finish formatting so %% becomes %. @@ -265,6 +284,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort \ a:command, \ get(l:options, 'read_buffer', 0), \ get(l:options, 'input', v:null), + \ get(l:options, 'filename_mappings', []), \) let l:command = ale#job#PrepareCommand(a:buffer, l:command) let l:job_options = { diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index cfc1e5d7..3f4f9338 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -256,6 +256,13 @@ function! s:RemapItemTypes(type_map, loclist) abort endfunction 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: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 endif - if has_key(l:old_item, 'filename') - \&& !ale#path#IsTempName(l:old_item.filename) + let l:old_name = get(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. " Temporary files are assumed to be for this buffer, " and the filename is not included then, because it looks bad " in the loclist window. - let l:filename = l:old_item.filename + let l:filename = l:old_name let l:item.filename = l:filename if has_key(l:old_item, 'bufnr') @@ -415,6 +428,7 @@ function! s:RunJob(command, options) abort \ 'executable': l:executable, \ 'read_buffer': l:read_buffer, \ 'log_output': 1, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:linter.name), \}) " Only proceed if the job is being run. diff --git a/autoload/ale/filename_mapping.vim b/autoload/ale/filename_mapping.vim new file mode 100644 index 00000000..76d47acc --- /dev/null +++ b/autoload/ale/filename_mapping.vim @@ -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 diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim index b2a39444..a53f8626 100644 --- a/autoload/ale/fix.vim +++ b/autoload/ale/fix.vim @@ -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('filename_mappings', {}) " Apply fixes queued up for buffers which may be hidden. " 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({ \ 'buffer': a:buffer, \ 'input': l:input, - \ 'output': l:output, \ 'callback_list': a:job_info.callback_list, \ '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: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 type(a:result) is v:t_list @@ -150,7 +154,6 @@ function! s:RunJob(result, options) abort \ 'input': l:input, \ 'callback_index': a:options.callback_index, \ 'callback_list': a:options.callback_list, - \ 'output': [], \}) return @@ -177,6 +180,7 @@ function! s:RunJob(result, options) abort \ 'read_buffer': l:read_buffer, \ 'input': l:input, \ 'log_output': 0, + \ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name), \}) if empty(l:run_result) @@ -200,32 +204,22 @@ function! s:RunFixer(options) abort return endif - let l:ChainCallback = get(a:options, 'chain_callback', v:null) - - let l:Function = l:ChainCallback isnot v:null - \ ? ale#util#GetFunction(l:ChainCallback) - \ : a:options.callback_list[l:index] + let [l:fixer_name, l:Function] = a:options.callback_list[l:index] " Record new jobs started as fixer jobs. call setbufvar(l:buffer, 'ale_job_type', 'fixer') - if l:ChainCallback isnot v:null - " Chained commands accept (buffer, output, [input]) - let l:result = ale#util#FunctionArgCount(l:Function) == 2 - \ ? call(l:Function, [l:buffer, a:options.output]) - \ : 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 + " 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)]) call s:RunJob(l:result, { \ 'buffer': l:buffer, \ 'input': l:input, \ 'callback_list': a:options.callback_list, \ 'callback_index': l:index, + \ 'fixer_name': l:fixer_name, \}) endfunction @@ -293,16 +287,24 @@ function! s:GetCallbacks(buffer, fixing_flag, fixers) abort " Variables with capital characters are needed, or Vim will complain about " funcref variables. 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 let l:Func = ale#fix#registry#GetFunc(l:Item) if !empty(l:Func) + let l:fixer_name = l:Item let l:Item = l:Func endif endif 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/ " Rethrow exceptions for failing to get a function so we can print " a friendly message about it. diff --git a/autoload/ale/fix/registry.vim b/autoload/ale/fix/registry.vim index 94476ca9..2a38945c 100644 --- a/autoload/ale/fix/registry.vim +++ b/autoload/ale/fix/registry.vim @@ -160,11 +160,11 @@ let s:default_registry = { \ 'suggested_filetypes': ['php'], \ 'description': 'Fix PHP files with php-cs-fixer.', \ }, -\ 'astyle': { +\ 'astyle': { \ 'function': 'ale#fixers#astyle#Fix', \ 'suggested_filetypes': ['c', 'cpp'], \ 'description': 'Fix C/C++ with astyle.', -\ }, +\ }, \ 'clangtidy': { \ 'function': 'ale#fixers#clangtidy#Fix', \ 'suggested_filetypes': ['c', 'cpp', 'objc'], diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index f4a42bf4..db640654 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -265,7 +265,14 @@ function! s:StartLSP(options, address, executable, command) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) 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:ready = ale#lsp#StartProgram(l:conn_id, a:executable, l:command) endif diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim index 30550503..f18a9733 100644 --- a/autoload/ale/path.vim +++ b/autoload/ale/path.vim @@ -95,7 +95,7 @@ function! ale#path#IsAbsolute(filename) abort return a:filename[:0] is# '/' || a:filename[1:2] is# ':\' 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 " created by Vim. |