Skip to content

Commit fdfdc18

Browse files
bors[bot]matklad
andauthored
Merge #1715
1715: Feature flags r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
2 parents 2d0931b + 8e7ecde commit fdfdc18

File tree

13 files changed

+172
-44
lines changed

13 files changed

+172
-44
lines changed

crates/ra_batch/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::{collections::HashSet, error::Error, path::Path};
33
use rustc_hash::FxHashMap;
44

55
use ra_db::{CrateGraph, FileId, SourceRootId};
6-
use ra_ide_api::{AnalysisChange, AnalysisHost};
6+
use ra_ide_api::{AnalysisChange, AnalysisHost, FeatureFlags};
77
use ra_project_model::{PackageRoot, ProjectWorkspace};
88
use ra_vfs::{RootEntry, Vfs, VfsChange};
99
use ra_vfs_glob::RustPackageFilterBuilder;
@@ -63,7 +63,7 @@ pub fn load(
6363
vfs: &mut Vfs,
6464
) -> AnalysisHost {
6565
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());
6767
let mut analysis_change = AnalysisChange::new();
6868
analysis_change.set_crate_graph(crate_graph);
6969

crates/ra_ide_api/src/completion/presentation.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ impl Completions {
118118
.set_documentation(func.docs(ctx.db))
119119
.detail(detail);
120120
// 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+
{
122125
tested_by!(inserts_parens_for_function_calls);
123126
let snippet =
124127
if data.params().is_empty() || data.has_self_param() && data.params().len() == 1 {

crates/ra_ide_api/src/db.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ra_db::{
77

88
use crate::{
99
symbol_index::{self, SymbolsDatabase},
10-
LineIndex,
10+
FeatureFlags, LineIndex,
1111
};
1212

1313
#[salsa::database(
@@ -22,6 +22,7 @@ use crate::{
2222
#[derive(Debug)]
2323
pub(crate) struct RootDatabase {
2424
runtime: salsa::Runtime<RootDatabase>,
25+
pub(crate) feature_flags: Arc<FeatureFlags>,
2526
pub(crate) last_gc: time::Instant,
2627
pub(crate) last_gc_check: time::Instant,
2728
}
@@ -46,16 +47,17 @@ impl salsa::Database for RootDatabase {
4647

4748
impl Default for RootDatabase {
4849
fn default() -> RootDatabase {
49-
RootDatabase::new(None)
50+
RootDatabase::new(None, FeatureFlags::default())
5051
}
5152
}
5253

5354
impl RootDatabase {
54-
pub fn new(lru_capacity: Option<usize>) -> RootDatabase {
55+
pub fn new(lru_capacity: Option<usize>, feature_flags: FeatureFlags) -> RootDatabase {
5556
let mut db = RootDatabase {
5657
runtime: salsa::Runtime::default(),
5758
last_gc: time::Instant::now(),
5859
last_gc_check: time::Instant::now(),
60+
feature_flags: Arc::new(feature_flags),
5961
};
6062
db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
6163
db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -74,6 +76,7 @@ impl salsa::ParallelDatabase for RootDatabase {
7476
runtime: self.runtime.snapshot(self),
7577
last_gc: self.last_gc,
7678
last_gc_check: self.last_gc_check,
79+
feature_flags: Arc::clone(&self.feature_flags),
7780
})
7881
}
7982
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use 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)]
16+
pub struct FeatureFlags {
17+
flags: FxHashMap<String, bool>,
18+
}
19+
20+
impl 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+
50+
impl 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+
60+
fn 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+
}

crates/ra_ide_api/src/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod db;
1414
pub mod mock_analysis;
1515
mod symbol_index;
1616
mod change;
17+
mod feature_flags;
1718

1819
mod status;
1920
mod completion;
@@ -63,6 +64,7 @@ pub use crate::{
6364
completion::{CompletionItem, CompletionItemKind, InsertTextFormat},
6465
diagnostics::Severity,
6566
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
67+
feature_flags::FeatureFlags,
6668
folding_ranges::{Fold, FoldKind},
6769
hover::HoverResult,
6870
inlay_hints::{InlayHint, InlayKind},
@@ -247,20 +249,24 @@ pub struct AnalysisHost {
247249

248250
impl Default for AnalysisHost {
249251
fn default() -> AnalysisHost {
250-
AnalysisHost::new(None)
252+
AnalysisHost::new(None, FeatureFlags::default())
251253
}
252254
}
253255

254256
impl AnalysisHost {
255-
pub fn new(lru_capcity: Option<usize>) -> AnalysisHost {
256-
AnalysisHost { db: db::RootDatabase::new(lru_capcity) }
257+
pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost {
258+
AnalysisHost { db: db::RootDatabase::new(lru_capcity, feature_flags) }
257259
}
258260
/// Returns a snapshot of the current state, which you can query for
259261
/// semantic information.
260262
pub fn analysis(&self) -> Analysis {
261263
Analysis { db: self.db.snapshot() }
262264
}
263265

266+
pub fn feature_flags(&self) -> &FeatureFlags {
267+
&self.db.feature_flags
268+
}
269+
264270
/// Applies changes to the current state of the world. If there are
265271
/// outstanding snapshots, they will be canceled.
266272
pub fn apply_change(&mut self, change: AnalysisChange) {
@@ -319,6 +325,10 @@ impl Analysis {
319325
(host.analysis(), file_id)
320326
}
321327

328+
pub fn feature_flags(&self) -> &FeatureFlags {
329+
&self.db.feature_flags
330+
}
331+
322332
/// Debug info about the current state of the analysis
323333
pub fn status(&self) -> Cancelable<String> {
324334
self.with_db(|db| status::status(&*db))

crates/ra_lsp_server/src/config.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use rustc_hash::FxHashMap;
2+
13
use serde::{Deserialize, Deserializer};
24

35
/// Client provided initialization options
@@ -12,29 +14,26 @@ pub struct ServerConfig {
1214
#[serde(deserialize_with = "nullable_bool_false")]
1315
pub publish_decorations: bool,
1416

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-
2117
pub exclude_globs: Vec<String>,
2218

2319
pub lru_capacity: Option<usize>,
2420

2521
/// For internal usage to make integrated tests faster.
2622
#[serde(deserialize_with = "nullable_bool_true")]
2723
pub with_sysroot: bool,
24+
25+
/// Fine grained feature flags to disable specific features.
26+
pub feature_flags: FxHashMap<String, bool>,
2827
}
2928

3029
impl Default for ServerConfig {
3130
fn default() -> ServerConfig {
3231
ServerConfig {
3332
publish_decorations: false,
34-
show_workspace_loaded: true,
3533
exclude_globs: Vec::new(),
3634
lru_capacity: None,
3735
with_sysroot: true,
36+
feature_flags: FxHashMap::default(),
3837
}
3938
}
4039
}

crates/ra_lsp_server/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ mod world;
1111

1212
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
1313
pub use crate::{
14-
caps::server_capabilities, config::ServerConfig, main_loop::main_loop, main_loop::LspError,
14+
caps::server_capabilities,
15+
config::ServerConfig,
16+
main_loop::LspError,
17+
main_loop::{main_loop, show_message},
1518
};

crates/ra_lsp_server/src/main.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use flexi_logger::{Duplicate, Logger};
22
use gen_lsp_server::{run_server, stdio_transport};
3-
use serde::Deserialize;
43

5-
use ra_lsp_server::{Result, ServerConfig};
4+
use ra_lsp_server::{show_message, Result, ServerConfig};
65
use ra_prof;
76

87
fn main() -> Result<()> {
@@ -46,15 +45,23 @@ fn main_inner() -> Result<()> {
4645
.filter(|workspaces| !workspaces.is_empty())
4746
.unwrap_or_else(|| vec![root]);
4847

49-
let opts = params
48+
let server_config: ServerConfig = params
5049
.initialization_options
5150
.and_then(|v| {
52-
ServerConfig::deserialize(v)
53-
.map_err(|e| log::error!("failed to deserialize config: {}", e))
51+
serde_json::from_value(v)
52+
.map_err(|e| {
53+
log::error!("failed to deserialize config: {}", e);
54+
show_message(
55+
lsp_types::MessageType::Error,
56+
format!("failed to deserialize config: {}", e),
57+
s,
58+
);
59+
})
5460
.ok()
5561
})
5662
.unwrap_or_default();
57-
ra_lsp_server::main_loop(workspace_roots, params.capabilities, opts, r, s)
63+
64+
ra_lsp_server::main_loop(workspace_roots, params.capabilities, server_config, r, s)
5865
})?;
5966
log::info!("shutting down IO...");
6067
threads.join()?;

crates/ra_lsp_server/src/main_loop.rs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use gen_lsp_server::{
99
handle_shutdown, ErrorCode, RawMessage, RawNotification, RawRequest, RawResponse,
1010
};
1111
use lsp_types::{ClientCapabilities, NumberOrString};
12-
use ra_ide_api::{Canceled, FileId, LibraryData};
12+
use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData};
1313
use ra_prof::profile;
1414
use ra_vfs::VfsTask;
1515
use serde::{de::DeserializeOwned, Serialize};
@@ -56,7 +56,7 @@ pub fn main_loop(
5656
msg_receiver: &Receiver<RawMessage>,
5757
msg_sender: &Sender<RawMessage>,
5858
) -> Result<()> {
59-
log::debug!("server_config: {:?}", config);
59+
log::info!("server_config: {:#?}", config);
6060
// FIXME: support dynamic workspace loading.
6161
let workspaces = {
6262
let ws_worker = workspace_loader(config.with_sysroot);
@@ -83,20 +83,35 @@ pub fn main_loop(
8383
.iter()
8484
.map(|glob| ra_vfs_glob::Glob::new(glob))
8585
.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);
86101
let mut state = WorldState::new(
87102
ws_roots,
88103
workspaces,
89104
config.lru_capacity,
90105
&globs,
91106
Options {
92107
publish_decorations: config.publish_decorations,
93-
show_workspace_loaded: config.show_workspace_loaded,
94108
supports_location_link: client_caps
95109
.text_document
96110
.and_then(|it| it.definition)
97111
.and_then(|it| it.link_support)
98112
.unwrap_or(false),
99113
},
114+
feature_flags,
100115
);
101116

102117
let pool = ThreadPool::new(THREADPOOL_SIZE);
@@ -276,7 +291,7 @@ fn main_loop_inner(
276291
&& in_flight_libraries == 0
277292
{
278293
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") {
280295
let msg = format!("workspace loaded, {} rust packages", n_packages);
281296
show_message(req::MessageType::Info, msg, msg_sender);
282297
}
@@ -587,17 +602,20 @@ fn update_file_notifications_on_threadpool(
587602
subscriptions: Vec<FileId>,
588603
) {
589604
log::trace!("updating notifications for {:?}", subscriptions);
605+
let publish_diagnostics = world.feature_flags().get("lsp.diagnostics");
590606
pool.execute(move || {
591607
for file_id in subscriptions {
592-
match handlers::publish_diagnostics(&world, file_id) {
593-
Err(e) => {
594-
if !is_canceled(&e) {
595-
log::error!("failed to compute diagnostics: {:?}", e);
608+
if publish_diagnostics {
609+
match handlers::publish_diagnostics(&world, file_id) {
610+
Err(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();
596618
}
597-
}
598-
Ok(params) => {
599-
let not = RawNotification::new::<req::PublishDiagnostics>(&params);
600-
sender.send(Task::Notify(not)).unwrap();
601619
}
602620
}
603621
if publish_decorations {
@@ -617,7 +635,11 @@ fn update_file_notifications_on_threadpool(
617635
});
618636
}
619637

620-
fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<RawMessage>) {
638+
pub fn show_message(
639+
typ: req::MessageType,
640+
message: impl Into<String>,
641+
sender: &Sender<RawMessage>,
642+
) {
621643
let message = message.into();
622644
let params = req::ShowMessageParams { typ, message };
623645
let not = RawNotification::new::<req::ShowMessage>(&params);

0 commit comments

Comments
 (0)