mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Show config deseralization failures on start up
This commit is contained in:
parent
7a564af989
commit
6d7055e322
@ -150,7 +150,17 @@ fn run_server() -> Result<()> {
|
|||||||
|
|
||||||
let mut config = Config::new(root_path, initialize_params.capabilities);
|
let mut config = Config::new(root_path, initialize_params.capabilities);
|
||||||
if let Some(json) = initialize_params.initialization_options {
|
if let Some(json) = initialize_params.initialization_options {
|
||||||
let _ = config.update(json);
|
if let Err(e) = config.update(json) {
|
||||||
|
use lsp_types::{
|
||||||
|
notification::{Notification, ShowMessage},
|
||||||
|
MessageType, ShowMessageParams,
|
||||||
|
};
|
||||||
|
let not = lsp_server::Notification::new(
|
||||||
|
ShowMessage::METHOD.to_string(),
|
||||||
|
ShowMessageParams { typ: MessageType::WARNING, message: e.to_string() },
|
||||||
|
);
|
||||||
|
connection.sender.send(lsp_server::Message::Notification(not)).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let server_capabilities = rust_analyzer::server_capabilities(&config);
|
let server_capabilities = rust_analyzer::server_capabilities(&config);
|
||||||
@ -161,7 +171,11 @@ fn run_server() -> Result<()> {
|
|||||||
name: String::from("rust-analyzer"),
|
name: String::from("rust-analyzer"),
|
||||||
version: Some(String::from(env!("REV"))),
|
version: Some(String::from(env!("REV"))),
|
||||||
}),
|
}),
|
||||||
offset_encoding: if supports_utf8(&config.caps) { Some("utf-8".to_string()) } else { None },
|
offset_encoding: if supports_utf8(config.caps()) {
|
||||||
|
Some("utf-8".to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let initialize_result = serde_json::to_value(initialize_result).unwrap();
|
let initialize_result = serde_json::to_value(initialize_result).unwrap();
|
||||||
@ -183,7 +197,7 @@ fn run_server() -> Result<()> {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
.filter(|workspaces| !workspaces.is_empty())
|
.filter(|workspaces| !workspaces.is_empty())
|
||||||
.unwrap_or_else(|| vec![config.root_path.clone()]);
|
.unwrap_or_else(|| vec![config.root_path().clone()]);
|
||||||
|
|
||||||
let discovered = ProjectManifest::discover_all(&workspace_roots);
|
let discovered = ProjectManifest::discover_all(&workspace_roots);
|
||||||
tracing::info!("discovered projects: {:?}", discovered);
|
tracing::info!("discovered projects: {:?}", discovered);
|
||||||
|
@ -27,7 +27,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
|||||||
})),
|
})),
|
||||||
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
hover_provider: Some(HoverProviderCapability::Simple(true)),
|
||||||
completion_provider: Some(CompletionOptions {
|
completion_provider: Some(CompletionOptions {
|
||||||
resolve_provider: completions_resolve_provider(&config.caps),
|
resolve_provider: completions_resolve_provider(config.caps()),
|
||||||
trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
|
trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
|
||||||
all_commit_characters: None,
|
all_commit_characters: None,
|
||||||
completion_item: None,
|
completion_item: None,
|
||||||
@ -46,7 +46,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
|
|||||||
document_highlight_provider: Some(OneOf::Left(true)),
|
document_highlight_provider: Some(OneOf::Left(true)),
|
||||||
document_symbol_provider: Some(OneOf::Left(true)),
|
document_symbol_provider: Some(OneOf::Left(true)),
|
||||||
workspace_symbol_provider: Some(OneOf::Left(true)),
|
workspace_symbol_provider: Some(OneOf::Left(true)),
|
||||||
code_action_provider: Some(code_action_capabilities(&config.caps)),
|
code_action_provider: Some(code_action_capabilities(config.caps())),
|
||||||
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
|
code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
|
||||||
document_formatting_provider: Some(OneOf::Left(true)),
|
document_formatting_provider: Some(OneOf::Left(true)),
|
||||||
document_range_formatting_provider: match config.rustfmt() {
|
document_range_formatting_provider: match config.rustfmt() {
|
||||||
|
@ -123,7 +123,7 @@ impl CargoTargetSpec {
|
|||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
) -> Result<Option<CargoTargetSpec>> {
|
) -> Result<Option<CargoTargetSpec>> {
|
||||||
let crate_id = match global_state_snapshot.analysis.crate_for(file_id)?.first() {
|
let crate_id = match global_state_snapshot.analysis.crate_for(file_id)?.first() {
|
||||||
Some(crate_id) => *crate_id,
|
Some(&crate_id) => crate_id,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) {
|
let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) {
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//! configure the server itself, feature flags are passed into analysis, and
|
//! configure the server itself, feature flags are passed into analysis, and
|
||||||
//! tweak things like automatic insertion of `()` in completions.
|
//! tweak things like automatic insertion of `()` in completions.
|
||||||
|
|
||||||
use std::{ffi::OsString, iter, path::PathBuf};
|
use std::{ffi::OsString, fmt, iter, path::PathBuf};
|
||||||
|
|
||||||
use flycheck::FlycheckConfig;
|
use flycheck::FlycheckConfig;
|
||||||
use ide::{
|
use ide::{
|
||||||
@ -19,6 +19,7 @@ use ide_db::{
|
|||||||
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
|
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
|
||||||
SnippetCap,
|
SnippetCap,
|
||||||
};
|
};
|
||||||
|
use itertools::Itertools;
|
||||||
use lsp_types::{ClientCapabilities, MarkupKind};
|
use lsp_types::{ClientCapabilities, MarkupKind};
|
||||||
use project_model::{
|
use project_model::{
|
||||||
CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, UnsetTestCrates,
|
CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, UnsetTestCrates,
|
||||||
@ -31,9 +32,7 @@ use crate::{
|
|||||||
caps::completion_item_edit_resolve,
|
caps::completion_item_edit_resolve,
|
||||||
diagnostics::DiagnosticsMapConfig,
|
diagnostics::DiagnosticsMapConfig,
|
||||||
line_index::OffsetEncoding,
|
line_index::OffsetEncoding,
|
||||||
lsp_ext::supports_utf8,
|
lsp_ext::{self, supports_utf8, WorkspaceSymbolSearchKind, WorkspaceSymbolSearchScope},
|
||||||
lsp_ext::WorkspaceSymbolSearchScope,
|
|
||||||
lsp_ext::{self, WorkspaceSymbolSearchKind},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Defines the server-side configuration of the rust-analyzer. We generate
|
// Defines the server-side configuration of the rust-analyzer. We generate
|
||||||
@ -369,11 +368,11 @@ impl Default for ConfigData {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub caps: lsp_types::ClientCapabilities,
|
pub discovered_projects: Option<Vec<ProjectManifest>>,
|
||||||
|
caps: lsp_types::ClientCapabilities,
|
||||||
|
root_path: AbsPathBuf,
|
||||||
data: ConfigData,
|
data: ConfigData,
|
||||||
detached_files: Vec<AbsPathBuf>,
|
detached_files: Vec<AbsPathBuf>,
|
||||||
pub discovered_projects: Option<Vec<ProjectManifest>>,
|
|
||||||
pub root_path: AbsPathBuf,
|
|
||||||
snippets: Vec<Snippet>,
|
snippets: Vec<Snippet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,6 +504,27 @@ pub struct ClientCommandsConfig {
|
|||||||
pub trigger_parameter_hints: bool,
|
pub trigger_parameter_hints: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ConfigUpdateError {
|
||||||
|
errors: Vec<(String, serde_json::Error)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConfigUpdateError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let errors = self.errors.iter().format_with("\n", |(key, e), f| {
|
||||||
|
f(key)?;
|
||||||
|
f(&": ")?;
|
||||||
|
f(e)
|
||||||
|
});
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"rust-analyzer found {} invalid config value{}:\n{}",
|
||||||
|
self.errors.len(),
|
||||||
|
if self.errors.len() == 1 { "" } else { "s" },
|
||||||
|
errors
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
|
pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
|
||||||
Config {
|
Config {
|
||||||
@ -516,10 +536,8 @@ impl Config {
|
|||||||
snippets: Default::default(),
|
snippets: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update(
|
|
||||||
&mut self,
|
pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
|
||||||
mut json: serde_json::Value,
|
|
||||||
) -> Result<(), Vec<(String, serde_json::Error)>> {
|
|
||||||
tracing::info!("updating config from JSON: {:#}", json);
|
tracing::info!("updating config from JSON: {:#}", json);
|
||||||
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
|
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -556,13 +574,25 @@ impl Config {
|
|||||||
if errors.is_empty() {
|
if errors.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(errors)
|
Err(ConfigUpdateError { errors })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn json_schema() -> serde_json::Value {
|
pub fn json_schema() -> serde_json::Value {
|
||||||
ConfigData::json_schema()
|
ConfigData::json_schema()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn root_path(&self) -> &AbsPathBuf {
|
||||||
|
&self.root_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn caps(&self) -> &lsp_types::ClientCapabilities {
|
||||||
|
&self.caps
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn detached_files(&self) -> &[AbsPathBuf] {
|
||||||
|
&self.detached_files
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! try_ {
|
macro_rules! try_ {
|
||||||
@ -578,43 +608,31 @@ macro_rules! try_or {
|
|||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn linked_projects(&self) -> Vec<LinkedProject> {
|
pub fn linked_projects(&self) -> Vec<LinkedProject> {
|
||||||
if self.data.linkedProjects.is_empty() {
|
match self.data.linkedProjects.as_slice() {
|
||||||
self.discovered_projects
|
[] => match self.discovered_projects.as_ref() {
|
||||||
.as_ref()
|
Some(discovered_projects) => {
|
||||||
.into_iter()
|
discovered_projects.iter().cloned().map(LinkedProject::from).collect()
|
||||||
.flatten()
|
}
|
||||||
.cloned()
|
None => Vec::new(),
|
||||||
.map(LinkedProject::from)
|
},
|
||||||
.collect()
|
linked_projects => linked_projects
|
||||||
} else {
|
|
||||||
self.data
|
|
||||||
.linkedProjects
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|linked_project| {
|
.filter_map(|linked_project| match linked_project {
|
||||||
let res = match linked_project {
|
|
||||||
ManifestOrProjectJson::Manifest(it) => {
|
ManifestOrProjectJson::Manifest(it) => {
|
||||||
let path = self.root_path.join(it);
|
let path = self.root_path.join(it);
|
||||||
ProjectManifest::from_manifest_file(path)
|
ProjectManifest::from_manifest_file(path)
|
||||||
.map_err(|e| {
|
.map_err(|e| tracing::error!("failed to load linked project: {}", e))
|
||||||
tracing::error!("failed to load linked project: {}", e)
|
.ok()
|
||||||
})
|
.map(Into::into)
|
||||||
.ok()?
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
ManifestOrProjectJson::ProjectJson(it) => {
|
ManifestOrProjectJson::ProjectJson(it) => {
|
||||||
ProjectJson::new(&self.root_path, it.clone()).into()
|
Some(ProjectJson::new(&self.root_path, it.clone()).into())
|
||||||
}
|
}
|
||||||
};
|
|
||||||
Some(res)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detached_files(&self) -> &[AbsPathBuf] {
|
|
||||||
&self.detached_files
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn did_save_text_document_dynamic_registration(&self) -> bool {
|
pub fn did_save_text_document_dynamic_registration(&self) -> bool {
|
||||||
let caps =
|
let caps =
|
||||||
try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default());
|
try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default());
|
||||||
|
@ -9,7 +9,6 @@ use std::{
|
|||||||
use always_assert::always;
|
use always_assert::always;
|
||||||
use crossbeam_channel::{select, Receiver};
|
use crossbeam_channel::{select, Receiver};
|
||||||
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
|
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
|
||||||
use itertools::Itertools;
|
|
||||||
use lsp_server::{Connection, Notification, Request};
|
use lsp_server::{Connection, Notification, Request};
|
||||||
use lsp_types::notification::Notification as _;
|
use lsp_types::notification::Notification as _;
|
||||||
use vfs::{ChangeKind, FileId};
|
use vfs::{ChangeKind, FileId};
|
||||||
@ -747,16 +746,8 @@ impl GlobalState {
|
|||||||
// Note that json can be null according to the spec if the client can't
|
// Note that json can be null according to the spec if the client can't
|
||||||
// provide a configuration. This is handled in Config::update below.
|
// provide a configuration. This is handled in Config::update below.
|
||||||
let mut config = Config::clone(&*this.config);
|
let mut config = Config::clone(&*this.config);
|
||||||
if let Err(errors) = config.update(json.take()) {
|
if let Err(error) = config.update(json.take()) {
|
||||||
let errors = errors
|
this.show_message(lsp_types::MessageType::WARNING, error.to_string());
|
||||||
.iter()
|
|
||||||
.format_with("\n", |(key, e),f| {
|
|
||||||
f(key)?;
|
|
||||||
f(&": ")?;
|
|
||||||
f(e)
|
|
||||||
});
|
|
||||||
let msg= format!("Failed to deserialize config key(s):\n{}", errors);
|
|
||||||
this.show_message(lsp_types::MessageType::WARNING, msg);
|
|
||||||
}
|
}
|
||||||
this.update_configuration(config);
|
this.update_configuration(config);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user