aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-08-22 12:44:16 +0100
committerAleksey Kladov <[email protected]>2019-08-22 13:07:31 +0100
commit69bbe79c5037eb3cd00744593d1836e45a6f56e1 (patch)
treefc48327d9d70b320c60e6c9fc19fdc5bdbff88f9
parent4dd5afb7fe2eb20748ade9141e74b04f5dd2f922 (diff)
implement feature flags
-rw-r--r--crates/ra_batch/src/lib.rs4
-rw-r--r--crates/ra_ide_api/src/completion/presentation.rs5
-rw-r--r--crates/ra_ide_api/src/db.rs9
-rw-r--r--crates/ra_ide_api/src/feature_flags.rs67
-rw-r--r--crates/ra_ide_api/src/lib.rs16
-rw-r--r--crates/ra_lsp_server/src/config.rs13
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs42
-rw-r--r--crates/ra_lsp_server/src/world.rs15
-rw-r--r--editors/code/package.json8
-rw-r--r--editors/code/src/config.ts4
-rw-r--r--editors/code/src/server.ts3
11 files changed, 150 insertions, 36 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs
index 0db751465..f458ea300 100644
--- a/crates/ra_batch/src/lib.rs
+++ b/crates/ra_batch/src/lib.rs
@@ -3,7 +3,7 @@ use std::{collections::HashSet, error::Error, path::Path};
3use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
4 4
5use ra_db::{CrateGraph, FileId, SourceRootId}; 5use ra_db::{CrateGraph, FileId, SourceRootId};
6use ra_ide_api::{AnalysisChange, AnalysisHost}; 6use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
7use ra_project_model::{PackageRoot, ProjectWorkspace}; 7use ra_project_model::{PackageRoot, ProjectWorkspace};
8use ra_vfs::{RootEntry, Vfs, VfsChange}; 8use ra_vfs::{RootEntry, Vfs, VfsChange};
9use ra_vfs_glob::RustPackageFilterBuilder; 9use ra_vfs_glob::RustPackageFilterBuilder;
@@ -63,7 +63,7 @@ pub fn load(
63 vfs: &mut Vfs, 63 vfs: &mut Vfs,
64) -> AnalysisHost { 64) -> AnalysisHost {
65 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 65 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
66 let mut host = AnalysisHost::new(lru_cap); 66 let mut host = AnalysisHost::new(lru_cap, FeatureFlags::default());
67 let mut analysis_change = AnalysisChange::new(); 67 let mut analysis_change = AnalysisChange::new();
68 analysis_change.set_crate_graph(crate_graph); 68 analysis_change.set_crate_graph(crate_graph);
69 69
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 {
118 .set_documentation(func.docs(ctx.db)) 118 .set_documentation(func.docs(ctx.db))
119 .detail(detail); 119 .detail(detail);
120 // If not an import, add parenthesis automatically. 120 // If not an import, add parenthesis automatically.
121 if ctx.use_item_syntax.is_none() && !ctx.is_call { 121 if ctx.use_item_syntax.is_none()
122 && !ctx.is_call
123 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
124 {
122 tested_by!(inserts_parens_for_function_calls); 125 tested_by!(inserts_parens_for_function_calls);
123 let snippet = 126 let snippet =
124 if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 { 127 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::{
7 7
8use crate::{ 8use crate::{
9 symbol_index::{self, SymbolsDatabase}, 9 symbol_index::{self, SymbolsDatabase},
10 LineIndex, 10 FeatureFlags, LineIndex,
11}; 11};
12 12
13#[salsa::database( 13#[salsa::database(
@@ -22,6 +22,7 @@ use crate::{
22#[derive(Debug)] 22#[derive(Debug)]
23pub(crate) struct RootDatabase { 23pub(crate) struct RootDatabase {
24 runtime: salsa::Runtime<RootDatabase>, 24 runtime: salsa::Runtime<RootDatabase>,
25 pub(crate) feature_flags: Arc<FeatureFlags>,
25 pub(crate) last_gc: time::Instant, 26 pub(crate) last_gc: time::Instant,
26 pub(crate) last_gc_check: time::Instant, 27 pub(crate) last_gc_check: time::Instant,
27} 28}
@@ -46,16 +47,17 @@ impl salsa::Database for RootDatabase {
46 47
47impl Default for RootDatabase { 48impl Default for RootDatabase {
48 fn default() -> RootDatabase { 49 fn default() -> RootDatabase {
49 RootDatabase::new(None) 50 RootDatabase::new(None, FeatureFlags::default())
50 } 51 }
51} 52}
52 53
53impl RootDatabase { 54impl RootDatabase {
54 pub fn new(lru_capacity: Option<usize>) -> RootDatabase { 55 pub fn new(lru_capacity: Option<usize>, feature_flags: FeatureFlags) -> RootDatabase {
55 let mut db = RootDatabase { 56 let mut db = RootDatabase {
56 runtime: salsa::Runtime::default(), 57 runtime: salsa::Runtime::default(),
57 last_gc: time::Instant::now(), 58 last_gc: time::Instant::now(),
58 last_gc_check: time::Instant::now(), 59 last_gc_check: time::Instant::now(),
60 feature_flags: Arc::new(feature_flags),
59 }; 61 };
60 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 62 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
61 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 63 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -74,6 +76,7 @@ impl salsa::ParallelDatabase for RootDatabase {
74 runtime: self.runtime.snapshot(self), 76 runtime: self.runtime.snapshot(self),
75 last_gc: self.last_gc, 77 last_gc: self.last_gc,
76 last_gc_check: self.last_gc_check, 78 last_gc_check: self.last_gc_check,
79 feature_flags: Arc::clone(&self.feature_flags),
77 }) 80 })
78 } 81 }
79} 82}
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 @@
1use rustc_hash::FxHashMap;
2
3/// Feature flags hold fine-grained toggles for all *user-visible* features of
4/// rust-analyzer.
5///
6/// The exists such that users are able to disable any annoying feature (and,
7/// with many users and many features, some features are bound to be annoying
8/// for some users)
9///
10/// Note that we purposefully use run-time checked strings, and not something
11/// checked at compile time, to keep things simple and flexible.
12///
13/// Also note that, at the moment, `FeatureFlags` also store features for
14/// `ra_lsp_server`. This should be benign layering violation.
15#[derive(Debug)]
16pub struct FeatureFlags {
17 flags: FxHashMap<String, bool>,
18}
19
20impl FeatureFlags {
21 fn new(flags: &[(&str, bool)]) -> FeatureFlags {
22 let flags = flags
23 .iter()
24 .map(|&(name, value)| {
25 check_flag_name(name);
26 (name.to_string(), value)
27 })
28 .collect();
29 FeatureFlags { flags }
30 }
31
32 pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> {
33 match self.flags.get_mut(flag) {
34 None => Err(()),
35 Some(slot) => {
36 *slot = value;
37 Ok(())
38 }
39 }
40 }
41
42 pub fn get(&self, flag: &str) -> bool {
43 match self.flags.get(flag) {
44 None => panic!("unknown flag: {:?}", flag),
45 Some(value) => *value,
46 }
47 }
48}
49
50impl Default for FeatureFlags {
51 fn default() -> FeatureFlags {
52 FeatureFlags::new(&[
53 ("lsp.diagnostics", true),
54 ("completion.insertion.add-call-parenthesis", true),
55 ("notifications.workspace-loaded", true),
56 ])
57 }
58}
59
60fn check_flag_name(flag: &str) {
61 for c in flag.bytes() {
62 match c {
63 b'a'..=b'z' | b'-' | b'.' => (),
64 _ => panic!("flag name does not match conventions: {:?}", flag),
65 }
66 }
67}
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;
14pub mod mock_analysis; 14pub mod mock_analysis;
15mod symbol_index; 15mod symbol_index;
16mod change; 16mod change;
17mod feature_flags;
17 18
18mod status; 19mod status;
19mod completion; 20mod completion;
@@ -63,6 +64,7 @@ pub use crate::{
63 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 64 completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
64 diagnostics::Severity, 65 diagnostics::Severity,
65 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 66 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67 feature_flags::FeatureFlags,
66 folding_ranges::{Fold, FoldKind}, 68 folding_ranges::{Fold, FoldKind},
67 hover::HoverResult, 69 hover::HoverResult,
68 inlay_hints::{InlayHint, InlayKind}, 70 inlay_hints::{InlayHint, InlayKind},
@@ -247,13 +249,13 @@ pub struct AnalysisHost {
247 249
248impl Default for AnalysisHost { 250impl Default for AnalysisHost {
249 fn default() -> AnalysisHost { 251 fn default() -> AnalysisHost {
250 AnalysisHost::new(None) 252 AnalysisHost::new(None, FeatureFlags::default())
251 } 253 }
252} 254}
253 255
254impl AnalysisHost { 256impl AnalysisHost {
255 pub fn new(lru_capcity: Option<usize>) -> AnalysisHost { 257 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost {
256 AnalysisHost { db: db::RootDatabase::new(lru_capcity) } 258 AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) }
257 } 259 }
258 /// Returns a snapshot of the current state, which you can query for 260 /// Returns a snapshot of the current state, which you can query for
259 /// semantic information. 261 /// semantic information.
@@ -261,6 +263,10 @@ impl AnalysisHost {
261 Analysis { db: self.db.snapshot() } 263 Analysis { db: self.db.snapshot() }
262 } 264 }
263 265
266 pub fn feature_flags(&self) -> &FeatureFlags {
267 &self.db.feature_flags
268 }
269
264 /// Applies changes to the current state of the world. If there are 270 /// Applies changes to the current state of the world. If there are
265 /// outstanding snapshots, they will be canceled. 271 /// outstanding snapshots, they will be canceled.
266 pub fn apply_change(&mut self, change: AnalysisChange) { 272 pub fn apply_change(&mut self, change: AnalysisChange) {
@@ -319,6 +325,10 @@ impl Analysis {
319 (host.analysis(), file_id) 325 (host.analysis(), file_id)
320 } 326 }
321 327
328 pub fn feature_flags(&self) -> &FeatureFlags {
329 &self.db.feature_flags
330 }
331
322 /// Debug info about the current state of the analysis 332 /// Debug info about the current state of the analysis
323 pub fn status(&self) -> Cancelable<String> { 333 pub fn status(&self) -> Cancelable<String> {
324 self.with_db(|db| status::status(&*db)) 334 self.with_db(|db| status::status(&*db))
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs
index 71838b89c..5c5ae3e18 100644
--- a/crates/ra_lsp_server/src/config.rs
+++ b/crates/ra_lsp_server/src/config.rs
@@ -1,3 +1,5 @@
1use rustc_hash::FxHashMap;
2
1use serde::{Deserialize, Deserializer}; 3use serde::{Deserialize, Deserializer};
2 4
3/// Client provided initialization options 5/// Client provided initialization options
@@ -12,12 +14,6 @@ pub struct ServerConfig {
12 #[serde(deserialize_with = "nullable_bool_false")] 14 #[serde(deserialize_with = "nullable_bool_false")]
13 pub publish_decorations: bool, 15 pub publish_decorations: bool,
14 16
15 /// Whether or not the workspace loaded notification should be sent
16 ///
17 /// Defaults to `true`
18 #[serde(deserialize_with = "nullable_bool_true")]
19 pub show_workspace_loaded: bool,
20
21 pub exclude_globs: Vec<String>, 17 pub exclude_globs: Vec<String>,
22 18
23 pub lru_capacity: Option<usize>, 19 pub lru_capacity: Option<usize>,
@@ -25,16 +21,19 @@ pub struct ServerConfig {
25 /// For internal usage to make integrated tests faster. 21 /// For internal usage to make integrated tests faster.
26 #[serde(deserialize_with = "nullable_bool_true")] 22 #[serde(deserialize_with = "nullable_bool_true")]
27 pub with_sysroot: bool, 23 pub with_sysroot: bool,
24
25 /// Fine grained feature flags to disable specific features.
26 pub feature_flags: FxHashMap<String, bool>,
28} 27}
29 28
30impl Default for ServerConfig { 29impl Default for ServerConfig {
31 fn default() -> ServerConfig { 30 fn default() -> ServerConfig {
32 ServerConfig { 31 ServerConfig {
33 publish_decorations: false, 32 publish_decorations: false,
34 show_workspace_loaded: true,
35 exclude_globs: Vec::new(), 33 exclude_globs: Vec::new(),
36 lru_capacity: None, 34 lru_capacity: None,
37 with_sysroot: true, 35 with_sysroot: true,
36 feature_flags: FxHashMap::default(),
38 } 37 }
39 } 38 }
40} 39}
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index c0395c6d8..ce25ff162 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -9,7 +9,7 @@ use gen_lsp_server::{
9 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse, 9 handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
10}; 10};
11use lsp_types::{ClientCapabilities, NumberOrString}; 11use lsp_types::{ClientCapabilities, NumberOrString};
12use ra_ide_api::{Canceled, FileId, LibraryData}; 12use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData};
13use ra_prof::profile; 13use ra_prof::profile;
14use ra_vfs::VfsTask; 14use ra_vfs::VfsTask;
15use serde::{de::DeserializeOwned, Serialize}; 15use serde::{de::DeserializeOwned, Serialize};
@@ -56,7 +56,7 @@ pub fn main_loop(
56 msg_receiver: &Receiver<RawMessage>, 56 msg_receiver: &Receiver<RawMessage>,
57 msg_sender: &Sender<RawMessage>, 57 msg_sender: &Sender<RawMessage>,
58) -> Result<()> { 58) -> Result<()> {
59 log::debug!("server_config: {:?}", config); 59 log::info!("server_config: {:#?}", config);
60 // FIXME: support dynamic workspace loading. 60 // FIXME: support dynamic workspace loading.
61 let workspaces = { 61 let workspaces = {
62 let ws_worker = workspace_loader(config.with_sysroot); 62 let ws_worker = workspace_loader(config.with_sysroot);
@@ -83,6 +83,21 @@ pub fn main_loop(
83 .iter() 83 .iter()
84 .map(|glob| ra_vfs_glob::Glob::new(glob)) 84 .map(|glob| ra_vfs_glob::Glob::new(glob))
85 .collect::<std::result::Result<Vec<_>, _>>()?; 85 .collect::<std::result::Result<Vec<_>, _>>()?;
86 let feature_flags = {
87 let mut ff = FeatureFlags::default();
88 for (flag, value) in config.feature_flags {
89 if let Err(_) = ff.set(flag.as_str(), value) {
90 log::error!("unknown feature flag: {:?}", flag);
91 show_message(
92 req::MessageType::Error,
93 format!("unknown feature flag: {:?}", flag),
94 msg_sender,
95 );
96 }
97 }
98 ff
99 };
100 log::info!("feature_flags: {:#?}", feature_flags);
86 let mut state = WorldState::new( 101 let mut state = WorldState::new(
87 ws_roots, 102 ws_roots,
88 workspaces, 103 workspaces,
@@ -90,13 +105,13 @@ pub fn main_loop(
90 &globs, 105 &globs,
91 Options { 106 Options {
92 publish_decorations: config.publish_decorations, 107 publish_decorations: config.publish_decorations,
93 show_workspace_loaded: config.show_workspace_loaded,
94 supports_location_link: client_caps 108 supports_location_link: client_caps
95 .text_document 109 .text_document
96 .and_then(|it| it.definition) 110 .and_then(|it| it.definition)
97 .and_then(|it| it.link_support) 111 .and_then(|it| it.link_support)
98 .unwrap_or(false), 112 .unwrap_or(false),
99 }, 113 },
114 feature_flags,
100 ); 115 );
101 116
102 let pool = ThreadPool::new(THREADPOOL_SIZE); 117 let pool = ThreadPool::new(THREADPOOL_SIZE);
@@ -276,7 +291,7 @@ fn main_loop_inner(
276 && in_flight_libraries == 0 291 && in_flight_libraries == 0
277 { 292 {
278 let n_packages: usize = state.workspaces.iter().map(|it| it.n_packages()).sum(); 293 let n_packages: usize = state.workspaces.iter().map(|it| it.n_packages()).sum();
279 if state.options.show_workspace_loaded { 294 if state.feature_flags().get("notifications.workspace-loaded") {
280 let msg = format!("workspace loaded, {} rust packages", n_packages); 295 let msg = format!("workspace loaded, {} rust packages", n_packages);
281 show_message(req::MessageType::Info, msg, msg_sender); 296 show_message(req::MessageType::Info, msg, msg_sender);
282 } 297 }
@@ -587,17 +602,20 @@ fn update_file_notifications_on_threadpool(
587 subscriptions: Vec<FileId>, 602 subscriptions: Vec<FileId>,
588) { 603) {
589 log::trace!("updating notifications for {:?}", subscriptions); 604 log::trace!("updating notifications for {:?}", subscriptions);
605 let publish_diagnostics = world.feature_flags().get("lsp.diagnostics");
590 pool.execute(move || { 606 pool.execute(move || {
591 for file_id in subscriptions { 607 for file_id in subscriptions {
592 match handlers::publish_diagnostics(&world, file_id) { 608 if publish_diagnostics {
593 Err(e) => { 609 match handlers::publish_diagnostics(&world, file_id) {
594 if !is_canceled(&e) { 610 Err(e) => {
595 log::error!("failed to compute diagnostics: {:?}", e); 611 if !is_canceled(&e) {
612 log::error!("failed to compute diagnostics: {:?}", e);
613 }
614 }
615 Ok(params) => {
616 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
617 sender.send(Task::Notify(not)).unwrap();
596 } 618 }
597 }
598 Ok(params) => {
599 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
600 sender.send(Task::Notify(not)).unwrap();
601 } 619 }
602 } 620 }
603 if publish_decorations { 621 if publish_decorations {
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 10f96812f..6696dff71 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -7,7 +7,8 @@ use gen_lsp_server::ErrorCode;
7use lsp_types::Url; 7use lsp_types::Url;
8use parking_lot::RwLock; 8use parking_lot::RwLock;
9use ra_ide_api::{ 9use ra_ide_api::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData,
11 SourceRootId,
11}; 12};
12use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot}; 13use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot};
13use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 14use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
@@ -22,7 +23,6 @@ use crate::{
22#[derive(Debug, Clone)] 23#[derive(Debug, Clone)]
23pub struct Options { 24pub struct Options {
24 pub publish_decorations: bool, 25 pub publish_decorations: bool,
25 pub show_workspace_loaded: bool,
26 pub supports_location_link: bool, 26 pub supports_location_link: bool,
27} 27}
28 28
@@ -58,6 +58,7 @@ impl WorldState {
58 lru_capacity: Option<usize>, 58 lru_capacity: Option<usize>,
59 exclude_globs: &[Glob], 59 exclude_globs: &[Glob],
60 options: Options, 60 options: Options,
61 feature_flags: FeatureFlags,
61 ) -> WorldState { 62 ) -> WorldState {
62 let mut change = AnalysisChange::new(); 63 let mut change = AnalysisChange::new();
63 64
@@ -99,7 +100,7 @@ impl WorldState {
99 } 100 }
100 change.set_crate_graph(crate_graph); 101 change.set_crate_graph(crate_graph);
101 102
102 let mut analysis_host = AnalysisHost::new(lru_capacity); 103 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags);
103 analysis_host.apply_change(change); 104 analysis_host.apply_change(change);
104 WorldState { 105 WorldState {
105 options, 106 options,
@@ -184,6 +185,10 @@ impl WorldState {
184 pub fn complete_request(&mut self, request: CompletedRequest) { 185 pub fn complete_request(&mut self, request: CompletedRequest) {
185 self.latest_requests.write().record(request) 186 self.latest_requests.write().record(request)
186 } 187 }
188
189 pub fn feature_flags(&self) -> &FeatureFlags {
190 self.analysis_host.feature_flags()
191 }
187} 192}
188 193
189impl WorldSnapshot { 194impl WorldSnapshot {
@@ -246,4 +251,8 @@ impl WorldSnapshot {
246 let path = self.vfs.read().file2path(VfsFile(file_id.0)); 251 let path = self.vfs.read().file2path(VfsFile(file_id.0));
247 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) 252 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
248 } 253 }
254
255 pub fn feature_flags(&self) -> &FeatureFlags {
256 self.analysis.feature_flags()
257 }
249} 258}
diff --git a/editors/code/package.json b/editors/code/package.json
index 98faf538f..95ec6cff6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -165,10 +165,10 @@
165 "default": false, 165 "default": false,
166 "description": "When highlighting Rust code, use a unique color per identifier" 166 "description": "When highlighting Rust code, use a unique color per identifier"
167 }, 167 },
168 "rust-analyzer.showWorkspaceLoadedNotification": { 168 "rust-analyzer.featureFlags": {
169 "type": "boolean", 169 "type": "object",
170 "default": true, 170 "default": {},
171 "description": "Show notification when workspace was loaded" 171 "description": "Fine grained feature flags to disable annoying features"
172 }, 172 },
173 "rust-analyzer.enableEnhancedTyping": { 173 "rust-analyzer.enableEnhancedTyping": {
174 "type": "boolean", 174 "type": "boolean",
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 4df6b50ef..570ddca46 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -23,6 +23,7 @@ export class Config {
23 public lruCapacity: null | number = null; 23 public lruCapacity: null | number = null;
24 public displayInlayHints = true; 24 public displayInlayHints = true;
25 public excludeGlobs = []; 25 public excludeGlobs = [];
26 public featureFlags = {};
26 public cargoWatchOptions: CargoWatchOptions = { 27 public cargoWatchOptions: CargoWatchOptions = {
27 enableOnStartup: 'ask', 28 enableOnStartup: 'ask',
28 trace: 'off', 29 trace: 'off',
@@ -132,5 +133,8 @@ export class Config {
132 if (config.has('excludeGlobs')) { 133 if (config.has('excludeGlobs')) {
133 this.excludeGlobs = config.get('excludeGlobs') || []; 134 this.excludeGlobs = config.get('excludeGlobs') || [];
134 } 135 }
136 if (config.has('featureFlags')) {
137 this.featureFlags = config.get('featureFlags') || {};
138 }
135 } 139 }
136} 140}
diff --git a/editors/code/src/server.ts b/editors/code/src/server.ts
index 3273d8749..0d2a99c70 100644
--- a/editors/code/src/server.ts
+++ b/editors/code/src/server.ts
@@ -45,7 +45,8 @@ export class Server {
45 showWorkspaceLoaded: 45 showWorkspaceLoaded:
46 Server.config.showWorkspaceLoadedNotification, 46 Server.config.showWorkspaceLoadedNotification,
47 lruCapacity: Server.config.lruCapacity, 47 lruCapacity: Server.config.lruCapacity,
48 excludeGlobs: Server.config.excludeGlobs 48 excludeGlobs: Server.config.excludeGlobs,
49 featureFlags: Server.config.featureFlags
49 }, 50 },
50 traceOutputChannel 51 traceOutputChannel
51 }; 52 };