diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index dba59893..74d5c14a 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -242,10 +242,21 @@ function! ale#linter#PreProcess(filetype, linter) abort endif endif - let l:obj.project_root_callback = get(a:linter, 'project_root_callback') + if has_key(a:linter, 'project_root') + let l:obj.project_root = a:linter.project_root - if !s:IsCallback(l:obj.project_root_callback) - throw '`project_root_callback` must be a callback for LSP linters' + if type(l:obj.project_root) isnot v:t_string + \&& type(l:obj.project_root) isnot v:t_func + throw '`project_root` must be a String or Function if defined' + endif + elseif has_key(a:linter, 'project_root_callback') + let l:obj.project_root_callback = a:linter.project_root_callback + + if !s:IsCallback(l:obj.project_root_callback) + throw '`project_root_callback` must be a callback if defined' + endif + else + throw '`project_root` or `project_root_callback` must be defined for LSP linters' endif if has_key(a:linter, 'completion_filter') diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index b92f5104..d0f66bf7 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -194,6 +194,12 @@ function! ale#lsp_linter#FindProjectRoot(buffer, linter) abort endif " Fall back to the linter-specific configuration + if has_key(a:linter, 'project_root') + let l:Root = a:linter.project_root + + return type(l:Root) is v:t_func ? l:Root(a:buffer) : l:Root + endif + return ale#util#GetFunction(a:linter.project_root_callback)(a:buffer) endfunction diff --git a/doc/ale.txt b/doc/ale.txt index ca895fe0..acf86fb1 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -3270,16 +3270,21 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* This argument must only be set if the `lsp` argument is set to `'socket'`. - `project_root_callback` A |String| or |Funcref| for a callback function - accepting a buffer number. A |String| should be - returned representing the path to the project for the - file being checked with the language server. If an - empty string is returned, the file will not be + `project_root` A |String| representing a path to the project for + the file being checked with the language server, or + a |Funcref| accepting a buffer number and returning + the |String|. + + If an empty string is returned, the file will not be checked at all. This argument must only be set if the `lsp` argument is also set to a non-empty string. + `project_root_callback` A |String| or |Funcref| for a callback function + accepting a buffer number and returning the + `project_root` |String| as documented above. + `language` A |String| representing the name of the language being checked, or a |Funcref| accepting a buffer number and returning the |String|. This string will diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader index a78b9838..cd32ebc8 100644 --- a/test/test_linter_defintion_processing.vader +++ b/test/test_linter_defintion_processing.vader @@ -1,4 +1,10 @@ Before: + Save g:ale_lsp_root + Save b:ale_lsp_root + + let g:ale_lsp_root = {} + unlet! b:ale_lsp_root + let g:linter = {} After: @@ -558,6 +564,68 @@ Execute(PreProcess should complain about invalid address values): \}) AssertEqual '`address` must be a String or Function if defined', g:vader_exception +Execute(PreProcess should accept allow the project root be set as a String): + let g:linter = ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root': '/foo/bar', + \}) + + AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter) + +Execute(PreProcess should accept allow the project root be set as a Function): + let g:linter = ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root': {-> '/foo/bar'}, + \}) + + AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter) + +Execute(PreProcess should complain when the project_root valid is invalid): + AssertThrows call ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root': 0, + \}) + AssertEqual '`project_root` must be a String or Function if defined', g:vader_exception + +Execute(PreProcess should accept project_root_callback as a String): + call ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root_callback': 'Foobar', + \}) + +Execute(PreProcess should accept project_root_callback as a Function): + let g:linter = ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root_callback': {-> '/foo/bar'}, + \}) + + AssertEqual '/foo/bar', ale#lsp_linter#FindProjectRoot(0, g:linter) + +Execute(PreProcess should complain when the project_root_callback valid is invalid): + AssertThrows call ale#linter#PreProcess('testft', { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address': 'foo:123', + \ 'language': 'x', + \ 'project_root_callback': 0, + \}) + AssertEqual '`project_root_callback` must be a callback if defined', g:vader_exception + Execute(PreProcess should complain about using initialization_options and initialization_options_callback together): let g:linter = { \ 'name': 'x',