From 69bbe79c5037eb3cd00744593d1836e45a6f56e1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 22 Aug 2019 14:44:16 +0300 Subject: implement feature flags --- crates/ra_ide_api/src/completion/presentation.rs | 5 +- crates/ra_ide_api/src/db.rs | 9 ++-- crates/ra_ide_api/src/feature_flags.rs | 67 ++++++++++++++++++++++++ crates/ra_ide_api/src/lib.rs | 16 ++++-- 4 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 crates/ra_ide_api/src/feature_flags.rs (limited to 'crates/ra_ide_api/src') diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 6878008d3..2b3f98482 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs @@ -118,7 +118,10 @@ impl Completions { .set_documentation(func.docs(ctx.db)) .detail(detail); // If not an import, add parenthesis automatically. - if ctx.use_item_syntax.is_none() && !ctx.is_call { + if ctx.use_item_syntax.is_none() + && !ctx.is_call + && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") + { tested_by!(inserts_parens_for_function_calls); let snippet = if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs index fc8252e4b..f2e6b8f12 100644 --- a/crates/ra_ide_api/src/db.rs +++ b/crates/ra_ide_api/src/db.rs @@ -7,7 +7,7 @@ use ra_db::{ use crate::{ symbol_index::{self, SymbolsDatabase}, - LineIndex, + FeatureFlags, LineIndex, }; #[salsa::database( @@ -22,6 +22,7 @@ use crate::{ #[derive(Debug)] pub(crate) struct RootDatabase { runtime: salsa::Runtime, + pub(crate) feature_flags: Arc, pub(crate) last_gc: time::Instant, pub(crate) last_gc_check: time::Instant, } @@ -46,16 +47,17 @@ impl salsa::Database for RootDatabase { impl Default for RootDatabase { fn default() -> RootDatabase { - RootDatabase::new(None) + RootDatabase::new(None, FeatureFlags::default()) } } impl RootDatabase { - pub fn new(lru_capacity: Option) -> RootDatabase { + pub fn new(lru_capacity: Option, feature_flags: FeatureFlags) -> RootDatabase { let mut db = RootDatabase { runtime: salsa::Runtime::default(), last_gc: time::Instant::now(), last_gc_check: time::Instant::now(), + feature_flags: Arc::new(feature_flags), }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); @@ -74,6 +76,7 @@ impl salsa::ParallelDatabase for RootDatabase { runtime: self.runtime.snapshot(self), last_gc: self.last_gc, last_gc_check: self.last_gc_check, + feature_flags: Arc::clone(&self.feature_flags), }) } } diff --git a/crates/ra_ide_api/src/feature_flags.rs b/crates/ra_ide_api/src/feature_flags.rs new file mode 100644 index 000000000..9f82ac71c --- /dev/null +++ b/crates/ra_ide_api/src/feature_flags.rs @@ -0,0 +1,67 @@ +use rustc_hash::FxHashMap; + +/// 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 +/// `ra_lsp_server`. This should be benign layering violation. +#[derive(Debug)] +pub struct FeatureFlags { + flags: FxHashMap, +} + +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), + ("notifications.workspace-loaded", 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), + } + } +} diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index fa4ae4379..514dcaf96 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -14,6 +14,7 @@ mod db; pub mod mock_analysis; mod symbol_index; mod change; +mod feature_flags; mod status; mod completion; @@ -63,6 +64,7 @@ pub use crate::{ completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, diagnostics::Severity, display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, + feature_flags::FeatureFlags, folding_ranges::{Fold, FoldKind}, hover::HoverResult, inlay_hints::{InlayHint, InlayKind}, @@ -247,13 +249,13 @@ pub struct AnalysisHost { impl Default for AnalysisHost { fn default() -> AnalysisHost { - AnalysisHost::new(None) + AnalysisHost::new(None, FeatureFlags::default()) } } impl AnalysisHost { - pub fn new(lru_capcity: Option) -> AnalysisHost { - AnalysisHost { db: db::RootDatabase::new(lru_capcity) } + pub fn new(lru_capcity: Option, feature_flags: FeatureFlags) -> AnalysisHost { + AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) } } /// Returns a snapshot of the current state, which you can query for /// semantic information. @@ -261,6 +263,10 @@ impl AnalysisHost { Analysis { db: self.db.snapshot() } } + pub fn feature_flags(&self) -> &FeatureFlags { + &self.db.feature_flags + } + /// Applies changes to the current state of the world. If there are /// outstanding snapshots, they will be canceled. pub fn apply_change(&mut self, change: AnalysisChange) { @@ -319,6 +325,10 @@ impl Analysis { (host.analysis(), file_id) } + pub fn feature_flags(&self) -> &FeatureFlags { + &self.db.feature_flags + } + /// Debug info about the current state of the analysis pub fn status(&self) -> Cancelable { self.with_db(|db| status::status(&*db)) -- cgit v1.2.3