mirror of
https://github.com/dense-analysis/ale
synced 2024-12-17 20:05:31 +00:00
Close #4458 - Add an ALEStopLSP command
Add an ALEStopLSP command to stop all language servers that match a given name. Completions are available for the command. This makes it possible to keep other language servers running other than the one you're interested in stopping.
This commit is contained in:
parent
be69af2705
commit
1799f8bec6
@ -1,9 +1,16 @@
|
||||
" Author: w0rp <dev@w0rp.com>
|
||||
" Description: Functions for resetting LSP servers.
|
||||
|
||||
function! s:Message(message) abort
|
||||
call ale#util#Execute('echom ' . string(a:message))
|
||||
endfunction
|
||||
|
||||
" Stop all LSPs and remove all of the data for them.
|
||||
function! ale#lsp#reset#StopAllLSPs() abort
|
||||
call ale#lsp#StopAll()
|
||||
|
||||
if exists('*ale#definition#ClearLSPData')
|
||||
" Clear the mapping for connections, etc.
|
||||
" Clear the go to definition mapping for everything.
|
||||
call ale#definition#ClearLSPData()
|
||||
endif
|
||||
|
||||
@ -25,3 +32,61 @@ function! ale#lsp#reset#StopAllLSPs() abort
|
||||
endfor
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#reset#Complete(arg, line, pos) abort
|
||||
let l:linter_map = ale#lsp_linter#GetLSPLinterMap()
|
||||
let l:candidates = map(values(l:linter_map), {_, linter -> linter.name})
|
||||
call uniq(sort(l:candidates))
|
||||
call filter(l:candidates, {_, name -> name =~? a:arg})
|
||||
|
||||
return l:candidates
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#reset#StopLSP(name, bang) abort
|
||||
let l:linter_map = ale#lsp_linter#GetLSPLinterMap()
|
||||
let l:matched = filter(
|
||||
\ items(l:linter_map),
|
||||
\ {_, item -> item[1].name is# a:name}
|
||||
\)
|
||||
|
||||
if empty(l:matched)
|
||||
if a:bang isnot# '!'
|
||||
call s:Message('No running language server with name: ' . a:name)
|
||||
endif
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
" Stop LSP connections first.
|
||||
for [l:conn_id, l:linter] in l:matched
|
||||
call ale#lsp#Stop(l:conn_id)
|
||||
endfor
|
||||
|
||||
if exists('*ale#definition#ClearLSPData')
|
||||
" Clear the go to definition mapping for everything.
|
||||
call ale#definition#ClearLSPData()
|
||||
endif
|
||||
|
||||
" Remove connections from the lsp_linter map.
|
||||
for [l:conn_id, l:linter] in l:matched
|
||||
call remove(l:linter_map, l:conn_id)
|
||||
endfor
|
||||
|
||||
" Remove the problems for the LSP linters in every buffer.
|
||||
for [l:buffer_string, l:info] in items(g:ale_buffer_info)
|
||||
let l:buffer = str2nr(l:buffer_string)
|
||||
let l:should_clear_buffer = 0
|
||||
|
||||
for l:item in l:info.loclist
|
||||
if l:item.linter_name is# a:name
|
||||
let l:should_clear_buffer = 1
|
||||
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
if l:should_clear_buffer
|
||||
call ale#engine#HandleLoclist(a:name, l:buffer, [], 0)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
@ -8,13 +8,9 @@ if !has_key(s:, 'lsp_linter_map')
|
||||
let s:lsp_linter_map = {}
|
||||
endif
|
||||
|
||||
" A Dictionary to track one-shot handlers for custom LSP requests
|
||||
let s:custom_handlers_map = get(s:, 'custom_handlers_map', {})
|
||||
|
||||
" Clear LSP linter data for the linting engine.
|
||||
function! ale#lsp_linter#ClearLSPData() abort
|
||||
let s:lsp_linter_map = {}
|
||||
let s:custom_handlers_map = {}
|
||||
endfunction
|
||||
|
||||
" Only for internal use.
|
||||
@ -82,7 +78,12 @@ function! s:ShouldIgnoreDiagnostics(buffer, linter) abort
|
||||
endfunction
|
||||
|
||||
function! s:HandleLSPDiagnostics(conn_id, response) abort
|
||||
let l:linter = s:lsp_linter_map[a:conn_id]
|
||||
let l:linter = get(s:lsp_linter_map, a:conn_id)
|
||||
|
||||
if empty(l:linter)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:filename = ale#util#ToResource(a:response.params.uri)
|
||||
let l:escaped_name = escape(
|
||||
\ fnameescape(l:filename),
|
||||
@ -540,9 +541,14 @@ endfunction
|
||||
|
||||
function! s:HandleLSPResponseToCustomRequests(conn_id, response) abort
|
||||
if has_key(a:response, 'id')
|
||||
\&& has_key(s:custom_handlers_map, a:response.id)
|
||||
let l:Handler = remove(s:custom_handlers_map, a:response.id)
|
||||
call l:Handler(a:response)
|
||||
" Get the custom handlers Dictionary from the linter map.
|
||||
let l:linter = get(s:lsp_linter_map, a:conn_id, {})
|
||||
let l:custom_handlers = get(l:linter, 'custom_handlers', {})
|
||||
|
||||
if has_key(l:custom_handlers, a:response.id)
|
||||
let l:Handler = remove(l:custom_handlers, a:response.id)
|
||||
call l:Handler(a:response)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -553,7 +559,17 @@ function! s:OnReadyForCustomRequests(args, linter, lsp_details) abort
|
||||
if l:request_id > 0 && has_key(a:args, 'handler')
|
||||
let l:Callback = function('s:HandleLSPResponseToCustomRequests')
|
||||
call ale#lsp#RegisterCallback(l:id, l:Callback)
|
||||
let s:custom_handlers_map[l:request_id] = a:args.handler
|
||||
|
||||
" Remember the linter this connection is for.
|
||||
let s:lsp_linter_map[l:id] = a:linter
|
||||
|
||||
" Add custom_handlers to the linter Dictionary.
|
||||
if !has_key(a:linter, 'custom_handlers')
|
||||
let a:linter.custom_handlers = {}
|
||||
endif
|
||||
|
||||
" Put the handler function in the map to call later.
|
||||
let a:linter.custom_handlers[l:request_id] = a:args.handler
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
16
doc/ale.txt
16
doc/ale.txt
@ -455,6 +455,11 @@ If you want to use another plugin for LSP features and tsserver, you can use
|
||||
the |g:ale_disable_lsp| setting to disable ALE's own LSP integrations, or
|
||||
ignore particular linters with |g:ale_linters_ignore|.
|
||||
|
||||
If for any reason you want to stop a language server ALE starts, such as when
|
||||
a project configuration has significantly changed, or new files have been
|
||||
added the language server isn't aware of, use either |ALEStopLSP| or
|
||||
|ALEStopAllLSPs| to stop the server until ALE automatically starts it again.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
5.1 Completion *ale-completion*
|
||||
|
||||
@ -3931,6 +3936,17 @@ ALEStopAllLSPs *ALEStopAllLSPs*
|
||||
This command can be used when LSP clients mess up and need to be restarted.
|
||||
|
||||
|
||||
ALEStopLSP `linter_name` *ALEStopLSP*
|
||||
|
||||
`ALEStopLSP` will stop a specific language server with a given linter name.
|
||||
Completion is supported for currently running language servers. All language
|
||||
servers with the given name will be stopped across all buffers for all
|
||||
projects.
|
||||
|
||||
If the command is run with a bang (`:ALEStopLSP!`), all warnings will be
|
||||
suppressed.
|
||||
|
||||
|
||||
===============================================================================
|
||||
9. API *ale-api*
|
||||
|
||||
|
@ -246,6 +246,8 @@ command! -bar ALEDisableBuffer :call ale#toggle#DisableBuffer(bufnr(''))
|
||||
command! -bar ALEResetBuffer :call ale#toggle#ResetBuffer(bufnr(''))
|
||||
" A command to stop all LSP-like clients, including tsserver.
|
||||
command! -bar ALEStopAllLSPs :call ale#lsp#reset#StopAllLSPs()
|
||||
" A command to stop a specific language server, or tsseserver.
|
||||
command! -bar -bang -nargs=1 -complete=customlist,ale#lsp#reset#Complete ALEStopLSP :call ale#lsp#reset#StopLSP(<f-args>, '<bang>')
|
||||
|
||||
" A command for linting manually.
|
||||
command! -bar ALELint :call ale#Queue(0, 'lint_file')
|
||||
|
@ -5,6 +5,7 @@ Before:
|
||||
Save g:ale_set_loclist
|
||||
Save g:ale_set_highlights
|
||||
Save g:ale_echo_cursor
|
||||
Save g:ale_buffer_info
|
||||
|
||||
let g:ale_enabled = 0
|
||||
let g:ale_set_signs = 0
|
||||
@ -12,14 +13,20 @@ Before:
|
||||
let g:ale_set_loclist = 0
|
||||
let g:ale_set_highlights = 0
|
||||
let g:ale_echo_cursor = 0
|
||||
let g:expr_list = []
|
||||
|
||||
function EmptyString() abort
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
runtime autoload/ale/util.vim
|
||||
|
||||
function! ale#util#Execute(expr) abort
|
||||
call add(g:expr_list, a:expr)
|
||||
endfunction
|
||||
|
||||
call ale#engine#InitBufferInfo(bufnr(''))
|
||||
" Call this function first, so we can be sure the module is loaded before we
|
||||
" check if it exists.
|
||||
" Call this function first, to clear LSP data.
|
||||
call ale#lsp_linter#ClearLSPData()
|
||||
|
||||
call ale#linter#Define('testft', {
|
||||
@ -30,7 +37,14 @@ Before:
|
||||
\ 'project_root': function('EmptyString'),
|
||||
\ 'language': function('EmptyString'),
|
||||
\})
|
||||
|
||||
call ale#linter#Define('testft', {
|
||||
\ 'name': 'lsplinter2',
|
||||
\ 'lsp': 'tsserver',
|
||||
\ 'executable': function('EmptyString'),
|
||||
\ 'command': function('EmptyString'),
|
||||
\ 'project_root': function('EmptyString'),
|
||||
\ 'language': function('EmptyString'),
|
||||
\})
|
||||
call ale#linter#Define('testft', {
|
||||
\ 'name': 'otherlinter',
|
||||
\ 'callback': 'TestCallback',
|
||||
@ -42,34 +56,23 @@ Before:
|
||||
After:
|
||||
Restore
|
||||
|
||||
delfunction EmptyString
|
||||
unlet! g:expr_list
|
||||
unlet! b:ale_save_event_fired
|
||||
|
||||
delfunction EmptyString
|
||||
" Clear LSP data after tests.
|
||||
call ale#lsp_linter#ClearLSPData()
|
||||
|
||||
runtime autoload/ale/util.vim
|
||||
|
||||
call ale#linter#Reset()
|
||||
|
||||
Given testft(Some file with an imaginary filetype):
|
||||
Execute(ALEStopAllLSPs should clear the loclist):
|
||||
" For these tests we only need to set the keys we need.
|
||||
let g:ale_buffer_info[bufnr('')].loclist = [
|
||||
\ {
|
||||
\ 'text': 'a',
|
||||
\ 'lnum': 10,
|
||||
\ 'col': 0,
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'vcol': 0,
|
||||
\ 'type': 'E',
|
||||
\ 'nr': -1,
|
||||
\ 'linter_name': 'lsplinter',
|
||||
\ },
|
||||
\ {
|
||||
\ 'text': 'a',
|
||||
\ 'lnum': 10,
|
||||
\ 'col': 0,
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'vcol': 0,
|
||||
\ 'type': 'E',
|
||||
\ 'nr': -1,
|
||||
\ 'linter_name': 'otherlinter',
|
||||
\ },
|
||||
\ {'linter_name': 'lsplinter'},
|
||||
\ {'linter_name': 'otherlinter'},
|
||||
\]
|
||||
let g:ale_buffer_info[bufnr('')].active_linter_list = [
|
||||
\ {'name': 'lsplinter'},
|
||||
@ -79,20 +82,88 @@ Execute(ALEStopAllLSPs should clear the loclist):
|
||||
ALEStopAllLSPs
|
||||
|
||||
" The loclist should be updated.
|
||||
AssertEqual g:ale_buffer_info[bufnr('')].loclist, [
|
||||
\ {
|
||||
\ 'text': 'a',
|
||||
\ 'lnum': 10,
|
||||
\ 'col': 0,
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'vcol': 0,
|
||||
\ 'type': 'E',
|
||||
\ 'nr': -1,
|
||||
\ 'linter_name': 'otherlinter',
|
||||
\ },
|
||||
\]
|
||||
AssertEqual
|
||||
\ ['otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].loclist), 'v:val.linter_name')
|
||||
|
||||
" The LSP linter should be removed from the active linter list.
|
||||
AssertEqual
|
||||
\ ['otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
|
||||
|
||||
Execute(ALEStopLSP should stop a named LSP):
|
||||
let g:ale_buffer_info[bufnr('')].loclist = [
|
||||
\ {'linter_name': 'lsplinter'},
|
||||
\ {'linter_name': 'lsplinter2'},
|
||||
\ {'linter_name': 'otherlinter'},
|
||||
\]
|
||||
let g:ale_buffer_info[bufnr('')].active_linter_list = [
|
||||
\ {'name': 'lsplinter'},
|
||||
\ {'name': 'lsplinter2'},
|
||||
\ {'name': 'otherlinter'},
|
||||
\]
|
||||
call ale#lsp_linter#SetLSPLinterMap({
|
||||
\ 'conn1': {'name': 'lsplinter'},
|
||||
\ 'conn2': {'name': 'lsplinter2'},
|
||||
\ 'conn3': {'name': 'lsplinter'},
|
||||
\})
|
||||
|
||||
ALEStopLSP lsplinter
|
||||
|
||||
" We should remove only the items for this linter.
|
||||
AssertEqual
|
||||
\ ['lsplinter2', 'otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].loclist), 'v:val.linter_name')
|
||||
|
||||
" The linter should be removed from the active linter list.
|
||||
AssertEqual
|
||||
\ ['lsplinter2', 'otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
|
||||
|
||||
" The connections linters with this name should be removed.
|
||||
AssertEqual
|
||||
\ {'conn2': {'name': 'lsplinter2'}},
|
||||
\ ale#lsp_linter#GetLSPLinterMap()
|
||||
|
||||
Execute(ALEStopLSP should not clear results for linters not running):
|
||||
let g:ale_buffer_info[bufnr('')].loclist = [
|
||||
\ {'linter_name': 'lsplinter'},
|
||||
\ {'linter_name': 'otherlinter'},
|
||||
\]
|
||||
let g:ale_buffer_info[bufnr('')].active_linter_list = [
|
||||
\ {'name': 'lsplinter'},
|
||||
\ {'name': 'otherlinter'},
|
||||
\]
|
||||
|
||||
ALEStopLSP lsplinter
|
||||
|
||||
" We should emit a message saying the server isn't running.
|
||||
AssertEqual
|
||||
\ ['echom ''No running language server with name: lsplinter'''],
|
||||
\ g:expr_list
|
||||
|
||||
" We should keep the linter items.
|
||||
AssertEqual
|
||||
\ ['lsplinter', 'otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].loclist), 'v:val.linter_name')
|
||||
AssertEqual
|
||||
\ ['lsplinter', 'otherlinter'],
|
||||
\ map(copy(g:ale_buffer_info[bufnr('')].active_linter_list), 'v:val.name')
|
||||
|
||||
Execute(ALEStopLSP with a bang should not emit warnings):
|
||||
ALEStopLSP! lsplinter
|
||||
|
||||
AssertEqual [], g:expr_list
|
||||
|
||||
Execute(ALEStopLSP's completion function should suggest running linter names):
|
||||
call ale#lsp_linter#SetLSPLinterMap({
|
||||
\ 'conn1': {'name': 'pyright'},
|
||||
\ 'conn2': {'name': 'pylsp'},
|
||||
\ 'conn3': {'name': 'imaginaryserver'},
|
||||
\})
|
||||
|
||||
AssertEqual
|
||||
\ ['imaginaryserver', 'pylsp', 'pyright'],
|
||||
\ ale#lsp#reset#Complete('', '', 42)
|
||||
AssertEqual ['imaginaryserver'], ale#lsp#reset#Complete('inary', '', 42)
|
||||
AssertEqual ['pylsp'], ale#lsp#reset#Complete('LSP', '', 42)
|
||||
|
Loading…
Reference in New Issue
Block a user