Cover the Rust LSP with tests, allow LSP linters to be named anything, and rename the Rust LSP linter to rls
				
					
				
			This commit is contained in:
		
							parent
							
								
									617582c5d3
								
							
						
					
					
						commit
						09d50ebe31
					
				@ -1,33 +0,0 @@
 | 
				
			|||||||
" Author: w0rp <devw0rp@gmail.com>
 | 
					 | 
				
			||||||
" Description: A language server for Rust
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
call ale#Set('rust_langserver_executable', 'rls')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function! ale_linters#rust#langserver#GetExecutable(buffer) abort
 | 
					 | 
				
			||||||
    return ale#Var(a:buffer, 'rust_langserver_executable')
 | 
					 | 
				
			||||||
endfunction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function! ale_linters#rust#langserver#GetCommand(buffer) abort
 | 
					 | 
				
			||||||
    let l:executable = ale_linters#rust#langserver#GetExecutable(a:buffer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return ale#Escape(l:executable) . ' +nightly'
 | 
					 | 
				
			||||||
endfunction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function! ale_linters#rust#langserver#GetLanguage(buffer) abort
 | 
					 | 
				
			||||||
    return 'rust'
 | 
					 | 
				
			||||||
endfunction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function! ale_linters#rust#langserver#GetProjectRoot(buffer) abort
 | 
					 | 
				
			||||||
    let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return !empty(l:cargo_file) ? fnamemodify(l:cargo_file, ':h') : ''
 | 
					 | 
				
			||||||
endfunction
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
call ale#linter#Define('rust', {
 | 
					 | 
				
			||||||
\   'name': 'langserver',
 | 
					 | 
				
			||||||
\   'lsp': 'stdio',
 | 
					 | 
				
			||||||
\   'executable_callback': 'ale_linters#rust#langserver#GetExecutable',
 | 
					 | 
				
			||||||
\   'command_callback': 'ale_linters#rust#langserver#GetCommand',
 | 
					 | 
				
			||||||
\   'language_callback': 'ale_linters#rust#langserver#GetLanguage',
 | 
					 | 
				
			||||||
\   'project_root_callback': 'ale_linters#rust#langserver#GetProjectRoot',
 | 
					 | 
				
			||||||
\})
 | 
					 | 
				
			||||||
							
								
								
									
										33
									
								
								ale_linters/rust/rls.vim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								ale_linters/rust/rls.vim
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					" Author: w0rp <devw0rp@gmail.com>
 | 
				
			||||||
 | 
					" Description: A language server for Rust
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					call ale#Set('rust_rls_executable', 'rls')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function! ale_linters#rust#rls#GetExecutable(buffer) abort
 | 
				
			||||||
 | 
					    return ale#Var(a:buffer, 'rust_rls_executable')
 | 
				
			||||||
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function! ale_linters#rust#rls#GetCommand(buffer) abort
 | 
				
			||||||
 | 
					    let l:executable = ale_linters#rust#rls#GetExecutable(a:buffer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ale#Escape(l:executable) . ' +nightly'
 | 
				
			||||||
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function! ale_linters#rust#rls#GetLanguage(buffer) abort
 | 
				
			||||||
 | 
					    return 'rust'
 | 
				
			||||||
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function! ale_linters#rust#rls#GetProjectRoot(buffer) abort
 | 
				
			||||||
 | 
					    let l:cargo_file = ale#path#FindNearestFile(a:buffer, 'Cargo.toml')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return !empty(l:cargo_file) ? fnamemodify(l:cargo_file, ':h') : ''
 | 
				
			||||||
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					call ale#linter#Define('rust', {
 | 
				
			||||||
 | 
					\   'name': 'rls',
 | 
				
			||||||
 | 
					\   'lsp': 'stdio',
 | 
				
			||||||
 | 
					\   'executable_callback': 'ale_linters#rust#rls#GetExecutable',
 | 
				
			||||||
 | 
					\   'command_callback': 'ale_linters#rust#rls#GetCommand',
 | 
				
			||||||
 | 
					\   'language_callback': 'ale_linters#rust#rls#GetLanguage',
 | 
				
			||||||
 | 
					\   'project_root_callback': 'ale_linters#rust#rls#GetProjectRoot',
 | 
				
			||||||
 | 
					\})
 | 
				
			||||||
@ -170,7 +170,7 @@ function! ale#completion#ParseTSServerCompletionEntryDetails(response) abort
 | 
				
			|||||||
    return l:results
 | 
					    return l:results
 | 
				
			||||||
endfunction
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function! s:HandleTSServerLSPResponse(response) abort
 | 
					function! s:HandleTSServerLSPResponse(conn_id, response) abort
 | 
				
			||||||
    if !s:CompletionStillValid(get(a:response, 'request_seq'))
 | 
					    if !s:CompletionStillValid(get(a:response, 'request_seq'))
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
    endif
 | 
					    endif
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,11 @@ if !has_key(s:, 'job_info_map')
 | 
				
			|||||||
    let s:job_info_map = {}
 | 
					    let s:job_info_map = {}
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Associates LSP connection IDs with linter names.
 | 
				
			||||||
 | 
					if !has_key(s:, 'lsp_linter_map')
 | 
				
			||||||
 | 
					    let s:lsp_linter_map = {}
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let s:executable_cache_map = {}
 | 
					let s:executable_cache_map = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
