summaryrefslogtreecommitdiff
path: root/autoload
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2020-08-23 19:55:42 +0100
committerw0rp <devw0rp@gmail.com>2020-08-23 19:55:42 +0100
commitba3dd0d02735e7b23918200ae58410b0deed2f62 (patch)
tree83012a41e42d746629fbf07293a1b3e7b7ed0fe6 /autoload
parent2b785688ead505dcbc1007374d3dca9914aa247a (diff)
downloadale-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.vim20
-rw-r--r--autoload/ale/command.vim26
-rw-r--r--autoload/ale/engine.vim20
-rw-r--r--autoload/ale/filename_mapping.vim22
-rw-r--r--autoload/ale/fix.vim40
-rw-r--r--autoload/ale/fix/registry.vim4
-rw-r--r--autoload/ale/lsp_linter.vim9
-rw-r--r--autoload/ale/path.vim2
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.