summaryrefslogtreecommitdiff
path: root/autoload
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2021-03-01 20:11:10 +0000
committerw0rp <devw0rp@gmail.com>2021-03-01 20:11:10 +0000
commit9fe7b1fe6a23fb55e6d782293585d58193123f59 (patch)
tree0403deb70011aee7be08e586b10b5828cf69499e /autoload
parent48fab99a0ab793e1b9607795c21659f12bd6947f (diff)
downloadale-9fe7b1fe6a23fb55e6d782293585d58193123f59.zip
Close #2281 - Separate cwd commands from commands
Working directories are now set seperately from the commands so they can later be swapped out when running linters over projects is supported, and also better support filename mapping for running linters on other machines in future.
Diffstat (limited to 'autoload')
-rw-r--r--autoload/ale/ant.vim26
-rw-r--r--autoload/ale/assert.vim126
-rw-r--r--autoload/ale/c.vim11
-rw-r--r--autoload/ale/command.vim66
-rw-r--r--autoload/ale/engine.vim16
-rw-r--r--autoload/ale/fix.vim2
-rw-r--r--autoload/ale/fixers/autoimport.vim6
-rw-r--r--autoload/ale/fixers/black.vim16
-rw-r--r--autoload/ale/fixers/eslint.vim12
-rw-r--r--autoload/ale/fixers/isort.vim6
-rw-r--r--autoload/ale/fixers/prettier.vim20
-rw-r--r--autoload/ale/fixers/prettier_eslint.vim4
-rw-r--r--autoload/ale/fixers/stylelint.vim4
-rw-r--r--autoload/ale/fixers/yamlfix.vim6
-rw-r--r--autoload/ale/gradle.vim25
-rw-r--r--autoload/ale/handlers/cppcheck.vim5
-rw-r--r--autoload/ale/handlers/eslint.vim10
-rw-r--r--autoload/ale/handlers/shellcheck.vim11
-rw-r--r--autoload/ale/linter.vim21
-rw-r--r--autoload/ale/lsp_linter.vim1
-rw-r--r--autoload/ale/maven.vim22
-rw-r--r--autoload/ale/path.vim20
22 files changed, 297 insertions, 139 deletions
diff --git a/autoload/ale/ant.vim b/autoload/ale/ant.vim
index 7d02484e..b6d4217f 100644
--- a/autoload/ale/ant.vim
+++ b/autoload/ale/ant.vim
@@ -23,19 +23,23 @@ function! ale#ant#FindExecutable(buffer) abort
return ''
endfunction
-" Given a buffer number, build a command to print the classpath of the root
-" project. Returns an empty string if cannot build the command.
+" Given a buffer number, get a working directory and command to print the
+" classpath of the root project.
+"
+" Returns an empty string for the command if Ant is not detected.
function! ale#ant#BuildClasspathCommand(buffer) abort
let l:executable = ale#ant#FindExecutable(a:buffer)
- let l:project_root = ale#ant#FindProjectRoot(a:buffer)
-
- if !empty(l:executable) && !empty(l:project_root)
- return ale#path#CdString(l:project_root)
- \ . ale#Escape(l:executable)
- \ . ' classpath'
- \ . ' -S'
- \ . ' -q'
+
+ if !empty(l:executable)
+ let l:project_root = ale#ant#FindProjectRoot(a:buffer)
+
+ if !empty(l:project_root)
+ return [
+ \ l:project_root,
+ \ ale#Escape(l:executable) .' classpath -S -q'
+ \]
+ endif
endif
- return ''
+ return ['', '']
endfunction
diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim
index 934fcaa8..7db0a22e 100644
--- a/autoload/ale/assert.vim
+++ b/autoload/ale/assert.vim
@@ -52,6 +52,36 @@ function! s:ProcessDeferredCommands(initial_result) abort
return l:command
endfunction
+function! s:ProcessDeferredCwds(initial_command, initial_cwd) abort
+ let l:result = a:initial_command
+ let l:last_cwd = v:null
+ let l:command_index = 0
+ let l:cwd_list = []
+
+ while ale#command#IsDeferred(l:result)
+ call add(l:cwd_list, l:result.cwd)
+
+ if get(g:, 'ale_run_synchronously_emulate_commands')
+ " Don't run commands, but simulate the results.
+ let l:Callback = g:ale_run_synchronously_callbacks[0]
+ let l:output = get(s:command_output, l:command_index, [])
+ call l:Callback(0, l:output)
+ unlet g:ale_run_synchronously_callbacks
+
+ let l:command_index += 1
+ else
+ " Run the commands in the shell, synchronously.
+ call ale#test#FlushJobs()
+ endif
+
+ let l:result = l:result.value
+ endwhile
+
+ call add(l:cwd_list, a:initial_cwd is v:null ? l:last_cwd : a:initial_cwd)
+
+ return l:cwd_list
+endfunction
+
" Load the currently loaded linter for a test case, and check that the command
" matches the given string.
function! ale#assert#Linter(expected_executable, expected_command) abort
@@ -85,6 +115,38 @@ function! ale#assert#Linter(expected_executable, expected_command) abort
\ [l:executable, l:command]
endfunction
+function! ale#assert#LinterCwd(expected_cwd) abort
+ let l:buffer = bufnr('')
+ let l:linter = s:GetLinter()
+
+ let l:initial_cwd = ale#linter#GetCwd(l:buffer, l:linter)
+ call ale#command#SetCwd(l:buffer, l:initial_cwd)
+
+ let l:cwd = s:ProcessDeferredCwds(
+ \ ale#linter#GetCommand(l:buffer, l:linter),
+ \ l:initial_cwd,
+ \)
+
+ call ale#command#ResetCwd(l:buffer)
+
+ if type(a:expected_cwd) isnot v:t_list
+ let l:cwd = l:cwd[-1]
+ endif
+
+ AssertEqual a:expected_cwd, l:cwd
+endfunction
+
+function! ale#assert#FixerCwd(expected_cwd) abort
+ let l:buffer = bufnr('')
+ let l:cwd = s:ProcessDeferredCwds(s:FixerFunction(l:buffer), v:null)
+
+ if type(a:expected_cwd) isnot v:t_list
+ let l:cwd = l:cwd[-1]
+ endif
+
+ AssertEqual a:expected_cwd, l:cwd
+endfunction
+
function! ale#assert#Fixer(expected_result) abort
let l:buffer = bufnr('')
let l:result = s:ProcessDeferredCommands(s:FixerFunction(l:buffer))
@@ -153,6 +215,7 @@ endfunction
function! ale#assert#SetUpLinterTestCommands() abort
command! -nargs=+ GivenCommandOutput :call ale#assert#GivenCommandOutput(<args>)
+ command! -nargs=+ AssertLinterCwd :call ale#assert#LinterCwd(<args>)
command! -nargs=+ AssertLinter :call ale#assert#Linter(<args>)
command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted()
command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions(<args>)
@@ -164,10 +227,35 @@ endfunction
function! ale#assert#SetUpFixerTestCommands() abort
command! -nargs=+ GivenCommandOutput :call ale#assert#GivenCommandOutput(<args>)
+ command! -nargs=+ AssertFixerCwd :call ale#assert#FixerCwd(<args>)
command! -nargs=+ AssertFixer :call ale#assert#Fixer(<args>)
command! -nargs=0 AssertFixerNotExecuted :call ale#assert#FixerNotExecuted()
endfunction
+function! ale#assert#ResetVariables(filetype, name, ...) abort
+ " If the suffix of the option names format is different, an additional
+ " argument can be used for that instead.
+ if a:0 > 1
+ throw 'Too many arguments'
+ endif
+
+ let l:option_suffix = get(a:000, 0, a:name)
+ let l:prefix = 'ale_' . a:filetype . '_'
+ \ . substitute(l:option_suffix, '-', '_', 'g')
+ let l:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
+
+ " Save and clear linter variables.
+ " We'll load the runtime file to reset them to defaults.
+ for l:key in filter(keys(g:), l:filter_expr)
+ execute 'Save g:' . l:key
+ unlet g:[l:key]
+ endfor
+
+ for l:key in filter(keys(b:), l:filter_expr)
+ unlet b:[l:key]
+ endfor
+endfunction
+
" A dummy function for making sure this module is loaded.
function! ale#assert#SetUpLinterTest(filetype, name) abort
" Set up a marker so ALE doesn't create real random temporary filenames.
@@ -177,31 +265,18 @@ function! ale#assert#SetUpLinterTest(filetype, name) abort
call ale#linter#Reset()
call ale#linter#PreventLoading(a:filetype)
- let l:prefix = 'ale_' . a:filetype . '_' . a:name
- let b:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
-
Save g:ale_lsp_root
let g:ale_lsp_root = {}
Save b:ale_lsp_root
unlet! b:ale_lsp_root
+ call ale#assert#ResetVariables(a:filetype, a:name)
+
Save g:ale_c_build_dir
unlet! g:ale_c_build_dir
-
- " Save and clear linter variables.
- " We'll load the runtime file to reset them to defaults.
- for l:key in filter(keys(g:), b:filter_expr)
- execute 'Save g:' . l:key
- unlet g:[l:key]
- endfor
-
unlet! b:ale_c_build_dir
- for l:key in filter(keys(b:), b:filter_expr)
- unlet b:[l:key]
- endfor
-
execute 'runtime ale_linters/' . a:filetype . '/' . a:name . '.vim'
if !exists('g:dir')
@@ -226,6 +301,10 @@ function! ale#assert#TearDownLinterTest() abort
delcommand GivenCommandOutput
endif
+ if exists(':AssertLinterCwd')
+ delcommand AssertLinterCwd
+ endif
+
if exists(':AssertLinter')
delcommand AssertLinter
endif
@@ -281,18 +360,7 @@ function! ale#assert#SetUpFixerTest(filetype, name, ...) abort
let s:FixerFunction = function(l:function_name)
let l:option_suffix = get(a:000, 0, a:name)
- let l:prefix = 'ale_' . a:filetype . '_'
- \ . substitute(l:option_suffix, '-', '_', 'g')
- let b:filter_expr = 'v:val[: len(l:prefix) - 1] is# l:prefix'
-
- for l:key in filter(keys(g:), b:filter_expr)
- execute 'Save g:' . l:key
- unlet g:[l:key]
- endfor
-
- for l:key in filter(keys(b:), b:filter_expr)
- unlet b:[l:key]
- endfor
+ call ale#assert#ResetVariables(a:filetype, a:name, l:option_suffix)
execute 'runtime autoload/ale/fixers/' . substitute(a:name, '-', '_', 'g') . '.vim'
@@ -329,6 +397,10 @@ function! ale#assert#TearDownFixerTest() abort
delcommand GivenCommandOutput
endif
+ if exists(':AssertFixerCwd')
+ delcommand AssertFixerCwd
+ endif
+
if exists(':AssertFixer')
delcommand AssertFixer
endif
diff --git a/autoload/ale/c.vim b/autoload/ale/c.vim
index 14621aa9..ec9d4482 100644
--- a/autoload/ale/c.vim
+++ b/autoload/ale/c.vim
@@ -513,16 +513,18 @@ function! ale#c#GetMakeCommand(buffer) abort
if !empty(l:path)
let l:always_make = ale#Var(a:buffer, 'c_always_make')
- return ale#path#CdString(fnamemodify(l:path, ':h'))
- \ . 'make -n' . (l:always_make ? ' --always-make' : '')
+ return [
+ \ fnamemodify(l:path, ':h'),
+ \ 'make -n' . (l:always_make ? ' --always-make' : ''),
+ \]
endif
endif
- return ''
+ return ['', '']
endfunction
function! ale#c#RunMakeCommand(buffer, Callback) abort
- let l:command = ale#c#GetMakeCommand(a:buffer)
+ let [l:cwd, l:command] = ale#c#GetMakeCommand(a:buffer)
if empty(l:command)
return a:Callback(a:buffer, [])
@@ -532,6 +534,7 @@ function! ale#c#RunMakeCommand(buffer, Callback) abort
\ a:buffer,
\ l:command,
\ {b, output -> a:Callback(a:buffer, output)},
+ \ {'cwd': l:cwd},
\)
endfunction
diff --git a/autoload/ale/command.vim b/autoload/ale/command.vim
index 8f497169..c9dc8d94 100644
--- a/autoload/ale/command.vim
+++ b/autoload/ale/command.vim
@@ -7,6 +7,9 @@ if !exists('s:buffer_data')
let s:buffer_data = {}
endif
+" The regular expression used for formatting filenames with modifiers.
+let s:path_format_regex = '\v\%s(%(:h|:t|:r|:e)*)'
+
" Used to get the data in tests.
function! ale#command#GetData() abort
return deepcopy(s:buffer_data)
@@ -26,6 +29,19 @@ function! ale#command#InitData(buffer) abort
endif
endfunction
+" Set the cwd for commands that are about to run.
+" Used internally.
+function! ale#command#SetCwd(buffer, cwd) abort
+ call ale#command#InitData(a:buffer)
+ let s:buffer_data[a:buffer].cwd = a:cwd
+endfunction
+
+function! ale#command#ResetCwd(buffer) abort
+ if has_key(s:buffer_data, a:buffer)
+ let s:buffer_data[a:buffer].cwd = v:null
+ endif
+endfunction
+
function! ale#command#ManageFile(buffer, file) abort
call ale#command#InitData(a:buffer)
call add(s:buffer_data[a:buffer].file_list, a:file)
@@ -151,6 +167,24 @@ function! s:FormatFilename(filename, mappings, modifiers) abort
return ale#Escape(l:filename)
endfunction
+" Produce a command prefix to check to a particular directory for a command.
+" %s format markers with filename-modifiers can be used as the directory, and
+" will be returned verbatim for formatting in paths relative to files.
+function! ale#command#CdString(directory) abort
+ let l:match = matchstrpos(a:directory, s:path_format_regex)
+ " Do not escape the directory here if it's a valid format string.
+ " This allows us to use sequences like %s:h, %s:h:h, etc.
+ let l:directory = l:match[1:] == [0, len(a:directory)]
+ \ ? a:directory
+ \ : ale#Escape(a:directory)
+
+ if has('win32')
+ return 'cd /d ' . l:directory . ' && '
+ endif
+
+ return 'cd ' . l:directory . ' && '
+endfunction
+
" Given a command string, replace every...
" %s -> with the current filename
" %t -> with the name of an unused file in a temporary directory
@@ -161,11 +195,16 @@ function! ale#command#FormatCommand(
\ command,
\ pipe_file_if_needed,
\ input,
+\ cwd,
\ mappings,
\) abort
let l:temporary_file = ''
let l:command = a:command
+ if !empty(a:cwd)
+ let l:command = ale#command#CdString(a:cwd) . l:command
+ endif
+
" First replace all uses of %%, used for literal percent characters,
" with an ugly string.
let l:command = substitute(l:command, '%%', '<<PERCENTS>>', 'g')
@@ -181,7 +220,7 @@ function! ale#command#FormatCommand(
let l:filename = fnamemodify(bufname(a:buffer), ':p')
let l:command = substitute(
\ l:command,
- \ '\v\%s(%(:h|:t|:r|:e)*)',
+ \ s:path_format_regex,
\ '\=s:FormatFilename(l:filename, a:mappings, submatch(1))',
\ 'g'
\)
@@ -279,9 +318,16 @@ function! s:ExitCallback(buffer, line_list, Callback, data) abort
let l:result = a:data.result
let l:result.value = l:value
- if get(l:result, 'result_callback', v:null) isnot v:null
- call call(l:result.result_callback, [l:value])
- endif
+ " Set the default cwd for this buffer in this call stack.
+ call ale#command#SetCwd(a:buffer, l:result.cwd)
+
+ try
+ if get(l:result, 'result_callback', v:null) isnot v:null
+ call call(l:result.result_callback, [l:value])
+ endif
+ finally
+ call ale#command#ResetCwd(a:buffer)
+ endtry
endfunction
function! ale#command#Run(buffer, command, Callback, ...) abort
@@ -293,6 +339,13 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
let l:output_stream = get(l:options, 'output_stream', 'stdout')
let l:line_list = []
+ let l:cwd = get(l:options, 'cwd', v:null)
+
+ if l:cwd is v:null
+ " Default the working directory to whatever it was for the last
+ " command run in the chain.
+ let l:cwd = get(get(s:buffer_data, a:buffer, {}), 'cwd', v:null)
+ endif
let [l:temporary_file, l:command, l:file_created] = ale#command#FormatCommand(
\ a:buffer,
@@ -300,6 +353,7 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
\ a:command,
\ get(l:options, 'read_buffer', 0),
\ get(l:options, 'input', v:null),
+ \ l:cwd,
\ get(l:options, 'filename_mappings', []),
\)
let l:command = ale#job#PrepareCommand(a:buffer, l:command)
@@ -366,10 +420,14 @@ function! ale#command#Run(buffer, command, Callback, ...) abort
" The `_deferred_job_id` is used for both checking the type of object, and
" for checking the job ID and status.
"
+ " The cwd is kept and used as the default value for the next command in
+ " the chain.
+ "
" The original command here is used in tests.
let l:result = {
\ '_deferred_job_id': l:job_id,
\ 'executable': get(l:options, 'executable', ''),
+ \ 'cwd': l:cwd,
\ 'command': a:command,
\}
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 3cafa25c..5b9b1fca 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -413,6 +413,7 @@ function! s:RunJob(command, options) abort
return 0
endif
+ let l:cwd = a:options.cwd
let l:executable = a:options.executable
let l:buffer = a:options.buffer
let l:linter = a:options.linter
@@ -425,6 +426,7 @@ function! s:RunJob(command, options) abort
\ 'executable': l:executable,
\}])
let l:result = ale#command#Run(l:buffer, l:command, l:Callback, {
+ \ 'cwd': l:cwd,
\ 'output_stream': l:output_stream,
\ 'executable': l:executable,
\ 'read_buffer': l:read_buffer,
@@ -541,8 +543,22 @@ function! s:RunIfExecutable(buffer, linter, lint_file, executable) abort
let l:job_type = a:lint_file ? 'file_linter' : 'linter'
call setbufvar(a:buffer, 'ale_job_type', l:job_type)
+ " Get the cwd for the linter and set it before we call GetCommand.
+ " This will ensure that ale#command#Run uses it by default.
+ let l:cwd = ale#linter#GetCwd(a:buffer, a:linter)
+
+ if l:cwd isnot v:null
+ call ale#command#SetCwd(a:buffer, l:cwd)
+ endif
+
let l:command = ale#linter#GetCommand(a:buffer, a:linter)
+
+ if l:cwd isnot v:null
+ call ale#command#ResetCwd(a:buffer)
+ endif
+
let l:options = {
+ \ 'cwd': l:cwd,
\ 'executable': a:executable,
\ 'buffer': a:buffer,
\ 'linter': a:linter,
diff --git a/autoload/ale/fix.vim b/autoload/ale/fix.vim
index c3338fc5..8ebba9fe 100644
--- a/autoload/ale/fix.vim
+++ b/autoload/ale/fix.vim
@@ -172,6 +172,7 @@ function! s:RunJob(result, options) abort
let l:read_temporary_file = get(a:result, 'read_temporary_file', 0)
let l:read_buffer = get(a:result, 'read_buffer', 1)
let l:output_stream = get(a:result, 'output_stream', 'stdout')
+ let l:cwd = get(a:result, 'cwd', v:null)
if l:read_temporary_file
let l:output_stream = 'none'
@@ -190,6 +191,7 @@ function! s:RunJob(result, options) abort
\ 'read_buffer': l:read_buffer,
\ 'input': l:input,
\ 'log_output': 0,
+ \ 'cwd': l:cwd,
\ 'filename_mappings': ale#GetFilenameMappings(l:buffer, l:fixer_name),
\})
diff --git a/autoload/ale/fixers/autoimport.vim b/autoload/ale/fixers/autoimport.vim
index 37a52db8..700d36b5 100644
--- a/autoload/ale/fixers/autoimport.vim
+++ b/autoload/ale/fixers/autoimport.vim
@@ -19,7 +19,9 @@ function! ale#fixers#autoimport#Fix(buffer) abort
endif
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -',
+ \ 'cwd': '%s:h',
+ \ 'command': ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '')
+ \ . ' -',
\}
endfunction
diff --git a/autoload/ale/fixers/black.vim b/autoload/ale/fixers/black.vim
index fba6c3b4..17697652 100644
--- a/autoload/ale/fixers/black.vim
+++ b/autoload/ale/fixers/black.vim
@@ -17,25 +17,25 @@ function! ale#fixers#black#GetExecutable(buffer) abort
endfunction
function! ale#fixers#black#Fix(buffer) abort
- let l:cd_string = ale#Var(a:buffer, 'python_black_change_directory')
- \ ? ale#path#BufferCdString(a:buffer)
- \ : ''
-
let l:executable = ale#fixers#black#GetExecutable(a:buffer)
-
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run black'
\ : ''
-
let l:options = ale#Var(a:buffer, 'python_black_options')
if expand('#' . a:buffer . ':e') is? 'pyi'
let l:options .= '--pyi'
endif
- return {
- \ 'command': l:cd_string . ale#Escape(l:executable) . l:exec_args
+ let l:result = {
+ \ 'command': ale#Escape(l:executable) . l:exec_args
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' -',
\}
+
+ if ale#Var(a:buffer, 'python_black_change_directory')
+ let l:result.cwd = '%s:h'
+ endif
+
+ return l:result
endfunction
diff --git a/autoload/ale/fixers/eslint.vim b/autoload/ale/fixers/eslint.vim
index f725875c..c9535cb0 100644
--- a/autoload/ale/fixers/eslint.vim
+++ b/autoload/ale/fixers/eslint.vim
@@ -53,8 +53,8 @@ function! ale#fixers#eslint#ApplyFixForVersion(buffer, version) abort
" Use --fix-to-stdout with eslint_d
if l:executable =~# 'eslint_d$' && ale#semver#GTE(a:version, [3, 19, 0])
return {
- \ 'command': ale#handlers#eslint#GetCdString(a:buffer)
- \ . ale#node#Executable(a:buffer, l:executable)
+ \ 'cwd': ale#handlers#eslint#GetCwd(a:buffer),
+ \ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ale#Pad(l:options)
\ . ' --stdin-filename %s --stdin --fix-to-stdout',
\ 'process_with': 'ale#fixers#eslint#ProcessEslintDOutput',
@@ -64,8 +64,8 @@ function! ale#fixers#eslint#ApplyFixForVersion(buffer, version) abort
" 4.9.0 is the first version with --fix-dry-run
if ale#semver#GTE(a:version, [4, 9, 0])
return {
- \ 'command': ale#handlers#eslint#GetCdString(a:buffer)
- \ . ale#node#Executable(a:buffer, l:executable)
+ \ 'cwd': ale#handlers#eslint#GetCwd(a:buffer),
+ \ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ale#Pad(l:options)
\ . ' --stdin-filename %s --stdin --fix-dry-run --format=json',
\ 'process_with': 'ale#fixers#eslint#ProcessFixDryRunOutput',
@@ -73,8 +73,8 @@ function! ale#fixers#eslint#ApplyFixForVersion(buffer, version) abort
endif
return {
- \ 'command': ale#handlers#eslint#GetCdString(a:buffer)
- \ . ale#node#Executable(a:buffer, l:executable)
+ \ 'cwd': ale#handlers#eslint#GetCwd(a:buffer),
+ \ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ale#Pad(l:options)
\ . (!empty(l:config) ? ' -c ' . ale#Escape(l:config) : '')
\ . ' --fix %t',
diff --git a/autoload/ale/fixers/isort.vim b/autoload/ale/fixers/isort.vim
index 55bb550e..ba95bb10 100644
--- a/autoload/ale/fixers/isort.vim
+++ b/autoload/ale/fixers/isort.vim
@@ -17,9 +17,7 @@ endfunction
function! ale#fixers#isort#Fix(buffer) abort
let l:options = ale#Var(a:buffer, 'python_isort_options')
-
let l:executable = ale#fixers#isort#GetExecutable(a:buffer)
-
let l:exec_args = l:executable =~? 'pipenv$'
\ ? ' run isort'
\ : ''
@@ -29,8 +27,8 @@ function! ale#fixers#isort#Fix(buffer) abort
endif
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable) . l:exec_args
+ \ 'cwd': '%s:h',
+ \ 'command': ale#Escape(l:executable) . l:exec_args
\ . (!empty(l:options) ? ' ' . l:options : '') . ' -',
\}
endfunction
diff --git a/autoload/ale/fixers/prettier.vim b/autoload/ale/fixers/prettier.vim
index 277f84c4..43806e8e 100644
--- a/autoload/ale/fixers/prettier.vim
+++ b/autoload/ale/fixers/prettier.vim
@@ -34,19 +34,11 @@ function! ale#fixers#prettier#ProcessPrettierDOutput(buffer, output) abort
return a:output
endfunction
-function! ale#fixers#prettier#GetProjectRoot(buffer) abort
+function! ale#fixers#prettier#GetCwd(buffer) abort
let l:config = ale#path#FindNearestFile(a:buffer, '.prettierignore')
- if !empty(l:config)
- return fnamemodify(l:config, ':h')
- endif
-
" Fall back to the directory of the buffer
- return fnamemodify(bufname(a:buffer), ':p:h')
-endfunction
-
-function! ale#fixers#prettier#CdProjectRoot(buffer) abort
- return ale#path#CdString(ale#fixers#prettier#GetProjectRoot(a:buffer))
+ return !empty(l:config) ? fnamemodify(l:config, ':h') : '%s:h'
endfunction
function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
@@ -103,8 +95,8 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
" Special error handling needed for prettier_d
if l:executable =~# 'prettier_d$'
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable)
+ \ 'cwd': '%s:h',
+ \ 'command':ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --stdin-filepath %s --stdin',
\ 'process_with': 'ale#fixers#prettier#ProcessPrettierDOutput',
@@ -114,8 +106,8 @@ function! ale#fixers#prettier#ApplyFixForVersion(buffer, version) abort
" 1.4.0 is the first version with --stdin-filepath
if ale#semver#GTE(a:version, [1, 4, 0])
return {
- \ 'command': ale#fixers#prettier#CdProjectRoot(a:buffer)
- \ . ale#Escape(l:executable)
+ \ 'cwd': ale#fixers#prettier#GetCwd(a:buffer),
+ \ 'command': ale#Escape(l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --stdin-filepath %s --stdin',
\}
diff --git a/autoload/ale/fixers/prettier_eslint.vim b/autoload/ale/fixers/prettier_eslint.vim
index 1e66f49e..09fadd71 100644
--- a/autoload/ale/fixers/prettier_eslint.vim
+++ b/autoload/ale/fixers/prettier_eslint.vim
@@ -37,8 +37,8 @@ function! ale#fixers#prettier_eslint#ApplyFixForVersion(buffer, version) abort
" 4.4.0 is the first version with --stdin-filepath
if ale#semver#GTE(a:version, [4, 4, 0])
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable)
+ \ 'cwd': '%s:h',
+ \ 'command': ale#Escape(l:executable)
\ . l:eslint_config_option
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' --stdin-filepath %s --stdin',
diff --git a/autoload/ale/fixers/stylelint.vim b/autoload/ale/fixers/stylelint.vim
index 6f4cf177..0caab802 100644
--- a/autoload/ale/fixers/stylelint.vim
+++ b/autoload/ale/fixers/stylelint.vim
@@ -17,8 +17,8 @@ function! ale#fixers#stylelint#Fix(buffer) abort
let l:options = ale#Var(a:buffer, 'stylelint_options')
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#node#Executable(a:buffer, l:executable)
+ \ 'cwd': '%s:h',
+ \ 'command': ale#node#Executable(a:buffer, l:executable)
\ . ' %t'
\ . ale#Pad(l:options)
\ . ' --fix',
diff --git a/autoload/ale/fixers/yamlfix.vim b/autoload/ale/fixers/yamlfix.vim
index 966556c9..6654a25c 100644
--- a/autoload/ale/fixers/yamlfix.vim
+++ b/autoload/ale/fixers/yamlfix.vim
@@ -7,7 +7,6 @@ call ale#Set('yaml_yamlfix_use_global', get(g:, 'ale_use_global_executables', 0)
function! ale#fixers#yamlfix#Fix(buffer) abort
let l:options = ale#Var(a:buffer, 'yaml_yamlfix_options')
-
let l:executable = ale#python#FindExecutable(
\ a:buffer,
\ 'yaml_yamlfix',
@@ -19,7 +18,8 @@ function! ale#fixers#yamlfix#Fix(buffer) abort
endif
return {
- \ 'command': ale#path#BufferCdString(a:buffer)
- \ . ale#Escape(l:executable) . (!empty(l:options) ? ' ' . l:options : '') . ' -',
+ \ 'cwd': '%s:h',
+ \ 'command': ale#Escape(l:executable)
+ \ . (!empty(l:options) ? ' ' . l:options : '') . ' -',
\}
endfunction
diff --git a/autoload/ale/gradle.vim b/autoload/ale/gradle.vim
index dc377fb9..ba1add4d 100644
--- a/autoload/ale/gradle.vim
+++ b/autoload/ale/gradle.vim
@@ -50,18 +50,25 @@ function! ale#gradle#FindExecutable(buffer) abort
return ''
endfunction
-" Given a buffer number, build a command to print the classpath of the root
-" project. Returns an empty string if cannot build the command.
+" Given a buffer number, get a working directory and command to print the
+" classpath of the root project.
+"
+" Returns an empty string for the command if Gradle is not detected.
function! ale#gradle#BuildClasspathCommand(buffer) abort
let l:executable = ale#gradle#FindExecutable(a:buffer)
- let l:project_root = ale#gradle#FindProjectRoot(a:buffer)
- if !empty(l:executable) && !empty(l:project_root)
- return ale#path#CdString(l:project_root)
- \ . ale#Escape(l:executable)
- \ . ' -I ' . ale#Escape(s:init_path)
- \ . ' -q printClasspath'
+ if !empty(l:executable)
+ let l:project_root = ale#gradle#FindProjectRoot(a:buffer)
+
+ if !empty(l:project_root)
+ return [
+ \ l:project_root,
+ \ ale#Escape(l:executable)
+ \ . ' -I ' . ale#Escape(s:init_path)
+ \ . ' -q printClasspath'
+ \]
+ endif
endif
- return ''
+ return ['', '']
endfunction
diff --git a/autoload/ale/handlers/cppcheck.vim b/autoload/ale/handlers/cppcheck.vim
index 7f68ba67..b70ae1bf 100644
--- a/autoload/ale/handlers/cppcheck.vim
+++ b/autoload/ale/handlers/cppcheck.vim
@@ -1,10 +1,9 @@
" Description: Handle errors for cppcheck.
-function! ale#handlers#cppcheck#GetCdCommand(buffer) abort
+function! ale#handlers#cppcheck#GetCwd(buffer) abort
let [l:dir, l:json_path] = ale#c#FindCompileCommands(a:buffer)
- let l:cd_command = !empty(l:dir) ? ale#path#CdString(l:dir) : ''
- return l:cd_command
+ return !empty(l:dir) ? l:dir : ''
endfunction
function! ale#handlers#cppcheck#GetBufferPathIncludeOptions(buffer) abort
diff --git a/autoload/ale/handlers/eslint.vim b/autoload/ale/handlers/eslint.vim
index b8610612..e953d40f 100644
--- a/autoload/ale/handlers/eslint.vim
+++ b/autoload/ale/handlers/eslint.vim
@@ -39,9 +39,8 @@ function! ale#handlers#eslint#GetExecutable(buffer) abort
return ale#node#FindExecutable(a:buffer, 'javascript_eslint', s:executables)
endfunction
-" Given a buffer, return a command prefix string which changes directory
-" as necessary for running ESLint.
-function! ale#handlers#eslint#GetCdString(buffer) abort
+" Given a buffer, return an appropriate working directory for ESLint.
+function! ale#handlers#eslint#GetCwd(buffer) abort
" ESLint 6 loads plugins/configs/parsers from the project root
" By default, the project root is simply the CWD of the running process.
" https://github.com/eslint/rfcs/blob/master/designs/2018-simplified-package-loading/README.md
@@ -60,7 +59,7 @@ function! ale#handlers#eslint#GetCdString(buffer) abort
let l:project_dir = !empty(l:modules_dir) ? fnamemodify(l:modules_dir, ':h:h') : ''
endif
- return !empty(l:project_dir) ? ale#path#CdString(l:project_dir) : ''
+ return !empty(l:project_dir) ? l:project_dir : ''
endfunction
function! ale#handlers#eslint#GetCommand(buffer) abort
@@ -68,8 +67,7 @@ function! ale#handlers#eslint#GetCommand(buffer) abort
let l:options = ale#Var(a:buffer, 'javascript_eslint_options')
- return ale#handlers#eslint#GetCdString(a:buffer)
- \ . ale#node#Executable(a:buffer, l:executable)
+ return ale#node#Executable(a:buffer, l:executable)
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . ' -f json --stdin --stdin-filename %s'
endfunction
diff --git a/autoload/ale/handlers/shellcheck.vim b/autoload/ale/handlers/shellcheck.vim
index 701c43b2..17de2912 100644
--- a/autoload/ale/handlers/shellcheck.vim
+++ b/autoload/ale/handlers/shellcheck.vim
@@ -40,21 +40,21 @@ function! ale#handlers#shellcheck#GetDialectArgument(buffer) abort
return ''
endfunction
+function! ale#handlers#shellcheck#GetCwd(buffer) abort
+ return ale#Var(a:buffer, 'sh_shellcheck_change_directory') ? '%s:h' : ''
+endfunction
+
function! ale#handlers#shellcheck#GetCommand(buffer, version) abort
let l:options = ale#Var(a:buffer, 'sh_shellcheck_options')
let l:exclude_option = ale#Var(a:buffer, 'sh_shellcheck_exclusions')
let l:dialect = ale#Var(a:buffer, 'sh_shellcheck_dialect')
let l:external_option = ale#semver#GTE(a:version, [0, 4, 0]) ? ' -x' : ''
- let l:cd_string = ale#Var(a:buffer, 'sh_shellcheck_change_directory')
- \ ? ale#path#BufferCdString(a:buffer)
- \ : ''
if l:dialect is# 'auto'
let l:dialect = ale#handlers#shellcheck#GetDialectArgument(a:buffer)
endif
- return l:cd_string
- \ . '%e'
+ return '%e'
\ . (!empty(l:dialect) ? ' -s ' . l:dialect : '')
\ . (!empty(l:options) ? ' ' . l:options : '')
\ . (!empty(l:exclude_option) ? ' -e ' . l:exclude_option : '')
@@ -111,6 +111,7 @@ function! ale#handlers#shellcheck#DefineLinter(filetype) abort
call ale#linter#Define(a:filetype, {
\ 'name': 'shellcheck',
\ 'executable': {buffer -> ale#Var(buffer, 'sh_shellcheck_executable')},
+ \ 'cwd': function('ale#handlers#shellcheck#GetCwd'),
\ 'command': {buffer -> ale#semver#RunWithVersionCheck(
\ buffer,
\ ale#Var(buffer, 'sh_shellcheck_executable'),
diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim
index f9ec48d7..d7b6a1ac 100644
--- a/autoload/ale/linter.vim
+++ b/autoload/ale/linter.vim
@@ -151,17 +151,30 @@ function! ale#linter#PreProcess(filetype, linter) abort
endif
let l:obj.address = a:linter.address
+
+ if has_key(a:linter, 'cwd')
+ throw '`cwd` makes no sense for socket LSP connections'
+ endif
else
throw '`address` must be defined for getting the LSP address'
endif
+ if has_key(a:linter, 'cwd')
+ let l:obj.cwd = a:linter.cwd
+
+ if type(l:obj.cwd) isnot v:t_string
+ \&& type(l:obj.cwd) isnot v:t_func
+ throw '`cwd` must be a String or Function if defined'
+ endif
+ endif
+
if l:needs_lsp_details
" Default to using the filetype as the language.
let l:obj.language = get(a:linter, 'language', a:filetype)
if type(l:obj.language) isnot v:t_string
\&& type(l:obj.language) isnot v:t_func
- throw '`language` must be a String or Funcref if defined'
+ throw '`language` must be a String or Function if defined'
endif
if has_key(a:linter, 'project_root')
@@ -415,6 +428,12 @@ function! ale#linter#GetExecutable(buffer, linter) abort
\ : l:Executable
endfunction
+function! ale#linter#GetCwd(buffer, linter) abort
+ let l:Cwd = get(a:linter, 'cwd', v:null)
+
+ return type(l:Cwd) is v:t_func ? l:Cwd(a:buffer) : l:Cwd
+endfunction
+
" Given a buffer and linter, get the command String for the linter.
function! ale#linter#GetCommand(buffer, linter) abort
let l:Command = a:linter.command
diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim
index 628dde78..2a3902b6 100644
--- a/autoload/ale/lsp_linter.vim
+++ b/autoload/ale/lsp_linter.vim
@@ -290,6 +290,7 @@ function! s:StartLSP(options, address, executable, command) abort
\ a:command,
\ 0,
\ v:false,
+ \ v:null,
\ [],
\)[1]
let l:command = ale#job#PrepareCommand(l:buffer, l:command)
diff --git a/autoload/ale/maven.vim b/autoload/ale/maven.vim
index 42735286..4f87ebb7 100644
--- a/autoload/ale/maven.vim
+++ b/autoload/ale/maven.vim
@@ -17,7 +17,6 @@ function! ale#maven#FindProjectRoot(buffer) abort
return ''
endfunction
-
" Given a buffer number, find the path to the executable.
" First search on the path for 'mvnw' (mvnw.cmd on Windows), if nothing is found,
" try the global command. Returns an empty string if cannot find the executable.
@@ -36,16 +35,23 @@ function! ale#maven#FindExecutable(buffer) abort
return ''
endfunction
-" Given a buffer number, build a command to print the classpath of the root
-" project. Returns an empty string if cannot build the command.
+" Given a buffer number, get a working directory and command to print the
+" classpath of the root project.
+"
+" Returns an empty string for the command if Maven is not detected.
function! ale#maven#BuildClasspathCommand(buffer) abort
let l:executable = ale#maven#FindExecutable(a:buffer)
- let l:project_root = ale#maven#FindProjectRoot(a:buffer)
- if !empty(l:executable) && !empty(l:project_root)
- return ale#path#CdString(l:project_root)
- \ . l:executable . ' dependency:build-classpath'
+ if !empty(l:executable)
+ let l:project_root = ale#maven#FindProjectRoot(a:buffer)
+
+ if !empty(l:project_root)
+ return [
+ \ l:project_root,
+ \ ale#Escape(l:executable) . ' dependency:build-classpath'
+ \]
+ endif
endif
- return ''
+ return ['', '']
endfunction
diff --git a/autoload/ale/path.vim b/autoload/ale/path.vim
index fed95ccd..94de252d 100644
--- a/autoload/ale/path.vim
+++ b/autoload/ale/path.vim
@@ -77,26 +77,6 @@ function! ale#path#ResolveLocalPath(buffer, search_string, global_fallback) abor
return l:path
endfunction
-" Output 'cd <directory> && '
-" This function can be used changing the directory for a linter command.
-function! ale#path#CdString(directory) abort
- if has('win32')
- return 'cd /d ' . ale#Escape(a:directory) . ' && '
- endif
-
- return 'cd ' . ale#Escape(a:directory) . ' && '
-endfunction
-
-" Output 'cd <buffer_filename_directory> && '
-" This function can be used changing the directory for a linter command.
-function! ale#path#BufferCdString(buffer) abort
- if has('win32')
- return 'cd /d %s:h && '
- endif
-
- return 'cd %s:h && '
-endfunction
-
" Return 1 if a path is an absolute path.
function! ale#path#IsAbsolute(filename) abort
if has('win32') && a:filename[:0] is# '\'