" Check if files are executable, and if they are, remember that they are
 | 
					" Check if files are executable, and if they are, remember that they are
 | 
				
			||||||
@ -192,12 +197,18 @@ function! s:HandleExit(job_id, exit_code) abort
 | 
				
			|||||||
    call s:HandleLoclist(l:linter.name, l:buffer, l:loclist)
 | 
					    call s:HandleLoclist(l:linter.name, l:buffer, l:loclist)
 | 
				
			||||||
endfunction
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function! s:HandleLSPDiagnostics(response) abort
 | 
					function! s:HandleLSPDiagnostics(conn_id, response) abort
 | 
				
			||||||
 | 
					    let l:linter_name = s:lsp_linter_map[a:conn_id]
 | 
				
			||||||
    let l:filename = ale#path#FromURI(a:response.params.uri)
 | 
					    let l:filename = ale#path#FromURI(a:response.params.uri)
 | 
				
			||||||
    let l:buffer = bufnr(l:filename)
 | 
					    let l:buffer = bufnr(l:filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if l:buffer <= 0
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
 | 
					    let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    call s:HandleLoclist('langserver', l:buffer, l:loclist)
 | 
					    call s:HandleLoclist(l:linter_name, l:buffer, l:loclist)
 | 
				
			||||||
endfunction
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function! s:HandleTSServerDiagnostics(response, error_type) abort
 | 
					function! s:HandleTSServerDiagnostics(response, error_type) abort
 | 
				
			||||||
@ -233,14 +244,14 @@ function! s:HandleLSPErrorMessage(error_message) abort
 | 
				
			|||||||
    endfor
 | 
					    endfor
 | 
				
			||||||
endfunction
 | 
					endfunction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function! ale#engine#HandleLSPResponse(response) abort
 | 
					function! ale#engine#HandleLSPResponse(conn_id, response) abort
 | 
				
			||||||
    let l:method = get(a:response, 'method', '')
 | 
					    let l:method = get(a:response, 'method', '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if get(a:response, 'jsonrpc', '') ==# '2.0' && has_key(a:response, 'error')
 | 
					    if get(a:response, 'jsonrpc', '') ==# '2.0' && has_key(a:response, 'error')
 | 
				
			||||||
        " Uncomment this line to print LSP error messages.
 | 
					        " Uncomment this line to print LSP error messages.
 | 
				
			||||||
        " call s:HandleLSPErrorMessage(a:response.error.message)
 | 
					        " call s:HandleLSPErrorMessage(a:response.error.message)
 | 
				
			||||||
    elseif l:method ==# 'textDocument/publishDiagnostics'
 | 
					    elseif l:method ==# 'textDocument/publishDiagnostics'
 | 
				
			||||||
        call s:HandleLSPDiagnostics(a:response)
 | 
					        call s:HandleLSPDiagnostics(a:conn_id, a:response)
 | 
				
			||||||
    elseif get(a:response, 'type', '') ==# 'event'
 | 
					    elseif get(a:response, 'type', '') ==# 'event'
 | 
				
			||||||
    \&& get(a:response, 'event', '') ==# 'semanticDiag'
 | 
					    \&& get(a:response, 'event', '') ==# 'semanticDiag'
 | 
				
			||||||
        call s:HandleTSServerDiagnostics(a:response, 'semantic')
 | 
					        call s:HandleTSServerDiagnostics(a:response, 'semantic')
 | 
				
			||||||
@ -617,6 +628,9 @@ function! s:CheckWithLSP(buffer, linter) abort
 | 
				
			|||||||
    let l:id = l:lsp_details.connection_id
 | 
					    let l:id = l:lsp_details.connection_id
 | 
				
			||||||
    let l:root = l:lsp_details.project_root
 | 
					    let l:root = l:lsp_details.project_root
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    " Remember the linter this connection is for.
 | 
				
			||||||
 | 
					    let s:lsp_linter_map[l:id] = a:linter.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let l:change_message = a:linter.lsp ==# 'tsserver'
 | 
					    let l:change_message = a:linter.lsp ==# 'tsserver'
 | 
				
			||||||
    \   ? ale#lsp#tsserver_message#Geterr(a:buffer)
 | 
					    \   ? ale#lsp#tsserver_message#Geterr(a:buffer)
 | 
				
			||||||
    \   : ale#lsp#message#DidChange(a:buffer)
 | 
					    \   : ale#lsp#message#DidChange(a:buffer)
 | 
				
			||||||
 | 
				
			|||||||
@ -222,7 +222,7 @@ function! ale#lsp#HandleMessage(conn, message) abort
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            " Call all of the registered handlers with the response.
 | 
					            " Call all of the registered handlers with the response.
 | 
				
			||||||
            for l:Callback in a:conn.callback_list
 | 
					            for l:Callback in a:conn.callback_list
 | 
				
			||||||
                call ale#util#GetFunction(l:Callback)(l:response)
 | 
					                call ale#util#GetFunction(l:Callback)(a:conn.id, l:response)
 | 
				
			||||||
            endfor
 | 
					            endfor
 | 
				
			||||||
        endif
 | 
					        endif
 | 
				
			||||||
    endfor
 | 
					    endfor
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										0
									
								
								test/command_callback/rust-rls-project/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								test/command_callback/rust-rls-project/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										32
									
								
								test/command_callback/test_rust_rls_callbacks.vader
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								test/command_callback/test_rust_rls_callbacks.vader
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					Before:
 | 
				
			||||||
 | 
					  Save g:ale_rust_rls_executable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unlet! g:ale_rust_rls_executable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  runtime ale_linters/rust/rls.vim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call ale#test#SetDirectory('/testplugin/test/command_callback')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After:
 | 
				
			||||||
 | 
					  Restore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call ale#test#RestoreDirectory()
 | 
				
			||||||
 | 
					  call ale#linter#Reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Execute(The default executable path should be correct):
 | 
				
			||||||
 | 
					  AssertEqual 'rls', ale_linters#rust#rls#GetExecutable(bufnr(''))
 | 
				
			||||||
 | 
					  AssertEqual
 | 
				
			||||||
 | 
					  \ ale#Escape('rls') . ' +nightly',
 | 
				
			||||||
 | 
					  \ ale_linters#rust#rls#GetCommand(bufnr(''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Execute(The language string should be correct):
 | 
				
			||||||
 | 
					  AssertEqual 'rust', ale_linters#rust#rls#GetLanguage(bufnr(''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Execute(The project root should be detected correctly):
 | 
				
			||||||
 | 
					  AssertEqual '', ale_linters#rust#rls#GetProjectRoot(bufnr(''))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call ale#test#SetFilename('rust-rls-project/test.rs')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  AssertEqual
 | 
				
			||||||
 | 
					  \ g:dir . '/rust-rls-project',
 | 
				
			||||||
 | 
					  \ ale_linters#rust#rls#GetProjectRoot(bufnr(''))
 | 
				
			||||||
@ -15,7 +15,7 @@ Execute(tsserver syntax error responses should be handled correctly):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  " When we get syntax errors and no semantic errors, we should keep the
 | 
					  " When we get syntax errors and no semantic errors, we should keep the
 | 
				
			||||||
  " syntax errors.
 | 
					  " syntax errors.
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'syntaxDiag',
 | 
					  \ 'event': 'syntaxDiag',
 | 
				
			||||||
@ -37,7 +37,7 @@ Execute(tsserver syntax error responses should be handled correctly):
 | 
				
			|||||||
  \   ],
 | 
					  \   ],
 | 
				
			||||||
  \ },
 | 
					  \ },
 | 
				
			||||||
  \})
 | 
					  \})
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'semanticDiag',
 | 
					  \ 'event': 'semanticDiag',
 | 
				
			||||||
