Read language servers from config in :lsp-restart
`:lsp-stop` should consider only the set of active language servers for a document. `:lsp-restart` though may be used to start up a language server that crashed or was manually stopped, so it needs to consider the language servers in config instead. This change inlines the `valid_lang_servers` function into `:lsp-stop` and `:lsp-restart` and changes `:lsp-restart` to check the doc's config rather than active language servers. `:lsp-restart` now also does not need to clone the language server names as strings since it borrows from the config and arguments rather than `Document`. The completer has also been split into two - one matching active language servers, used by `:lsp-stop`, and the other matching configured language servers, used by `:lsp-restart`. This also removes the part of `:lsp-restart` which bailed if a language server failed to be restarted (for example because it is not installed). There might be multiple language servers configured for a language and only one installed. In that case the `:lsp-restart` should be considered successful even if not all servers could be started. Bailing prevented any language servers which could start from being attached to the document. Instead errors are collected and emitted at the end.
This commit is contained in:
parent
6304e7b2a7
commit
3d7e2730e7
2 changed files with 77 additions and 36 deletions
|
@ -1513,31 +1513,6 @@ fn lsp_workspace_command(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns all language servers used by the current document if no servers are supplied
|
||||
/// If servers are supplied, do a check to make sure that all of the servers exist
|
||||
fn valid_lang_servers(doc: &Document, servers: &[Cow<str>]) -> anyhow::Result<Vec<String>> {
|
||||
let valid_ls_names = doc
|
||||
.language_servers()
|
||||
.map(|ls| ls.name().to_string())
|
||||
.collect();
|
||||
|
||||
if servers.is_empty() {
|
||||
Ok(valid_ls_names)
|
||||
} else {
|
||||
let (valid, invalid): (Vec<_>, Vec<_>) = servers
|
||||
.iter()
|
||||
.map(|m| m.to_string())
|
||||
.partition(|ls| valid_ls_names.contains(ls));
|
||||
|
||||
if !invalid.is_empty() {
|
||||
let s = if invalid.len() == 1 { "" } else { "s" };
|
||||
bail!("Unknown language server{s}: {}", invalid.join(", "));
|
||||
};
|
||||
|
||||
Ok(valid)
|
||||
}
|
||||
}
|
||||
|
||||
fn lsp_restart(
|
||||
cx: &mut compositor::Context,
|
||||
args: &[Cow<str>],
|
||||
|
@ -1553,10 +1528,29 @@ fn lsp_restart(
|
|||
.language_config()
|
||||
.context("LSP not defined for the current document")?;
|
||||
|
||||
let ls_restart_names = valid_lang_servers(doc, args)?;
|
||||
let language_servers: Vec<_> = config
|
||||
.language_servers
|
||||
.iter()
|
||||
.map(|ls| ls.name.as_str())
|
||||
.collect();
|
||||
let language_servers = if args.is_empty() {
|
||||
language_servers
|
||||
} else {
|
||||
let (valid, invalid): (Vec<_>, Vec<_>) = args
|
||||
.iter()
|
||||
.map(|arg| arg.as_ref())
|
||||
.partition(|name| language_servers.contains(name));
|
||||
if !invalid.is_empty() {
|
||||
let s = if invalid.len() == 1 { "" } else { "s" };
|
||||
bail!("Unknown language server{s}: {}", invalid.join(", "));
|
||||
}
|
||||
valid
|
||||
};
|
||||
|
||||
for server in ls_restart_names.iter() {
|
||||
cx.editor
|
||||
let mut errors = Vec::new();
|
||||
for server in language_servers.iter() {
|
||||
match cx
|
||||
.editor
|
||||
.language_servers
|
||||
.restart_server(
|
||||
server,
|
||||
|
@ -1565,7 +1559,15 @@ fn lsp_restart(
|
|||
&editor_config.workspace_lsp_roots,
|
||||
editor_config.lsp.snippets,
|
||||
)
|
||||
.transpose()?;
|
||||
.transpose()
|
||||
{
|
||||
// Ignore the executable-not-found error unless the server was explicitly requested
|
||||
// in the arguments.
|
||||
Err(helix_lsp::Error::ExecutableNotFound(_))
|
||||
if !args.iter().any(|arg| arg == server) => {}
|
||||
Err(err) => errors.push(err.to_string()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
// This collect is needed because refresh_language_server would need to re-borrow editor.
|
||||
|
@ -1575,7 +1577,7 @@ fn lsp_restart(
|
|||
.filter_map(|doc| match doc.language_config() {
|
||||
Some(config)
|
||||
if config.language_servers.iter().any(|ls| {
|
||||
ls_restart_names
|
||||
language_servers
|
||||
.iter()
|
||||
.any(|restarted_ls| restarted_ls == &ls.name)
|
||||
}) =>
|
||||
|
@ -1590,7 +1592,14 @@ fn lsp_restart(
|
|||
cx.editor.refresh_language_servers(document_id);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"Error restarting language servers: {}",
|
||||
errors.join(", ")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn lsp_stop(
|
||||
|
@ -1603,9 +1612,25 @@ fn lsp_stop(
|
|||
}
|
||||
let doc = doc!(cx.editor);
|
||||
|
||||
let ls_shutdown_names = valid_lang_servers(doc, args)?;
|
||||
let language_servers: Vec<_> = doc
|
||||
.language_servers()
|
||||
.map(|ls| ls.name().to_string())
|
||||
.collect();
|
||||
let language_servers = if args.is_empty() {
|
||||
language_servers
|
||||
} else {
|
||||
let (valid, invalid): (Vec<_>, Vec<_>) = args
|
||||
.iter()
|
||||
.map(|arg| arg.to_string())
|
||||
.partition(|name| language_servers.contains(name));
|
||||
if !invalid.is_empty() {
|
||||
let s = if invalid.len() == 1 { "" } else { "s" };
|
||||
bail!("Unknown language server{s}: {}", invalid.join(", "));
|
||||
}
|
||||
valid
|
||||
};
|
||||
|
||||
for ls_name in &ls_shutdown_names {
|
||||
for ls_name in &language_servers {
|
||||
cx.editor.language_servers.stop(ls_name);
|
||||
|
||||
for doc in cx.editor.documents_mut() {
|
||||
|
@ -2983,14 +3008,14 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
|
|||
aliases: &[],
|
||||
doc: "Restarts the given language servers, or all language servers that are used by the current file if no arguments are supplied",
|
||||
fun: lsp_restart,
|
||||
signature: CommandSignature::all(completers::language_servers),
|
||||
signature: CommandSignature::all(completers::configured_language_servers),
|
||||
},
|
||||
TypableCommand {
|
||||
name: "lsp-stop",
|
||||
aliases: &[],
|
||||
doc: "Stops the given language servers, or all language servers that are used by the current file if no arguments are supplied",
|
||||
fun: lsp_stop,
|
||||
signature: CommandSignature::all(completers::language_servers),
|
||||
signature: CommandSignature::all(completers::active_language_servers),
|
||||
},
|
||||
TypableCommand {
|
||||
name: "tree-sitter-scopes",
|
||||
|
|
|
@ -410,7 +410,8 @@ pub mod completers {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn language_servers(editor: &Editor, input: &str) -> Vec<Completion> {
|
||||
/// Completes names of language servers which are running for the current document.
|
||||
pub fn active_language_servers(editor: &Editor, input: &str) -> Vec<Completion> {
|
||||
let language_servers = doc!(editor).language_servers().map(|ls| ls.name());
|
||||
|
||||
fuzzy_match(input, language_servers, false)
|
||||
|
@ -419,6 +420,21 @@ pub mod completers {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Completes names of language servers which are configured for the language of the current
|
||||
/// document.
|
||||
pub fn configured_language_servers(editor: &Editor, input: &str) -> Vec<Completion> {
|
||||
let language_servers = doc!(editor)
|
||||
.language_config()
|
||||
.into_iter()
|
||||
.flat_map(|config| &config.language_servers)
|
||||
.map(|ls| ls.name.as_str());
|
||||
|
||||
fuzzy_match(input, language_servers, false)
|
||||
.into_iter()
|
||||
.map(|(name, _)| ((0..), Span::raw(name.to_string())))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn setting(_editor: &Editor, input: &str) -> Vec<Completion> {
|
||||
static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
|
||||
let mut keys = Vec::new();
|
||||
|
|
Loading…
Add table
Reference in a new issue