Merge pull request #2241 from bk2204/lsp-detect-hook
Add a hook to detect LSP project root
This commit is contained in:
commit
452460b8cd
@ -109,6 +109,14 @@ function! ale#assert#LSPProject(expected_root) abort
|
||||
AssertEqual a:expected_root, l:root
|
||||
endfunction
|
||||
|
||||
function! ale#assert#LSPProjectFull(expected_root) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:linter = s:GetLinter()
|
||||
let l:root = ale#lsp_linter#FindProjectRoot(l:buffer, l:linter)
|
||||
|
||||
AssertEqual a:expected_root, l:root
|
||||
endfunction
|
||||
|
||||
function! ale#assert#LSPAddress(expected_address) abort
|
||||
let l:buffer = bufnr('')
|
||||
let l:linter = s:GetLinter()
|
||||
@ -158,6 +166,7 @@ function! ale#assert#SetUpLinterTest(filetype, name) abort
|
||||
command! -nargs=+ AssertLSPConfig :call ale#assert#LSPConfig(<args>)
|
||||
command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage(<args>)
|
||||
command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject(<args>)
|
||||
command! -nargs=+ AssertLSPProjectFull :call ale#assert#LSPProjectFull(<args>)
|
||||
command! -nargs=+ AssertLSPAddress :call ale#assert#LSPAddress(<args>)
|
||||
endfunction
|
||||
|
||||
@ -193,6 +202,10 @@ function! ale#assert#TearDownLinterTest() abort
|
||||
delcommand AssertLSPProject
|
||||
endif
|
||||
|
||||
if exists(':AssertLSPProjectFull')
|
||||
delcommand AssertLSPProjectFull
|
||||
endif
|
||||
|
||||
if exists(':AssertLSPAddress')
|
||||
delcommand AssertLSPAddress
|
||||
endif
|
||||
|
@ -31,6 +31,7 @@ let s:global_variable_list = [
|
||||
\ 'ale_list_vertical',
|
||||
\ 'ale_list_window_size',
|
||||
\ 'ale_loclist_msg_format',
|
||||
\ 'ale_lsp_root',
|
||||
\ 'ale_max_buffer_history_size',
|
||||
\ 'ale_max_signs',
|
||||
\ 'ale_maximum_file_size',
|
||||
|
@ -152,12 +152,45 @@ function! ale#lsp_linter#GetConfig(buffer, linter) abort
|
||||
return l:config
|
||||
endfunction
|
||||
|
||||
function! ale#lsp_linter#FindProjectRoot(buffer, linter) abort
|
||||
let l:buffer_ale_root = getbufvar(a:buffer, 'ale_lsp_root', {})
|
||||
|
||||
if type(l:buffer_ale_root) is v:t_string
|
||||
return l:buffer_ale_root
|
||||
endif
|
||||
|
||||
" Try to get a buffer-local setting for the root
|
||||
if has_key(l:buffer_ale_root, a:linter.name)
|
||||
let l:Root = l:buffer_ale_root[a:linter.name]
|
||||
|
||||
if type(l:Root) is v:t_func
|
||||
return l:Root(a:buffer)
|
||||
else
|
||||
return l:Root
|
||||
endif
|
||||
endif
|
||||
|
||||
" Try to get a global setting for the root
|
||||
if has_key(g:ale_lsp_root, a:linter.name)
|
||||
let l:Root = g:ale_lsp_root[a:linter.name]
|
||||
|
||||
if type(l:Root) is v:t_func
|
||||
return l:Root(a:buffer)
|
||||
else
|
||||
return l:Root
|
||||
endif
|
||||
endif
|
||||
|
||||
" Fall back to the linter-specific configuration
|
||||
return ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
|
||||
endfunction
|
||||
|
||||
" Given a buffer, an LSP linter, start up an LSP linter and get ready to
|
||||
" receive messages for the document.
|
||||
function! ale#lsp_linter#StartLSP(buffer, linter) abort
|
||||
let l:command = ''
|
||||
let l:address = ''
|
||||
let l:root = ale#util#GetFunction(a:linter.project_root_callback)(a:buffer)
|
||||
let l:root = ale#lsp_linter#FindProjectRoot(a:buffer, a:linter)
|
||||
|
||||
if empty(l:root) && a:linter.lsp isnot# 'tsserver'
|
||||
" If there's no project root, then we can't check files with LSP,
|
||||
|
16
doc/ale.txt
16
doc/ale.txt
@ -1572,6 +1572,22 @@ b:ale_loclist_msg_format *b:ale_loclist_msg_format*
|
||||
|
||||
The strings for configuring `%severity%` are also used for this option.
|
||||
|
||||
g:ale_lsp_root *g:ale_lsp_root*
|
||||
b:ale_lsp_root *b:ale_lsp_root*
|
||||
|
||||
Type: |Dictionary| or |String|
|
||||
Default: {}
|
||||
|
||||
This option is used to determine the project root for the LSP linter. If the
|
||||
value is a |Dictionary|, it maps a linter to either a string containing the
|
||||
project root or a |Funcref| to call to look up the root. The funcref is
|
||||
provided the buffer number as its argument.
|
||||
|
||||
The buffer-specific variable may additionally be a string containing the
|
||||
project root itself.
|
||||
|
||||
If neither variable yields a result, a linter-specific function is invoked to
|
||||
detect a project root. If this, too, yields no result, the linter is disabled.
|
||||
|
||||
g:ale_max_buffer_history_size *g:ale_max_buffer_history_size*
|
||||
|
||||
|
@ -87,6 +87,9 @@ let g:ale_lint_on_save = get(g:, 'ale_lint_on_save', 1)
|
||||
" This flag can be set to 1 to enable linting when the filetype is changed.
|
||||
let g:ale_lint_on_filetype_changed = get(g:, 'ale_lint_on_filetype_changed', 1)
|
||||
|
||||
" This Dictionary configures the default LSP roots for various linters.
|
||||
let g:ale_lsp_root = get(g:, 'ale_lsp_root', {})
|
||||
|
||||
" This flag can be set to 1 to enable automatically fixing files on save.
|
||||
let g:ale_fix_on_save = get(g:, 'ale_fix_on_save', 0)
|
||||
|
||||
|
@ -17,6 +17,7 @@ Execute(Command formatting should be applied correctly for LSP linters):
|
||||
call ale#lsp_linter#StartLSP(
|
||||
\ bufnr(''),
|
||||
\ {
|
||||
\ 'name': 'linter',
|
||||
\ 'language_callback': {-> 'x'},
|
||||
\ 'project_root_callback': {-> '/foo/bar'},
|
||||
\ 'lsp': 'stdio',
|
||||
|
63
test/lsp/test_lsp_root_detection.vader
Normal file
63
test/lsp/test_lsp_root_detection.vader
Normal file
@ -0,0 +1,63 @@
|
||||
Before:
|
||||
call ale#assert#SetUpLinterTest('c', 'clangd')
|
||||
|
||||
function! Hook1(buffer)
|
||||
return 'abc123'
|
||||
endfunction
|
||||
|
||||
After:
|
||||
let g:ale_lsp_root = {}
|
||||
unlet! b:ale_lsp_root
|
||||
delfunction Hook1
|
||||
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(The buffer-specific variable can be a string):
|
||||
let b:ale_lsp_root = '/some/path'
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull '/some/path'
|
||||
|
||||
Execute(The buffer-specific variable can be a dictionary):
|
||||
let b:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull '/some/path'
|
||||
|
||||
Execute(The buffer-specific variable can have funcrefs):
|
||||
let b:ale_lsp_root = {'clangd': function('Hook1'), 'golangserver': '/path'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull 'abc123'
|
||||
|
||||
Execute(The global variable can be a dictionary):
|
||||
let g:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull '/some/path'
|
||||
|
||||
Execute(The global variable can have funcrefs):
|
||||
let g:ale_lsp_root = {'clangd': function('Hook1'), 'golangserver': '/path'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull 'abc123'
|
||||
|
||||
Execute(The buffer-specific variable overrides the global variable):
|
||||
let b:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/other/path'}
|
||||
let g:ale_lsp_root = {'clangd': '/not/this/path', 'golangserver': '/elsewhere'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull '/some/path'
|
||||
|
||||
Execute(The global variable is queried if the buffer-specific has no value):
|
||||
let b:ale_lsp_root = {'golangserver': '/other/path'}
|
||||
let g:ale_lsp_root = {'clangd': '/some/path', 'golangserver': '/elsewhere'}
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull '/some/path'
|
||||
|
||||
|
||||
Execute(The default hook value is acceptable):
|
||||
call ale#test#SetFilename('other-file.c')
|
||||
|
||||
AssertLSPProjectFull ''
|
@ -97,6 +97,7 @@ Before:
|
||||
\ 'let g:ale_list_vertical = 0',
|
||||
\ 'let g:ale_list_window_size = 10',
|
||||
\ 'let g:ale_loclist_msg_format = ''%code: %%s''',
|
||||
\ 'let g:ale_lsp_root = {}',
|
||||
\ 'let g:ale_max_buffer_history_size = 20',
|
||||
\ 'let g:ale_max_signs = -1',
|
||||
\ 'let g:ale_maximum_file_size = 0',
|
||||
|
Loading…
x
Reference in New Issue
Block a user