@ -65,7 +65,7 @@ Execute(tsserver syntax error responses should be handled correctly):
 | 
				
			|||||||
  \ getloclist(0)
 | 
					  \ getloclist(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  " After we get empty syntax errors, we should clear them.
 | 
					  " After we get empty syntax errors, we should clear them.
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'syntaxDiag',
 | 
					  \ 'event': 'syntaxDiag',
 | 
				
			||||||
@ -88,7 +88,7 @@ Execute(tsserver semantic error responses should be handled correctly):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  " When we get syntax errors and no semantic errors, we should keep the
 | 
					  " When we get syntax errors and no semantic errors, we should keep the
 | 
				
			||||||
  " syntax errors.
 | 
					  " syntax errors.
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'syntaxDiag',
 | 
					  \ 'event': 'syntaxDiag',
 | 
				
			||||||
@ -98,7 +98,7 @@ Execute(tsserver semantic error responses should be handled correctly):
 | 
				
			|||||||
  \   ],
 | 
					  \   ],
 | 
				
			||||||
  \ },
 | 
					  \ },
 | 
				
			||||||
  \})
 | 
					  \})
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'semanticDiag',
 | 
					  \ 'event': 'semanticDiag',
 | 
				
			||||||
@ -138,7 +138,7 @@ Execute(tsserver semantic error responses should be handled correctly):
 | 
				
			|||||||
  \ getloclist(0)
 | 
					  \ getloclist(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  " After we get empty syntax errors, we should clear them.
 | 
					  " After we get empty syntax errors, we should clear them.
 | 
				
			||||||
  call ale#engine#HandleLSPResponse({
 | 
					  call ale#engine#HandleLSPResponse(1, {
 | 
				
			||||||
  \ 'seq': 0,
 | 
					  \ 'seq': 0,
 | 
				
			||||||
  \ 'type': 'event',
 | 
					  \ 'type': 'event',
 | 
				
			||||||
  \ 'event': 'semanticDiag',
 | 
					  \ 'event': 'semanticDiag',
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user