Merge pull request #2241 from bk2204/lsp-detect-hook

Add a hook to detect LSP project root
This commit is contained in:
w0rp 2019-01-26 19:40:45 +00:00 committed by GitHub
commit 452460b8cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 132 additions and 1 deletions

View File

@ -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

View File

@ -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',

View File

@ -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,

View File

@ -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*

View File

@ -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)

View File

@ -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',

View 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 ''

View File

@ -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',