mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
Centralize all config
This commit is contained in:
parent
1e012eb991
commit
a97e5eb85d
9 changed files with 153 additions and 330 deletions
|
@ -22,12 +22,22 @@ use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
|
|||
|
||||
pub use crate::conv::url_from_path_with_drive_lowercasing;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FlycheckConfig {
|
||||
CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> },
|
||||
CustomCommand { command: String, args: Vec<String> },
|
||||
}
|
||||
|
||||
impl Default for FlycheckConfig {
|
||||
fn default() -> Self {
|
||||
FlycheckConfig::CargoCommand {
|
||||
command: "check".to_string(),
|
||||
all_targets: true,
|
||||
extra_args: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Flycheck wraps the shared state and communication machinery used for
|
||||
/// running `cargo check` (or other compatible command) and providing
|
||||
/// diagnostics based on the output.
|
||||
|
|
|
@ -5,7 +5,7 @@ mod args;
|
|||
|
||||
use lsp_server::Connection;
|
||||
|
||||
use rust_analyzer::{cli, from_json, show_message, Result, ServerConfig};
|
||||
use rust_analyzer::{cli, from_json, Config, Result};
|
||||
|
||||
use crate::args::HelpPrinted;
|
||||
|
||||
|
@ -78,24 +78,18 @@ fn run_server() -> Result<()> {
|
|||
.filter(|workspaces| !workspaces.is_empty())
|
||||
.unwrap_or_else(|| vec![root]);
|
||||
|
||||
let server_config = initialize_params
|
||||
.initialization_options
|
||||
.and_then(|v| {
|
||||
from_json::<ServerConfig>("config", v)
|
||||
.map_err(|e| {
|
||||
log::error!("{}", e);
|
||||
show_message(lsp_types::MessageType::Error, e.to_string(), &connection.sender);
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let config = {
|
||||
let mut config = Config::default();
|
||||
if let Some(value) = &initialize_params.initialization_options {
|
||||
config.update(value);
|
||||
}
|
||||
if let Some(caps) = &initialize_params.capabilities.text_document {
|
||||
config.update_caps(caps);
|
||||
}
|
||||
config
|
||||
};
|
||||
|
||||
rust_analyzer::main_loop(
|
||||
workspace_roots,
|
||||
initialize_params.capabilities,
|
||||
server_config,
|
||||
connection,
|
||||
)?;
|
||||
rust_analyzer::main_loop(workspace_roots, config, connection)?;
|
||||
|
||||
log::info!("shutting down IO...");
|
||||
io_threads.join()?;
|
||||
|
|
|
@ -7,14 +7,11 @@
|
|||
//! configure the server itself, feature flags are passed into analysis, and
|
||||
//! tweak things like automatic insertion of `()` in completions.
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::feature_flags::FeatureFlags;
|
||||
use lsp_types::TextDocumentClientCapabilities;
|
||||
use ra_flycheck::FlycheckConfig;
|
||||
use ra_ide::{CompletionConfig, InlayHintsConfig};
|
||||
use ra_project_model::CargoFeatures;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
|
@ -61,171 +58,109 @@ impl Default for RustfmtConfig {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_config(
|
||||
config: &ServerConfig,
|
||||
text_document_caps: Option<&TextDocumentClientCapabilities>,
|
||||
) -> Config {
|
||||
let feature_flags = get_feature_flags(config);
|
||||
Config {
|
||||
publish_decorations: config.publish_decorations,
|
||||
publish_diagnostics: feature_flags.get("lsp.diagnostics"),
|
||||
notifications: NotificationsConfig {
|
||||
workspace_loaded: feature_flags.get("notifications.workspace-loaded"),
|
||||
cargo_toml_not_found: feature_flags.get("notifications.cargo-toml-not-found"),
|
||||
},
|
||||
supports_location_link: text_document_caps
|
||||
.and_then(|it| it.definition)
|
||||
.and_then(|it| it.link_support)
|
||||
.unwrap_or(false),
|
||||
line_folding_only: text_document_caps
|
||||
.and_then(|it| it.folding_range.as_ref())
|
||||
.and_then(|it| it.line_folding_only)
|
||||
.unwrap_or(false),
|
||||
inlay_hints: InlayHintsConfig {
|
||||
type_hints: config.inlay_hints_type,
|
||||
parameter_hints: config.inlay_hints_parameter,
|
||||
chaining_hints: config.inlay_hints_chaining,
|
||||
max_length: config.inlay_hints_max_length,
|
||||
},
|
||||
completion: CompletionConfig {
|
||||
enable_postfix_completions: feature_flags.get("completion.enable-postfix"),
|
||||
add_call_parenthesis: feature_flags.get("completion.insertion.add-call-parenthesis"),
|
||||
add_call_argument_snippets: feature_flags
|
||||
.get("completion.insertion.add-argument-snippets"),
|
||||
},
|
||||
call_info_full: feature_flags.get("call-info.full"),
|
||||
check: if config.cargo_watch_enable {
|
||||
Some(FlycheckConfig::CargoCommand {
|
||||
command: config.cargo_watch_command.clone(),
|
||||
all_targets: config.cargo_watch_all_targets,
|
||||
extra_args: config.cargo_watch_args.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
rustfmt: RustfmtConfig::Rustfmt { extra_args: config.rustfmt_args.clone() },
|
||||
vscode_lldb: config.vscode_lldb,
|
||||
proc_macro_srv: None, // FIXME: get this from config
|
||||
lru_capacity: config.lru_capacity,
|
||||
use_client_watching: config.use_client_watching,
|
||||
exclude_globs: config.exclude_globs.clone(),
|
||||
cargo: config.cargo_features.clone(),
|
||||
with_sysroot: config.with_sysroot,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_feature_flags(config: &ServerConfig) -> FeatureFlags {
|
||||
let mut ff = FeatureFlags::default();
|
||||
for (flag, &value) in &config.feature_flags {
|
||||
if ff.set(flag.as_str(), value).is_err() {
|
||||
log::error!("unknown feature flag: {:?}", flag);
|
||||
}
|
||||
}
|
||||
log::info!("feature_flags: {:#?}", ff);
|
||||
ff
|
||||
}
|
||||
|
||||
/// Client provided initialization options
|
||||
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase", default)]
|
||||
pub struct ServerConfig {
|
||||
/// Whether the client supports our custom highlighting publishing decorations.
|
||||
/// This is different to the highlightingOn setting, which is whether the user
|
||||
/// wants our custom highlighting to be used.
|
||||
///
|
||||
/// Defaults to `false`
|
||||
#[serde(deserialize_with = "nullable_bool_false")]
|
||||
pub publish_decorations: bool,
|
||||
|
||||
pub exclude_globs: Vec<String>,
|
||||
#[serde(deserialize_with = "nullable_bool_false")]
|
||||
pub use_client_watching: bool,
|
||||
|
||||
pub lru_capacity: Option<usize>,
|
||||
|
||||
#[serde(deserialize_with = "nullable_bool_true")]
|
||||
pub inlay_hints_type: bool,
|
||||
#[serde(deserialize_with = "nullable_bool_true")]
|
||||
pub inlay_hints_parameter: bool,
|
||||
#[serde(deserialize_with = "nullable_bool_true")]
|
||||
pub inlay_hints_chaining: bool,
|
||||
pub inlay_hints_max_length: Option<usize>,
|
||||
|
||||
pub cargo_watch_enable: bool,
|
||||
pub cargo_watch_args: Vec<String>,
|
||||
pub cargo_watch_command: String,
|
||||
pub cargo_watch_all_targets: bool,
|
||||
|
||||
/// For internal usage to make integrated tests faster.
|
||||
#[serde(deserialize_with = "nullable_bool_true")]
|
||||
pub with_sysroot: bool,
|
||||
|
||||
/// Fine grained feature flags to disable specific features.
|
||||
pub feature_flags: FxHashMap<String, bool>,
|
||||
|
||||
pub rustfmt_args: Vec<String>,
|
||||
|
||||
/// Cargo feature configurations.
|
||||
pub cargo_features: CargoFeatures,
|
||||
|
||||
/// Enabled if the vscode_lldb extension is available.
|
||||
pub vscode_lldb: bool,
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> ServerConfig {
|
||||
ServerConfig {
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Config {
|
||||
publish_decorations: false,
|
||||
exclude_globs: Vec::new(),
|
||||
use_client_watching: false,
|
||||
lru_capacity: None,
|
||||
inlay_hints_type: true,
|
||||
inlay_hints_parameter: true,
|
||||
inlay_hints_chaining: true,
|
||||
inlay_hints_max_length: None,
|
||||
cargo_watch_enable: true,
|
||||
cargo_watch_args: Vec::new(),
|
||||
cargo_watch_command: "check".to_string(),
|
||||
cargo_watch_all_targets: true,
|
||||
with_sysroot: true,
|
||||
feature_flags: FxHashMap::default(),
|
||||
cargo_features: Default::default(),
|
||||
rustfmt_args: Vec::new(),
|
||||
publish_diagnostics: true,
|
||||
notifications: NotificationsConfig {
|
||||
workspace_loaded: true,
|
||||
cargo_toml_not_found: true,
|
||||
},
|
||||
supports_location_link: false,
|
||||
line_folding_only: false,
|
||||
inlay_hints: InlayHintsConfig {
|
||||
type_hints: true,
|
||||
parameter_hints: true,
|
||||
chaining_hints: true,
|
||||
max_length: None,
|
||||
},
|
||||
completion: CompletionConfig {
|
||||
enable_postfix_completions: true,
|
||||
add_call_parenthesis: true,
|
||||
add_call_argument_snippets: true,
|
||||
},
|
||||
call_info_full: true,
|
||||
rustfmt: RustfmtConfig::default(),
|
||||
check: Some(FlycheckConfig::default()),
|
||||
vscode_lldb: false,
|
||||
proc_macro_srv: None,
|
||||
lru_capacity: None,
|
||||
use_client_watching: false,
|
||||
exclude_globs: Vec::new(),
|
||||
cargo: CargoFeatures::default(),
|
||||
with_sysroot: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes a null value to a bool false by default
|
||||
fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::deserialize(deserializer)?;
|
||||
Ok(opt.unwrap_or(false))
|
||||
}
|
||||
impl Config {
|
||||
#[rustfmt::skip]
|
||||
pub fn update(&mut self, value: &serde_json::Value) {
|
||||
let line_folding_only = self.line_folding_only;
|
||||
let supports_location_link = self.supports_location_link;
|
||||
*self = Default::default();
|
||||
self.line_folding_only = line_folding_only;
|
||||
self.supports_location_link = supports_location_link;
|
||||
|
||||
/// Deserializes a null value to a bool true by default
|
||||
fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::deserialize(deserializer)?;
|
||||
Ok(opt.unwrap_or(true))
|
||||
}
|
||||
set(value, "publishDecorations", &mut self.publish_decorations);
|
||||
set(value, "excludeGlobs", &mut self.exclude_globs);
|
||||
set(value, "useClientWatching", &mut self.use_client_watching);
|
||||
set(value, "lruCapacity", &mut self.lru_capacity);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
set(value, "inlayHintsType", &mut self.inlay_hints.type_hints);
|
||||
set(value, "inlayHintsParameter", &mut self.inlay_hints.parameter_hints);
|
||||
set(value, "inlayHintsChaining", &mut self.inlay_hints.chaining_hints);
|
||||
set(value, "inlayHintsMaxLength", &mut self.inlay_hints.max_length);
|
||||
|
||||
#[test]
|
||||
fn deserialize_init_options_defaults() {
|
||||
// check that null == default for both fields
|
||||
let default = ServerConfig::default();
|
||||
assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap());
|
||||
assert_eq!(
|
||||
default,
|
||||
serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap()
|
||||
);
|
||||
if let Some(false) = get(value, "cargo_watch_enable") {
|
||||
self.check = None
|
||||
} else {
|
||||
if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check
|
||||
{
|
||||
set(value, "cargoWatchArgs", extra_args);
|
||||
set(value, "cargoWatchCommand", command);
|
||||
set(value, "cargoWatchAllTargets", all_targets);
|
||||
}
|
||||
};
|
||||
|
||||
set(value, "withSysroot", &mut self.with_sysroot);
|
||||
if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
|
||||
set(value, "rustfmtArgs", extra_args);
|
||||
}
|
||||
|
||||
set(value, "cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features);
|
||||
set(value, "cargoFeatures/allFeatures", &mut self.cargo.all_features);
|
||||
set(value, "cargoFeatures/features", &mut self.cargo.features);
|
||||
set(value, "cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
|
||||
|
||||
set(value, "vscodeLldb", &mut self.vscode_lldb);
|
||||
|
||||
set(value, "featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
|
||||
set(value, "featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded);
|
||||
set(value, "featureFlags/notifications.cargo-toml-not-found", &mut self.notifications.cargo_toml_not_found);
|
||||
set(value, "featureFlags/completion.enable-postfix", &mut self.completion.enable_postfix_completions);
|
||||
set(value, "featureFlags/completion.insertion.add-call-parenthesis", &mut self.completion.add_call_parenthesis);
|
||||
set(value, "featureFlags/completion.insertion.add-argument-snippets", &mut self.completion.add_call_argument_snippets);
|
||||
set(value, "featureFlags/call-info.full", &mut self.call_info_full);
|
||||
|
||||
fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
|
||||
value.pointer(pointer).and_then(|it| T::deserialize(it).ok())
|
||||
}
|
||||
|
||||
fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) {
|
||||
if let Some(new_value) = get(value, pointer) {
|
||||
*slot = new_value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) {
|
||||
if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) {
|
||||
self.supports_location_link = value;
|
||||
}
|
||||
if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) {
|
||||
self.line_folding_only = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
//! See docs for `FeatureFlags`.
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
// FIXME: looks like a much better design is to pass options to each call,
|
||||
// rather than to have a global ambient feature flags -- that way, the clients
|
||||
// can issue two successive calls with different options.
|
||||
|
||||
/// Feature flags hold fine-grained toggles for all *user-visible* features of
|
||||
/// rust-analyzer.
|
||||
///
|
||||
/// The exists such that users are able to disable any annoying feature (and,
|
||||
/// with many users and many features, some features are bound to be annoying
|
||||
/// for some users)
|
||||
///
|
||||
/// Note that we purposefully use run-time checked strings, and not something
|
||||
/// checked at compile time, to keep things simple and flexible.
|
||||
///
|
||||
/// Also note that, at the moment, `FeatureFlags` also store features for
|
||||
/// `rust-analyzer`. This should be benign layering violation.
|
||||
#[derive(Debug)]
|
||||
pub struct FeatureFlags {
|
||||
flags: FxHashMap<String, bool>,
|
||||
}
|
||||
|
||||
impl FeatureFlags {
|
||||
fn new(flags: &[(&str, bool)]) -> FeatureFlags {
|
||||
let flags = flags
|
||||
.iter()
|
||||
.map(|&(name, value)| {
|
||||
check_flag_name(name);
|
||||
(name.to_string(), value)
|
||||
})
|
||||
.collect();
|
||||
FeatureFlags { flags }
|
||||
}
|
||||
|
||||
pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> {
|
||||
match self.flags.get_mut(flag) {
|
||||
None => Err(()),
|
||||
Some(slot) => {
|
||||
*slot = value;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, flag: &str) -> bool {
|
||||
match self.flags.get(flag) {
|
||||
None => panic!("unknown flag: {:?}", flag),
|
||||
Some(value) => *value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FeatureFlags {
|
||||
fn default() -> FeatureFlags {
|
||||
FeatureFlags::new(&[
|
||||
("lsp.diagnostics", true),
|
||||
("completion.insertion.add-call-parenthesis", true),
|
||||
("completion.insertion.add-argument-snippets", true),
|
||||
("completion.enable-postfix", true),
|
||||
("call-info.full", true),
|
||||
("notifications.workspace-loaded", true),
|
||||
("notifications.cargo-toml-not-found", true),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
fn check_flag_name(flag: &str) {
|
||||
for c in flag.bytes() {
|
||||
match c {
|
||||
b'a'..=b'z' | b'-' | b'.' => (),
|
||||
_ => panic!("flag name does not match conventions: {:?}", flag),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,14 +37,13 @@ mod config;
|
|||
mod world;
|
||||
mod diagnostics;
|
||||
mod semantic_tokens;
|
||||
mod feature_flags;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
||||
pub use crate::{
|
||||
config::Config,
|
||||
caps::server_capabilities,
|
||||
config::ServerConfig,
|
||||
main_loop::LspError,
|
||||
main_loop::{main_loop, show_message},
|
||||
};
|
||||
|
|
|
@ -17,9 +17,8 @@ use std::{
|
|||
use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
|
||||
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
|
||||
use lsp_types::{
|
||||
ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress,
|
||||
WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
|
||||
WorkDoneProgressReport,
|
||||
NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
|
||||
WorkDoneProgressEnd, WorkDoneProgressReport,
|
||||
};
|
||||
use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask};
|
||||
use ra_ide::{Canceled, FileId, LibraryData, SourceRootId};
|
||||
|
@ -31,7 +30,7 @@ use serde::{de::DeserializeOwned, Serialize};
|
|||
use threadpool::ThreadPool;
|
||||
|
||||
use crate::{
|
||||
config::get_config,
|
||||
config::Config,
|
||||
diagnostics::DiagnosticTask,
|
||||
main_loop::{
|
||||
pending_requests::{PendingRequest, PendingRequests},
|
||||
|
@ -39,7 +38,7 @@ use crate::{
|
|||
},
|
||||
req,
|
||||
world::{WorldSnapshot, WorldState},
|
||||
Result, ServerConfig,
|
||||
Result,
|
||||
};
|
||||
use req::ConfigurationParams;
|
||||
|
||||
|
@ -65,14 +64,7 @@ impl fmt::Display for LspError {
|
|||
|
||||
impl Error for LspError {}
|
||||
|
||||
pub fn main_loop(
|
||||
ws_roots: Vec<PathBuf>,
|
||||
client_caps: ClientCapabilities,
|
||||
config: ServerConfig,
|
||||
connection: Connection,
|
||||
) -> Result<()> {
|
||||
let text_document_caps = client_caps.text_document.as_ref();
|
||||
let config = get_config(&config, text_document_caps);
|
||||
pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> {
|
||||
log::info!("initial config: {:#?}", config);
|
||||
|
||||
// Windows scheduler implements priority boosts: if thread waits for an
|
||||
|
@ -205,7 +197,6 @@ pub fn main_loop(
|
|||
&task_sender,
|
||||
&libdata_sender,
|
||||
&connection,
|
||||
text_document_caps,
|
||||
&mut world_state,
|
||||
&mut loop_state,
|
||||
event,
|
||||
|
@ -316,7 +307,6 @@ fn loop_turn(
|
|||
task_sender: &Sender<Task>,
|
||||
libdata_sender: &Sender<LibraryData>,
|
||||
connection: &Connection,
|
||||
text_document_caps: Option<&TextDocumentClientCapabilities>,
|
||||
world_state: &mut WorldState,
|
||||
loop_state: &mut LoopState,
|
||||
event: Event,
|
||||
|
@ -370,27 +360,14 @@ fn loop_turn(
|
|||
log::debug!("config update response: '{:?}", resp);
|
||||
let Response { error, result, .. } = resp;
|
||||
|
||||
match (
|
||||
error,
|
||||
result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
|
||||
) {
|
||||
match (error, result) {
|
||||
(Some(err), _) => {
|
||||
log::error!("failed to fetch the server settings: {:?}", err)
|
||||
}
|
||||
(None, Some(Ok(new_config))) => {
|
||||
let new_config = new_config
|
||||
.first()
|
||||
.expect(
|
||||
"the client is expected to always send a non-empty config data",
|
||||
)
|
||||
.to_owned();
|
||||
world_state.update_configuration(
|
||||
new_config.lru_capacity,
|
||||
get_config(&new_config, text_document_caps),
|
||||
);
|
||||
}
|
||||
(None, Some(Err(e))) => {
|
||||
log::error!("failed to parse client config response: {}", e)
|
||||
(None, Some(new_config)) => {
|
||||
let mut config = world_state.config.clone();
|
||||
config.update(&new_config);
|
||||
world_state.update_configuration(config);
|
||||
}
|
||||
(None, None) => {
|
||||
log::error!("received empty server settings response from the client")
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
|||
use crossbeam_channel::{unbounded, Receiver};
|
||||
use lsp_types::Url;
|
||||
use parking_lot::RwLock;
|
||||
use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck};
|
||||
use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig};
|
||||
use ra_ide::{
|
||||
Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
|
||||
};
|
||||
|
@ -30,9 +30,7 @@ use crate::{
|
|||
use ra_db::ExternSourceId;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<Flycheck> {
|
||||
let check_config = config.check.as_ref()?;
|
||||
|
||||
fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
|
||||
// FIXME: Figure out the multi-workspace situation
|
||||
workspaces
|
||||
.iter()
|
||||
|
@ -42,7 +40,7 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F
|
|||
})
|
||||
.map(|cargo| {
|
||||
let cargo_project_root = cargo.workspace_root().to_path_buf();
|
||||
Some(Flycheck::new(check_config.clone(), cargo_project_root))
|
||||
Some(Flycheck::new(config.clone(), cargo_project_root))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
|
||||
|
@ -187,7 +185,7 @@ impl WorldState {
|
|||
});
|
||||
change.set_crate_graph(crate_graph);
|
||||
|
||||
let flycheck = create_flycheck(&workspaces, &config);
|
||||
let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c));
|
||||
|
||||
let mut analysis_host = AnalysisHost::new(lru_capacity);
|
||||
analysis_host.apply_change(change);
|
||||
|
@ -204,9 +202,13 @@ impl WorldState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_configuration(&mut self, lru_capacity: Option<usize>, config: Config) {
|
||||
self.analysis_host.update_lru_capacity(lru_capacity);
|
||||
self.flycheck = create_flycheck(&self.workspaces, &config);
|
||||
pub fn update_configuration(&mut self, config: Config) {
|
||||
self.analysis_host.update_lru_capacity(config.lru_capacity);
|
||||
if config.check != self.config.check {
|
||||
self.flycheck =
|
||||
config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it));
|
||||
}
|
||||
|
||||
self.config = config;
|
||||
}
|
||||
|
||||
|
|
|
@ -615,7 +615,7 @@ fn main() { message(); }
|
|||
"###,
|
||||
)
|
||||
.with_config(|config| {
|
||||
config.cargo_features.load_out_dirs_from_check = true;
|
||||
config.cargo.load_out_dirs_from_check = true;
|
||||
})
|
||||
.server();
|
||||
server.wait_until_workspace_is_loaded();
|
||||
|
|
|
@ -11,8 +11,7 @@ use lsp_server::{Connection, Message, Notification, Request};
|
|||
use lsp_types::{
|
||||
notification::{DidOpenTextDocument, Exit},
|
||||
request::Shutdown,
|
||||
ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities,
|
||||
TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
|
||||
DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde_json::{to_string_pretty, Value};
|
||||
|
@ -20,14 +19,14 @@ use tempfile::TempDir;
|
|||
use test_utils::{find_mismatch, parse_fixture};
|
||||
|
||||
use req::{ProgressParams, ProgressParamsValue};
|
||||
use rust_analyzer::{main_loop, req, ServerConfig};
|
||||
use rust_analyzer::{main_loop, req, Config};
|
||||
|
||||
pub struct Project<'a> {
|
||||
fixture: &'a str,
|
||||
with_sysroot: bool,
|
||||
tmp_dir: Option<TempDir>,
|
||||
roots: Vec<PathBuf>,
|
||||
config: Option<Box<dyn Fn(&mut ServerConfig)>>,
|
||||
config: Option<Box<dyn Fn(&mut Config)>>,
|
||||
}
|
||||
|
||||
impl<'a> Project<'a> {
|
||||
|
@ -50,7 +49,7 @@ impl<'a> Project<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> {
|
||||
pub fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> {
|
||||
self.config = Some(Box::new(config));
|
||||
self
|
||||
}
|
||||
|
@ -78,8 +77,11 @@ impl<'a> Project<'a> {
|
|||
|
||||
let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
|
||||
|
||||
let mut config =
|
||||
ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() };
|
||||
let mut config = Config {
|
||||
supports_location_link: true,
|
||||
with_sysroot: self.with_sysroot,
|
||||
..Config::default()
|
||||
};
|
||||
|
||||
if let Some(f) = &self.config {
|
||||
f(&mut config)
|
||||
|
@ -105,7 +107,7 @@ pub struct Server {
|
|||
impl Server {
|
||||
fn new(
|
||||
dir: TempDir,
|
||||
config: ServerConfig,
|
||||
config: Config,
|
||||
roots: Vec<PathBuf>,
|
||||
files: Vec<(PathBuf, String)>,
|
||||
) -> Server {
|
||||
|
@ -116,26 +118,7 @@ impl Server {
|
|||
|
||||
let _thread = jod_thread::Builder::new()
|
||||
.name("test server".to_string())
|
||||
.spawn(move || {
|
||||
main_loop(
|
||||
roots,
|
||||
ClientCapabilities {
|
||||
workspace: None,
|
||||
text_document: Some(TextDocumentClientCapabilities {
|
||||
definition: Some(GotoCapability {
|
||||
dynamic_registration: None,
|
||||
link_support: Some(true),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
window: None,
|
||||
experimental: None,
|
||||
},
|
||||
config,
|
||||
connection,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.spawn(move || main_loop(roots, config, connection).unwrap())
|
||||
.expect("failed to spawn a thread");
|
||||
|
||||
let res =
|
||||
|
|
Loading…
Reference in a new issue