about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAli Bektas <bektasali@protonmail.com>2024-04-27 22:50:31 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-06-05 10:45:22 +0200
commitff6e912efb7d87918a41577ffa6f72c03e3b5d60 (patch)
tree3e890c3b950ca8ccc72dcc199c660f10d6ef2847
parent53b5038b5409fc8a85e1a054cb18ce6da4a4431c (diff)
downloadrust-ff6e912efb7d87918a41577ffa6f72c03e3b5d60.tar.gz
rust-ff6e912efb7d87918a41577ffa6f72c03e3b5d60.zip
Apply requested changes round 2
-rw-r--r--src/tools/rust-analyzer/crates/paths/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs232
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs114
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs1045
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs1019
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs4
11 files changed, 1159 insertions, 1269 deletions
diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs
index 7d7cf0220e6..33c3f83db50 100644
--- a/src/tools/rust-analyzer/crates/paths/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs
@@ -150,7 +150,7 @@ impl AbsPathBuf {
     /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`)
     ///   and `path` is not empty, the new path is normalized: all references
     ///   to `.` and `..` are removed.
-    pub fn push(&mut self, suffix: &str) {
+    pub fn push<P: AsRef<Utf8Path>>(&mut self, suffix: P) {
         self.0.push(suffix)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 7e58cd70c49..316384f9633 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -229,7 +229,7 @@ fn run_server() -> anyhow::Result<()> {
         let mut change = ConfigChange::default();
         change.change_client_config(json);
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
         if !error_sink.is_empty() {
             use lsp_types::{
                 notification::{Notification, ShowMessage},
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index b2d70562890..d73e4028e51 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -46,7 +46,7 @@ impl flags::Scip {
             let mut change = ConfigChange::default();
             change.change_client_config(json);
             let mut error_sink = ConfigError::default();
-            config = config.apply_change(change, &mut error_sink);
+            (config, _) = config.apply_change(change, &mut error_sink);
             // FIXME @alibektas : What happens to errors without logging?
             error!(?error_sink, "Config Error(s)");
         }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 51664dd799e..646bae4467a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -29,12 +29,11 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use semver::Version;
 use serde::{
     de::{DeserializeOwned, Error},
-    ser::SerializeStruct,
     Deserialize, Serialize,
 };
 use stdx::format_to_acc;
 use triomphe::Arc;
-use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
+use vfs::{AbsPath, AbsPathBuf, VfsPath};
 
 use crate::{
     caps::completion_item_edit_resolve,
@@ -667,7 +666,7 @@ pub struct Config {
     default_config: DefaultConfigData,
     /// Config node that obtains its initial value during the server initialization and
     /// by receiving a `lsp_types::notification::DidChangeConfiguration`.
-    client_config: ClientConfig,
+    client_config: FullConfigInput,
 
     /// Path to the root configuration file. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer/rust-analyzer.toml` in Linux.
     /// If not specified by init of a `Config` object this value defaults to :
@@ -681,139 +680,48 @@ pub struct Config {
 
     /// FIXME @alibektas : Change this to sth better.
     /// Config node whose values apply to **every** Rust project.
-    user_config: Option<RatomlNode>,
+    user_config: Option<GlobalLocalConfigInput>,
 
     /// A special file for this session whose path is set to `self.root_path.join("rust-analyzer.toml")`
     root_ratoml_path: VfsPath,
 
     /// This file can be used to make global changes while having only a workspace-wide scope.
-    root_ratoml: Option<RatomlNode>,
+    root_ratoml: Option<GlobalLocalConfigInput>,
 
     /// For every `SourceRoot` there can be at most one RATOML file.
-    ratoml_files: FxHashMap<SourceRootId, RatomlNode>,
+    ratoml_files: FxHashMap<SourceRootId, GlobalLocalConfigInput>,
 
     /// Clone of the value that is stored inside a `GlobalState`.
     source_root_parent_map: Arc<FxHashMap<SourceRootId, SourceRootId>>,
 
-    /// Changes made to client and global configurations will partially not be reflected even after `.apply_change()` was called.
-    /// This field signals that the `GlobalState` should call its `update_configuration()` method.
-    should_update: bool,
-}
-
-#[derive(Clone, Debug)]
-struct RatomlNode {
-    node: GlobalLocalConfigInput,
-    file_id: FileId,
-}
-
-#[derive(Debug, Clone, Default)]
-struct ClientConfig {
-    node: FullConfigInput,
     detached_files: Vec<AbsPathBuf>,
 }
 
-impl Serialize for RatomlNode {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        let mut s = serializer.serialize_struct("RatomlNode", 2)?;
-        s.serialize_field("file_id", &self.file_id.index())?;
-        s.serialize_field("config", &self.node)?;
-        s.end()
-    }
-}
-
-#[derive(Debug, Hash, Eq, PartialEq)]
-pub(crate) enum ConfigNodeKey {
-    Ratoml(SourceRootId),
-    Client,
-    User,
-}
-
-impl Serialize for ConfigNodeKey {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        match self {
-            ConfigNodeKey::Ratoml(source_root_id) => serializer.serialize_u32(source_root_id.0),
-            ConfigNodeKey::Client => serializer.serialize_str("client"),
-            ConfigNodeKey::User => serializer.serialize_str("user"),
-        }
-    }
-}
-
-#[derive(Debug, Serialize)]
-enum ConfigNodeValue<'a> {
-    /// `rust-analyzer::config` module works by setting
-    /// a mapping between `SourceRootId` and `ConfigInput`.
-    /// Storing a `FileId` is mostly for debugging purposes.
-    Ratoml(&'a RatomlNode),
-    Client(&'a FullConfigInput),
-}
-
 impl Config {
-    /// FIXME @alibektas : Before integration tests, I thought I would
-    /// get the debug output of the config tree and do assertions based on it.
-    /// The reason why I didn't delete this is that we may want to have a lsp_ext
-    /// like "DebugConfigTree" so that it is easier for users to get a snapshot of
-    /// the config state for us to debug.
-    #[allow(dead_code)]
-    /// Walk towards the root starting from a specified `ConfigNode`
-    fn traverse(
-        &self,
-        start: ConfigNodeKey,
-    ) -> impl Iterator<Item = (ConfigNodeKey, ConfigNodeValue<'_>)> {
-        let mut v = vec![];
-
-        if let ConfigNodeKey::Ratoml(start) = start {
-            let mut par: Option<SourceRootId> = Some(start);
-            while let Some(source_root_id) = par {
-                par = self.source_root_parent_map.get(&start).copied();
-                if let Some(config) = self.ratoml_files.get(&source_root_id) {
-                    v.push((
-                        ConfigNodeKey::Ratoml(source_root_id),
-                        ConfigNodeValue::Ratoml(config),
-                    ));
-                }
-            }
-        }
-
-        v.push((ConfigNodeKey::Client, ConfigNodeValue::Client(&self.client_config.node)));
-
-        if let Some(user_config) = self.user_config.as_ref() {
-            v.push((ConfigNodeKey::User, ConfigNodeValue::Ratoml(user_config)));
-        }
-
-        v.into_iter()
-    }
-
     pub fn user_config_path(&self) -> &VfsPath {
         &self.user_config_path
     }
 
-    pub fn should_update(&self) -> bool {
-        self.should_update
-    }
-
     // FIXME @alibektas : Server's health uses error sink but in other places it is not used atm.
-    pub fn apply_change(&self, change: ConfigChange, error_sink: &mut ConfigError) -> Config {
+    /// Changes made to client and global configurations will partially not be reflected even after `.apply_change()` was called.
+    /// The return tuple's bool component signals whether the `GlobalState` should call its `update_configuration()` method.
+    pub fn apply_change(
+        &self,
+        change: ConfigChange,
+        error_sink: &mut ConfigError,
+    ) -> (Config, bool) {
         let mut config = self.clone();
         let mut toml_errors = vec![];
         let mut json_errors = vec![];
 
-        config.should_update = false;
-
-        if let Some((file_id, change)) = change.user_config_change {
-            config.user_config = Some(RatomlNode {
-                file_id,
-                node: GlobalLocalConfigInput::from_toml(
-                    toml::from_str(change.to_string().as_str()).unwrap(),
-                    &mut toml_errors,
-                ),
-            });
-            config.should_update = true;
+        let mut should_update = false;
+
+        if let Some(change) = change.user_config_change {
+            if let Ok(change) = toml::from_str(&change) {
+                config.user_config =
+                    Some(GlobalLocalConfigInput::from_toml(change, &mut toml_errors));
+                should_update = true;
+            }
         }
 
         if let Some(mut json) = change.client_config_change {
@@ -832,38 +740,29 @@ impl Config {
 
                 patch_old_style::patch_json_for_outdated_configs(&mut json);
 
-                config.client_config = ClientConfig {
-                    node: FullConfigInput::from_json(json, &mut json_errors),
-                    detached_files,
-                }
+                config.client_config = FullConfigInput::from_json(json, &mut json_errors);
+                config.detached_files = detached_files;
             }
-            config.should_update = true;
+            should_update = true;
         }
 
-        if let Some((file_id, change)) = change.root_ratoml_change {
-            config.root_ratoml = Some(RatomlNode {
-                file_id,
-                node: GlobalLocalConfigInput::from_toml(
-                    toml::from_str(change.to_string().as_str()).unwrap(),
-                    &mut toml_errors,
-                ),
-            });
-            config.should_update = true;
+        if let Some(change) = change.root_ratoml_change {
+            if let Ok(change) = toml::from_str(&change) {
+                config.root_ratoml =
+                    Some(GlobalLocalConfigInput::from_toml(change, &mut toml_errors));
+                should_update = true;
+            }
         }
 
         if let Some(change) = change.ratoml_file_change {
-            for (source_root_id, (file_id, _, text)) in change {
+            for (source_root_id, (_, text)) in change {
                 if let Some(text) = text {
-                    config.ratoml_files.insert(
-                        source_root_id,
-                        RatomlNode {
-                            file_id,
-                            node: GlobalLocalConfigInput::from_toml(
-                                toml::from_str(&text).unwrap(),
-                                &mut toml_errors,
-                            ),
-                        },
-                    );
+                    if let Ok(change) = toml::from_str(&text) {
+                        config.ratoml_files.insert(
+                            source_root_id,
+                            GlobalLocalConfigInput::from_toml(change, &mut toml_errors),
+                        );
+                    }
                 }
             }
         }
@@ -907,16 +806,22 @@ impl Config {
                 serde_json::Error::custom("expected a non-empty string"),
             ));
         }
-        config
+        (config, should_update)
+    }
+
+    pub fn apply_change_whatever(&self, change: ConfigChange) -> (Config, ConfigError) {
+        let mut e = ConfigError(vec![]);
+        let (config, _) = self.apply_change(change, &mut e);
+        (config, e)
     }
 }
 
 #[derive(Default, Debug)]
 pub struct ConfigChange {
-    user_config_change: Option<(FileId, String)>,
-    root_ratoml_change: Option<(FileId, String)>,
+    user_config_change: Option<String>,
+    root_ratoml_change: Option<String>,
     client_config_change: Option<serde_json::Value>,
-    ratoml_file_change: Option<FxHashMap<SourceRootId, (FileId, VfsPath, Option<String>)>>,
+    ratoml_file_change: Option<FxHashMap<SourceRootId, (VfsPath, Option<String>)>>,
     source_map_change: Option<Arc<FxHashMap<SourceRootId, SourceRootId>>>,
 }
 
@@ -924,26 +829,25 @@ impl ConfigChange {
     pub fn change_ratoml(
         &mut self,
         source_root: SourceRootId,
-        file_id: FileId,
         vfs_path: VfsPath,
         content: Option<String>,
-    ) -> Option<(FileId, VfsPath, Option<String>)> {
+    ) -> Option<(VfsPath, Option<String>)> {
         if let Some(changes) = self.ratoml_file_change.as_mut() {
-            changes.insert(source_root, (file_id, vfs_path, content))
+            changes.insert(source_root, (vfs_path, content))
         } else {
             let mut map = FxHashMap::default();
-            map.insert(source_root, (file_id, vfs_path, content));
+            map.insert(source_root, (vfs_path, content));
             self.ratoml_file_change = Some(map);
             None
         }
     }
 
-    pub fn change_user_config(&mut self, content: Option<(FileId, String)>) {
+    pub fn change_user_config(&mut self, content: Option<String>) {
         assert!(self.user_config_change.is_none()); // Otherwise it is a double write.
         self.user_config_change = content;
     }
 
-    pub fn change_root_ratoml(&mut self, content: Option<(FileId, String)>) {
+    pub fn change_root_ratoml(&mut self, content: Option<String>) {
         assert!(self.user_config_change.is_none()); // Otherwise it is a double write.
         self.root_ratoml_change = content;
     }
@@ -1163,8 +1067,6 @@ impl ConfigError {
     }
 }
 
-impl ConfigError {}
-
 impl fmt::Display for ConfigError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let errors = self.0.iter().format_with("\n", |inner, f| match inner {
@@ -1221,7 +1123,7 @@ impl Config {
             snippets: Default::default(),
             workspace_roots,
             visual_studio_code_version,
-            client_config: ClientConfig::default(),
+            client_config: FullConfigInput::default(),
             user_config: None,
             ratoml_files: FxHashMap::default(),
             default_config: DefaultConfigData::default(),
@@ -1229,7 +1131,7 @@ impl Config {
             user_config_path,
             root_ratoml: None,
             root_ratoml_path,
-            should_update: false,
+            detached_files: Default::default(),
         }
     }
 
@@ -1318,7 +1220,7 @@ impl Config {
     pub fn detached_files(&self) -> &Vec<AbsPathBuf> {
         // FIXME @alibektas : This is the only config that is confusing. If it's a proper configuration
         // why is it not among the others? If it's client only which I doubt it is current state should be alright
-        &self.client_config.detached_files
+        &self.detached_files
     }
 
     pub fn diagnostics(&self, source_root: Option<SourceRootId>) -> DiagnosticsConfig {
@@ -2587,19 +2489,19 @@ macro_rules! _impl_for_config_data {
                         while let Some(source_root_id) = par {
                             par = self.source_root_parent_map.get(&source_root_id).copied();
                             if let Some(config) = self.ratoml_files.get(&source_root_id) {
-                                if let Some(value) = config.node.local.$field.as_ref() {
+                                if let Some(value) = config.local.$field.as_ref() {
                                     return value;
                                 }
                             }
                         }
                     }
 
-                    if let Some(v) = self.client_config.node.local.$field.as_ref() {
+                    if let Some(v) = self.client_config.local.$field.as_ref() {
                         return &v;
                     }
 
                     if let Some(user_config) = self.user_config.as_ref() {
-                        if let Some(v) = user_config.node.local.$field.as_ref() {
+                        if let Some(v) = user_config.local.$field.as_ref() {
                             return &v;
                         }
                     }
@@ -2621,17 +2523,17 @@ macro_rules! _impl_for_config_data {
                 $vis fn $field(&self) -> &$ty {
 
                     if let Some(root_path_ratoml) = self.root_ratoml.as_ref() {
-                        if let Some(v) = root_path_ratoml.node.global.$field.as_ref() {
+                        if let Some(v) = root_path_ratoml.global.$field.as_ref() {
                             return &v;
                         }
                     }
 
-                    if let Some(v) = self.client_config.node.global.$field.as_ref() {
+                    if let Some(v) = self.client_config.global.$field.as_ref() {
                         return &v;
                     }
 
                     if let Some(user_config) = self.user_config.as_ref() {
-                        if let Some(v) = user_config.node.global.$field.as_ref() {
+                        if let Some(v) = user_config.global.$field.as_ref() {
                             return &v;
                         }
                     }
@@ -2673,7 +2575,7 @@ macro_rules! _config_data {
     }) => {
         /// Default config values for this grouping.
         #[allow(non_snake_case)]
-        #[derive(Debug, Clone, Serialize)]
+        #[derive(Debug, Clone )]
         struct $name { $($field: $ty,)* }
 
         impl_for_config_data!{
@@ -3399,7 +3301,7 @@ mod tests {
         }}));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
         assert_eq!(config.proc_macro_srv(), None);
     }
 
@@ -3419,7 +3321,7 @@ mod tests {
         }}));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
         assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap()));
     }
 
@@ -3441,7 +3343,7 @@ mod tests {
         }}));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
 
         assert_eq!(
             config.proc_macro_srv(),
@@ -3466,7 +3368,7 @@ mod tests {
         }));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
         assert_eq!(config.cargo_targetDir(), &None);
         assert!(
             matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none())
@@ -3489,7 +3391,7 @@ mod tests {
         }));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
 
         assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true)));
         assert!(
@@ -3513,7 +3415,7 @@ mod tests {
         }));
 
         let mut error_sink = ConfigError::default();
-        config = config.apply_change(change, &mut error_sink);
+        (config, _) = config.apply_change(change, &mut error_sink);
 
         assert_eq!(
             config.cargo_targetDir(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 2210dab0f57..c289a07978d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -9,7 +9,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
 use flycheck::FlycheckHandle;
 use hir::ChangeWithProcMacros;
 use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
-use ide_db::base_db::{CrateId, ProcMacroPaths};
+use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabaseExt};
 use load_cargo::SourceRootConfig;
 use lsp_types::{SemanticTokens, Url};
 use nohash_hasher::IntMap;
@@ -266,8 +266,8 @@ impl GlobalState {
         let mut ratoml_text_map: FxHashMap<FileId, (vfs::VfsPath, Option<String>)> =
             FxHashMap::default();
 
-        let mut user_config_file: Option<(FileId, Option<String>)> = None;
-        let mut root_path_ratoml: Option<(FileId, Option<String>)> = None;
+        let mut user_config_file: Option<Option<String>> = None;
+        let mut root_path_ratoml: Option<Option<String>> = None;
 
         let root_vfs_path = {
             let mut root_vfs_path = self.config.root_path().to_path_buf();
@@ -336,30 +336,23 @@ impl GlobalState {
                 bytes.push((file.file_id, text));
             }
             let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
-            bytes.into_iter().for_each(|(file_id, text)| match text {
-                None => {
-                    change.change_file(file_id, None);
-                    if let Some(vfs_path) = modified_ratoml_files.get(&file_id) {
-                        if vfs_path == self.config.user_config_path() {
-                            user_config_file = Some((file_id, None));
-                        } else if vfs_path == &root_vfs_path {
-                            root_path_ratoml = Some((file_id, None));
-                        } else {
-                            ratoml_text_map.insert(file_id, (vfs_path.clone(), None));
-                        }
+            bytes.into_iter().for_each(|(file_id, text)| {
+                let text = match text {
+                    None => None,
+                    Some((text, line_endings)) => {
+                        line_endings_map.insert(file_id, line_endings);
+                        Some(text)
                     }
-                }
-                Some((text, line_endings)) => {
-                    line_endings_map.insert(file_id, line_endings);
-                    change.change_file(file_id, Some(text.clone()));
-                    if let Some(vfs_path) = modified_ratoml_files.get(&file_id) {
-                        if vfs_path == self.config.user_config_path() {
-                            user_config_file = Some((file_id, Some(text.clone())));
-                        } else if vfs_path == &root_vfs_path {
-                            root_path_ratoml = Some((file_id, Some(text.clone())));
-                        } else {
-                            ratoml_text_map.insert(file_id, (vfs_path.clone(), Some(text.clone())));
-                        }
+                };
+
+                change.change_file(file_id, text.clone());
+                if let Some(vfs_path) = modified_ratoml_files.get(&file_id) {
+                    if vfs_path == self.config.user_config_path() {
+                        user_config_file = Some(text);
+                    } else if vfs_path == &root_vfs_path {
+                        root_path_ratoml = Some(text);
+                    } else {
+                        ratoml_text_map.insert(file_id, (vfs_path.clone(), text));
                     }
                 }
             });
@@ -372,53 +365,54 @@ impl GlobalState {
 
         let _p = span!(Level::INFO, "GlobalState::process_changes/apply_change").entered();
         self.analysis_host.apply_change(change);
-
-        let config_change = {
-            let mut change = ConfigChange::default();
-            let snap = self.analysis_host.analysis();
-
-            for (file_id, (vfs_path, text)) in ratoml_text_map {
-                // If change has been made to a ratoml file that
-                // belongs to a non-local source root, we will ignore it.
-                // As it doesn't make sense a users to use external config files.
-                if let Ok(source_root) = snap.source_root_id(file_id) {
-                    if let Ok(true) = snap.is_local_source_root(source_root) {
-                        if let Some((old_file, old_path, old_text)) =
-                            change.change_ratoml(source_root, file_id, vfs_path.clone(), text)
+        if !(ratoml_text_map.is_empty() && user_config_file.is_none() && root_path_ratoml.is_none())
+        {
+            let config_change = {
+                let mut change = ConfigChange::default();
+                let db = self.analysis_host.raw_database();
+
+                for (file_id, (vfs_path, text)) in ratoml_text_map {
+                    // If change has been made to a ratoml file that
+                    // belongs to a non-local source root, we will ignore it.
+                    // As it doesn't make sense a users to use external config files.
+                    let sr_id = db.file_source_root(file_id);
+                    let sr = db.source_root(sr_id);
+                    if !sr.is_library {
+                        if let Some((old_path, old_text)) =
+                            change.change_ratoml(sr_id, vfs_path.clone(), text)
                         {
                             // SourceRoot has more than 1 RATOML files. In this case lexicographically smaller wins.
                             if old_path < vfs_path {
                                 span!(Level::ERROR, "Two `rust-analyzer.toml` files were found inside the same crate. {vfs_path} has no effect.");
                                 // Put the old one back in.
-                                change.change_ratoml(source_root, old_file, old_path, old_text);
+                                change.change_ratoml(sr_id, old_path, old_text);
                             }
                         }
+                    } else {
+                        // Mapping to a SourceRoot should always end up in `Ok`
+                        span!(Level::ERROR, "Mapping to SourceRootId failed.");
                     }
-                } else {
-                    // Mapping to a SourceRoot should always end up in `Ok`
-                    span!(Level::ERROR, "Mapping to SourceRootId failed.");
                 }
-            }
 
-            if let Some((file_id, Some(txt))) = user_config_file {
-                change.change_user_config(Some((file_id, txt)));
-            }
-
-            if let Some((file_id, Some(txt))) = root_path_ratoml {
-                change.change_root_ratoml(Some((file_id, txt)));
-            }
+                if let Some(Some(txt)) = user_config_file {
+                    change.change_user_config(Some(txt));
+                }
 
-            change
-        };
+                if let Some(Some(txt)) = root_path_ratoml {
+                    change.change_root_ratoml(Some(txt));
+                }
 
-        let mut error_sink = ConfigError::default();
-        let config = self.config.apply_change(config_change, &mut error_sink);
+                change
+            };
+            let mut error_sink = ConfigError::default();
+            let (config, should_update) = self.config.apply_change(config_change, &mut error_sink);
 
-        if config.should_update() {
-            self.update_configuration(config);
-        } else {
-            // No global or client level config was changed. So we can just naively replace config.
-            self.config = Arc::new(config);
+            if should_update {
+                self.update_configuration(config);
+            } else {
+                // No global or client level config was changed. So we can just naively replace config.
+                self.config = Arc::new(config);
+            }
         }
 
         {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 123a9a06a3b..bdb27043ebe 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -201,7 +201,8 @@ pub(crate) fn handle_did_change_configuration(
                         let mut change = ConfigChange::default();
                         change.change_client_config(json.take());
                         let mut error_sink = ConfigError::default();
-                        config = config.apply_change(change, &mut error_sink);
+                        (config, _) = config.apply_change(change, &mut error_sink);
+                        // Client config changes neccesitates .update_config method to be called.
                         this.update_configuration(config);
                     }
                 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index d9b31550c56..b3c11d0156e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -39,7 +39,7 @@ pub mod tracing {
 }
 
 pub mod config;
-pub mod global_state;
+mod global_state;
 pub mod lsp;
 use self::lsp::ext as lsp_ext;
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index e804ba3db95..364a0083e59 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -605,7 +605,8 @@ impl GlobalState {
         let mut config_change = ConfigChange::default();
         config_change.change_source_root_parent_map(self.local_roots_parent_map.clone());
         let mut error_sink = ConfigError::default();
-        self.config = Arc::new(self.config.apply_change(config_change, &mut error_sink));
+        let (config, _) = self.config.apply_change(config_change, &mut error_sink);
+        self.config = Arc::new(config);
 
         self.recreate_crate_graph(cause);
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index fd6f79abc1c..f886df60e68 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -11,36 +11,33 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::disallowed_types)]
 
+mod ratoml;
 #[cfg(not(feature = "in-rust-tree"))]
 mod sourcegen;
 mod support;
 mod testdir;
 mod tidy;
 
-use std::{collections::HashMap, path::PathBuf, sync::Once, time::Instant};
+use std::{collections::HashMap, path::PathBuf, time::Instant};
 
 use lsp_types::{
-    notification::{DidChangeTextDocument, DidOpenTextDocument, DidSaveTextDocument},
+    notification::DidOpenTextDocument,
     request::{
         CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest,
         InlayHintRequest, InlayHintResolveRequest, WillRenameFiles, WorkspaceSymbolRequest,
     },
-    CodeAction, CodeActionContext, CodeActionOrCommand, CodeActionParams, CodeActionResponse,
-    CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
-    DidSaveTextDocumentParams, DocumentFormattingParams, FileRename, FormattingOptions,
-    GotoDefinitionParams, Hover, HoverParams, InlayHint, InlayHintLabel, InlayHintParams,
-    PartialResultParams, Position, Range, RenameFilesParams, TextDocumentContentChangeEvent,
-    TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url,
-    VersionedTextDocumentIdentifier, WorkDoneProgressParams,
+    CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
+    DocumentFormattingParams, FileRename, FormattingOptions, GotoDefinitionParams, HoverParams,
+    InlayHint, InlayHintLabel, InlayHintParams, PartialResultParams, Position, Range,
+    RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams,
 };
-use paths::Utf8PathBuf;
+
 use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams, UnindexedProject};
 use serde_json::json;
 use stdx::format_to_acc;
-use support::Server;
+
 use test_utils::skip_slow_tests;
 use testdir::TestDir;
-use tracing_subscriber::fmt::TestWriter;
 
 use crate::support::{project, Project};
 
@@ -1471,1027 +1468,3 @@ version = "0.0.0"
 
     server.request::<WorkspaceSymbolRequest>(Default::default(), json!([]));
 }
-
-enum QueryType {
-    AssistEmitMustUse,
-    /// A query whose config key is a part of the global configs, so that
-    /// testing for changes to this config means testing if global changes
-    /// take affect.
-    GlobalHover,
-}
-
-struct RatomlTest {
-    urls: Vec<Url>,
-    server: Server,
-    tmp_path: Utf8PathBuf,
-    user_config_dir: Utf8PathBuf,
-}
-
-impl RatomlTest {
-    const EMIT_MUST_USE: &'static str = r#"assist.emitMustUse = true"#;
-    const EMIT_MUST_NOT_USE: &'static str = r#"assist.emitMustUse = false"#;
-    const EMIT_MUST_USE_SNIPPET: &'static str = r#"
-
-impl Value {
-    #[must_use]
-    fn as_text(&self) -> Option<&String> {
-        if let Self::Text(v) = self {
-            Some(v)
-        } else {
-            None
-        }
-    }
-}"#;
-
-    const GLOBAL_TRAIT_ASSOC_ITEMS_ZERO: &'static str = r#"hover.show.traitAssocItems = 0"#;
-    const GLOBAL_TRAIT_ASSOC_ITEMS_SNIPPET: &'static str = r#"
-```rust
-p1
-```
-
-```rust
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-```"#;
-
-    fn new(
-        fixtures: Vec<&str>,
-        roots: Vec<&str>,
-        client_config: Option<serde_json::Value>,
-    ) -> Self {
-        // setup();
-        let tmp_dir = TestDir::new();
-        let tmp_path = tmp_dir.path().to_owned();
-
-        let full_fixture = fixtures.join("\n");
-
-        let user_cnf_dir = TestDir::new();
-        let user_config_dir = user_cnf_dir.path().to_owned();
-
-        let mut project =
-            Project::with_fixture(&full_fixture).tmp_dir(tmp_dir).user_config_dir(user_cnf_dir);
-
-        for root in roots {
-            project = project.root(root);
-        }
-
-        if let Some(client_config) = client_config {
-            project = project.with_config(client_config);
-        }
-
-        let server = project.server().wait_until_workspace_is_loaded();
-
-        let mut case = Self { urls: vec![], server, tmp_path, user_config_dir };
-        let urls = fixtures.iter().map(|fixture| case.fixture_path(fixture)).collect::<Vec<_>>();
-        case.urls = urls;
-        case
-    }
-
-    fn fixture_path(&self, fixture: &str) -> Url {
-        let mut lines = fixture.trim().split('\n');
-
-        let mut path =
-            lines.next().expect("All files in a fixture are expected to have at least one line.");
-
-        if path.starts_with("//- minicore") {
-            path = lines.next().expect("A minicore line must be followed by a path.")
-        }
-
-        path = path.strip_prefix("//- ").expect("Path must be preceded by a //- prefix ");
-
-        let spl = path[1..].split('/');
-        let mut path = self.tmp_path.clone();
-
-        let mut spl = spl.into_iter();
-        if let Some(first) = spl.next() {
-            if first == "$$CONFIG_DIR$$" {
-                path = self.user_config_dir.clone();
-            } else {
-                path = path.join(first);
-            }
-        }
-        for piece in spl {
-            path = path.join(piece);
-        }
-
-        Url::parse(
-            format!(
-                "file://{}",
-                path.into_string().to_owned().replace("C:\\", "/c:/").replace('\\', "/")
-            )
-            .as_str(),
-        )
-        .unwrap()
-    }
-
-    fn create(&mut self, fixture_path: &str, text: String) {
-        let url = self.fixture_path(fixture_path);
-
-        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
-            text_document: TextDocumentItem {
-                uri: url.clone(),
-                language_id: "rust".to_owned(),
-                version: 0,
-                text: String::new(),
-            },
-        });
-
-        self.server.notification::<DidChangeTextDocument>(DidChangeTextDocumentParams {
-            text_document: VersionedTextDocumentIdentifier { uri: url, version: 0 },
-            content_changes: vec![TextDocumentContentChangeEvent {
-                range: None,
-                range_length: None,
-                text,
-            }],
-        });
-    }
-
-    fn delete(&mut self, file_idx: usize) {
-        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
-            text_document: TextDocumentItem {
-                uri: self.urls[file_idx].clone(),
-                language_id: "rust".to_owned(),
-                version: 0,
-                text: "".to_owned(),
-            },
-        });
-
-        // See if deleting ratoml file will make the config of interest to return to its default value.
-        self.server.notification::<DidSaveTextDocument>(DidSaveTextDocumentParams {
-            text_document: TextDocumentIdentifier { uri: self.urls[file_idx].clone() },
-            text: Some("".to_owned()),
-        });
-    }
-
-    fn edit(&mut self, file_idx: usize, text: String) {
-        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
-            text_document: TextDocumentItem {
-                uri: self.urls[file_idx].clone(),
-                language_id: "rust".to_owned(),
-                version: 0,
-                text: String::new(),
-            },
-        });
-
-        self.server.notification::<DidChangeTextDocument>(DidChangeTextDocumentParams {
-            text_document: VersionedTextDocumentIdentifier {
-                uri: self.urls[file_idx].clone(),
-                version: 0,
-            },
-            content_changes: vec![TextDocumentContentChangeEvent {
-                range: None,
-                range_length: None,
-                text,
-            }],
-        });
-    }
-
-    fn query(&self, query: QueryType, source_file_idx: usize) -> bool {
-        match query {
-            QueryType::AssistEmitMustUse => {
-                let res = self.server.send_request::<CodeActionRequest>(CodeActionParams {
-                    text_document: TextDocumentIdentifier {
-                        uri: self.urls[source_file_idx].clone(),
-                    },
-                    range: lsp_types::Range {
-                        start: Position::new(2, 13),
-                        end: Position::new(2, 15),
-                    },
-                    context: CodeActionContext {
-                        diagnostics: vec![],
-                        only: None,
-                        trigger_kind: None,
-                    },
-                    work_done_progress_params: WorkDoneProgressParams { work_done_token: None },
-                    partial_result_params: lsp_types::PartialResultParams {
-                        partial_result_token: None,
-                    },
-                });
-
-                let res = serde_json::de::from_str::<CodeActionResponse>(res.to_string().as_str())
-                    .unwrap();
-
-                // The difference setting the new config key will cause can be seen in the lower layers of this nested response
-                // so here are some ugly unwraps and other obscure stuff.
-                let ca: CodeAction = res
-                    .into_iter()
-                    .find_map(|it| {
-                        if let CodeActionOrCommand::CodeAction(ca) = it {
-                            if ca.title.as_str() == "Generate an `as_` method for this enum variant"
-                            {
-                                return Some(ca);
-                            }
-                        }
-
-                        None
-                    })
-                    .unwrap();
-
-                if let lsp_types::DocumentChanges::Edits(edits) =
-                    ca.edit.unwrap().document_changes.unwrap()
-                {
-                    if let lsp_types::OneOf::Left(l) = &edits[0].edits[0] {
-                        return l.new_text.as_str() == RatomlTest::EMIT_MUST_USE_SNIPPET;
-                    }
-                }
-            }
-            QueryType::GlobalHover => {
-                let res = self.server.send_request::<HoverRequest>(HoverParams {
-                    work_done_progress_params: WorkDoneProgressParams { work_done_token: None },
-                    text_document_position_params: TextDocumentPositionParams {
-                        text_document: TextDocumentIdentifier {
-                            uri: self.urls[source_file_idx].clone(),
-                        },
-                        position: Position::new(7, 18),
-                    },
-                });
-                let res = serde_json::de::from_str::<Hover>(res.to_string().as_str()).unwrap();
-                assert!(matches!(res.contents, lsp_types::HoverContents::Markup(_)));
-                if let lsp_types::HoverContents::Markup(m) = res.contents {
-                    return m.value == RatomlTest::GLOBAL_TRAIT_ASSOC_ITEMS_SNIPPET;
-                }
-            }
-        }
-
-        panic!()
-    }
-}
-
-static INIT: Once = Once::new();
-
-fn setup() {
-    INIT.call_once(|| {
-        let trc = rust_analyzer::tracing::Config {
-            writer: TestWriter::default(),
-            // Deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually
-            // useful information in there for debugging.
-            filter: std::env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()),
-            chalk_filter: std::env::var("CHALK_DEBUG").ok(),
-            profile_filter: std::env::var("RA_PROFILE").ok(),
-        };
-
-        trc.init().unwrap();
-    });
-}
-
-// /// Check if we are listening for changes in user's config file ( e.g on Linux `~/.config/rust-analyzer/.rust-analyzer.toml`)
-// #[test]
-// #[cfg(target_os = "windows")]
-// fn listen_to_user_config_scenario_windows() {
-//     todo!()
-// }
-
-// #[test]
-// #[cfg(target_os = "linux")]
-// fn listen_to_user_config_scenario_linux() {
-//     todo!()
-// }
-
-// #[test]
-// #[cfg(target_os = "macos")]
-// fn listen_to_user_config_scenario_macos() {
-//     todo!()
-// }
-
-/// Check if made changes have had any effect on
-/// the client config.
-#[test]
-fn ratoml_client_config_basic() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-        ],
-        vec!["p1"],
-        Some(json!({
-            "assist" : {
-                "emitMustUse" : true
-            }
-        })),
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 1));
-}
-
-/// Checks if client config can be modified.
-/// FIXME @alibektas : This test is atm not valid.
-/// Asking for client config from the client is a 2 way communication
-/// which we cannot imitate with the current slow-tests infrastructure.
-/// See rust-analyzer::handlers::notifications#197
-//     #[test]
-//     fn client_config_update() {
-//         setup();
-
-//         let server = RatomlTest::new(
-//             vec![
-//                 r#"
-// //- /p1/Cargo.toml
-// [package]
-// name = "p1"
-// version = "0.1.0"
-// edition = "2021"
-// "#,
-//                 r#"
-// //- /p1/src/lib.rs
-// enum Value {
-//     Number(i32),
-//     Text(String),
-// }"#,
-//             ],
-//             vec!["p1"],
-//             None,
-//         );
-
-//         assert!(!server.query(QueryType::AssistEmitMustUse, 1));
-
-//         // a.notification::<DidChangeConfiguration>(DidChangeConfigurationParams {
-//         //     settings: json!({
-//         //         "assists" : {
-//         //             "emitMustUse" : true
-//         //         }
-//         //     }),
-//         // });
-
-//         assert!(server.query(QueryType::AssistEmitMustUse, 1));
-//     }
-
-//     #[test]
-//     fn ratoml_create_ratoml_basic() {
-//         let server = RatomlTest::new(
-//             vec![
-//                 r#"
-// //- /p1/Cargo.toml
-// [package]
-// name = "p1"
-// version = "0.1.0"
-// edition = "2021"
-// "#,
-//                 r#"
-// //- /p1/rust-analyzer.toml
-// assist.emitMustUse = true
-// "#,
-//                 r#"
-// //- /p1/src/lib.rs
-// enum Value {
-//     Number(i32),
-//     Text(String),
-// }
-// "#,
-//             ],
-//             vec!["p1"],
-//             None,
-//         );
-
-//         assert!(server.query(QueryType::AssistEmitMustUse, 2));
-//     }
-
-#[test]
-fn ratoml_user_config_detected() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 2));
-}
-
-#[test]
-fn ratoml_create_user_config() {
-    setup();
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
-    server.create(
-        "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml",
-        RatomlTest::EMIT_MUST_USE.to_owned(),
-    );
-    assert!(server.query(QueryType::AssistEmitMustUse, 1));
-}
-
-#[test]
-fn ratoml_modify_user_config() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021""#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
-assist.emitMustUse = true"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 1));
-    server.edit(2, String::new());
-    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
-}
-
-#[test]
-fn ratoml_delete_user_config() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021""#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
-assist.emitMustUse = true"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 1));
-    server.delete(2);
-    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
-}
-// #[test]
-// fn delete_user_config() {
-//     todo!()
-// }
-
-// #[test]
-// fn modify_client_config() {
-//     todo!()
-// }
-
-#[test]
-fn ratoml_inherit_config_from_ws_root() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-#[test]
-fn ratoml_modify_ratoml_at_ws_root() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = false
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
-    server.edit(1, "assist.emitMustUse = true".to_owned());
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-#[test]
-fn ratoml_delete_ratoml_at_ws_root() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-    server.delete(1);
-    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-#[test]
-fn ratoml_add_immediate_child_to_ws_root() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-    server.create("//- /p1/p2/rust-analyzer.toml", RatomlTest::EMIT_MUST_NOT_USE.to_owned());
-    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-#[test]
-fn ratoml_rm_ws_root_ratoml_child_has_client_as_parent_now() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-    server.delete(1);
-    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-#[test]
-fn ratoml_crates_both_roots() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-workspace = { members = ["p2"] }
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/p2/Cargo.toml
-[package]
-name = "p2"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/p2/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}"#,
-        ],
-        vec!["p1", "p2"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-    assert!(server.query(QueryType::AssistEmitMustUse, 4));
-}
-
-#[test]
-fn ratoml_multiple_ratoml_in_single_source_root() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-        //- /p1/Cargo.toml
-        [package]
-        name = "p1"
-        version = "0.1.0"
-        edition = "2021"
-        "#,
-            r#"
-        //- /p1/rust-analyzer.toml
-        assist.emitMustUse = true
-        "#,
-            r#"
-        //- /p1/src/rust-analyzer.toml
-        assist.emitMustUse = false
-        "#,
-            r#"
-        //- /p1/src/lib.rs
-        enum Value {
-            Number(i32),
-            Text(String),
-        }
-        "#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-"#,
-            r#"
-//- /p1/src/rust-analyzer.toml
-assist.emitMustUse = false
-"#,
-            r#"
-//- /p1/rust-analyzer.toml
-assist.emitMustUse = true
-"#,
-            r#"
-//- /p1/src/lib.rs
-enum Value {
-    Number(i32),
-    Text(String),
-}
-"#,
-        ],
-        vec!["p1"],
-        None,
-    );
-
-    assert!(server.query(QueryType::AssistEmitMustUse, 3));
-}
-
-/// If a root is non-local, so we cannot find what its parent is
-/// in our `config.local_root_parent_map`. So if any config should
-/// apply, it must be looked for starting from the client level.
-/// FIXME @alibektas : "locality" is according to ra that, which is simply in the file system.
-/// This doesn't really help us with what we want to achieve here.
-//     #[test]
-//     fn ratoml_non_local_crates_start_inheriting_from_client() {
-//         let server = RatomlTest::new(
-//             vec![
-//                 r#"
-// //- /p1/Cargo.toml
-// [package]
-// name = "p1"
-// version = "0.1.0"
-// edition = "2021"
-
-// [dependencies]
-// p2 = { path = "../p2" }
-// #,
-//                 r#"
-// //- /p1/src/lib.rs
-// enum Value {
-//     Number(i32),
-//     Text(String),
-// }
-
-// use p2;
-
-// pub fn add(left: usize, right: usize) -> usize {
-//     p2::add(left, right)
-// }
-
-// #[cfg(test)]
-// mod tests {
-//     use super::*;
-
-//     #[test]
-//     fn it_works() {
-//         let result = add(2, 2);
-//         assert_eq!(result, 4);
-//     }
-// }"#,
-//                 r#"
-// //- /p2/Cargo.toml
-// [package]
-// name = "p2"
-// version = "0.1.0"
-// edition = "2021"
-
-// # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-// [dependencies]
-// "#,
-//                 r#"
-// //- /p2/rust-analyzer.toml
-// # DEF
-// assist.emitMustUse = true
-// "#,
-//                 r#"
-// //- /p2/src/lib.rs
-// enum Value {
-//     Number(i32),
-//     Text(String),
-// }"#,
-//             ],
-//             vec!["p1", "p2"],
-//             None,
-//         );
-
-//         assert!(!server.query(QueryType::AssistEmitMustUse, 5));
-//     }
-
-/// Having a ratoml file at the root of a project enables
-/// configuring global level configurations as well.
-#[test]
-fn ratoml_in_root_is_global() {
-    let server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-        "#,
-            r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
-        "#,
-            r#"
-//- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
-fn main() {
-    let a = RandomTrait;
-}"#,
-        ],
-        vec![],
-        None,
-    );
-
-    server.query(QueryType::GlobalHover, 2);
-}
-
-#[test]
-fn ratoml_root_is_updateable() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-        "#,
-            r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
-        "#,
-            r#"
-//- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
-fn main() {
-    let a = RandomTrait;
-}"#,
-        ],
-        vec![],
-        None,
-    );
-
-    assert!(server.query(QueryType::GlobalHover, 2));
-    server.edit(1, RatomlTest::GLOBAL_TRAIT_ASSOC_ITEMS_ZERO.to_owned());
-    assert!(!server.query(QueryType::GlobalHover, 2));
-}
-
-#[test]
-fn ratoml_root_is_deletable() {
-    let mut server = RatomlTest::new(
-        vec![
-            r#"
-//- /p1/Cargo.toml
-[package]
-name = "p1"
-version = "0.1.0"
-edition = "2021"
-        "#,
-            r#"
-//- /rust-analyzer.toml
-hover.show.traitAssocItems = 4
-        "#,
-            r#"
-//- /p1/src/lib.rs
-trait RandomTrait {
-    type B;
-    fn abc() -> i32;
-    fn def() -> i64;
-}
-
-fn main() {
-    let a = RandomTrait;
-}"#,
-        ],
-        vec![],
-        None,
-    );
-
-    assert!(server.query(QueryType::GlobalHover, 2));
-    server.delete(1);
-    assert!(!server.query(QueryType::GlobalHover, 2));
-}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
new file mode 100644
index 00000000000..739da9c998d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -0,0 +1,1019 @@
+use crate::support::{Project, Server};
+use crate::testdir::TestDir;
+use lsp_types::{
+    notification::{DidChangeTextDocument, DidOpenTextDocument, DidSaveTextDocument},
+    request::{CodeActionRequest, HoverRequest},
+    CodeAction, CodeActionContext, CodeActionOrCommand, CodeActionParams, CodeActionResponse,
+    DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, Hover,
+    HoverParams, Position, TextDocumentContentChangeEvent, TextDocumentIdentifier,
+    TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
+    WorkDoneProgressParams,
+};
+use paths::Utf8PathBuf;
+
+use serde_json::json;
+
+enum QueryType {
+    AssistEmitMustUse,
+    /// A query whose config key is a part of the global configs, so that
+    /// testing for changes to this config means testing if global changes
+    /// take affect.
+    GlobalHover,
+}
+
+struct RatomlTest {
+    urls: Vec<Url>,
+    server: Server,
+    tmp_path: Utf8PathBuf,
+    user_config_dir: Utf8PathBuf,
+}
+
+impl RatomlTest {
+    const EMIT_MUST_USE: &'static str = r#"assist.emitMustUse = true"#;
+    const EMIT_MUST_NOT_USE: &'static str = r#"assist.emitMustUse = false"#;
+    const EMIT_MUST_USE_SNIPPET: &'static str = r#"
+
+impl Value {
+    #[must_use]
+    fn as_text(&self) -> Option<&String> {
+        if let Self::Text(v) = self {
+            Some(v)
+        } else {
+            None
+        }
+    }
+}"#;
+
+    const GLOBAL_TRAIT_ASSOC_ITEMS_ZERO: &'static str = r#"hover.show.traitAssocItems = 0"#;
+    const GLOBAL_TRAIT_ASSOC_ITEMS_SNIPPET: &'static str = r#"
+```rust
+p1
+```
+
+```rust
+trait RandomTrait {
+    type B;
+    fn abc() -> i32;
+    fn def() -> i64;
+}
+```"#;
+
+    fn new(
+        fixtures: Vec<&str>,
+        roots: Vec<&str>,
+        client_config: Option<serde_json::Value>,
+    ) -> Self {
+        let tmp_dir = TestDir::new();
+        let tmp_path = tmp_dir.path().to_owned();
+
+        let full_fixture = fixtures.join("\n");
+
+        let user_cnf_dir = TestDir::new();
+        let user_config_dir = user_cnf_dir.path().to_owned();
+
+        let mut project =
+            Project::with_fixture(&full_fixture).tmp_dir(tmp_dir).user_config_dir(user_cnf_dir);
+
+        for root in roots {
+            project = project.root(root);
+        }
+
+        if let Some(client_config) = client_config {
+            project = project.with_config(client_config);
+        }
+
+        let server = project.server().wait_until_workspace_is_loaded();
+
+        let mut case = Self { urls: vec![], server, tmp_path, user_config_dir };
+        let urls = fixtures.iter().map(|fixture| case.fixture_path(fixture)).collect::<Vec<_>>();
+        case.urls = urls;
+        case
+    }
+
+    fn fixture_path(&self, fixture: &str) -> Url {
+        let mut lines = fixture.trim().split('\n');
+
+        let mut path =
+            lines.next().expect("All files in a fixture are expected to have at least one line.");
+
+        if path.starts_with("//- minicore") {
+            path = lines.next().expect("A minicore line must be followed by a path.")
+        }
+
+        path = path.strip_prefix("//- ").expect("Path must be preceded by a //- prefix ");
+
+        let spl = path[1..].split('/');
+        let mut path = self.tmp_path.clone();
+
+        let mut spl = spl.into_iter();
+        if let Some(first) = spl.next() {
+            if first == "$$CONFIG_DIR$$" {
+                path = self.user_config_dir.clone();
+            } else {
+                path = path.join(first);
+            }
+        }
+        for piece in spl {
+            path = path.join(piece);
+        }
+
+        Url::parse(
+            format!(
+                "file://{}",
+                path.into_string().to_owned().replace("C:\\", "/c:/").replace('\\', "/")
+            )
+            .as_str(),
+        )
+        .unwrap()
+    }
+
+    fn create(&mut self, fixture_path: &str, text: String) {
+        let url = self.fixture_path(fixture_path);
+
+        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
+            text_document: TextDocumentItem {
+                uri: url.clone(),
+                language_id: "rust".to_owned(),
+                version: 0,
+                text: String::new(),
+            },
+        });
+
+        self.server.notification::<DidChangeTextDocument>(DidChangeTextDocumentParams {
+            text_document: VersionedTextDocumentIdentifier { uri: url, version: 0 },
+            content_changes: vec![TextDocumentContentChangeEvent {
+                range: None,
+                range_length: None,
+                text,
+            }],
+        });
+    }
+
+    fn delete(&mut self, file_idx: usize) {
+        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
+            text_document: TextDocumentItem {
+                uri: self.urls[file_idx].clone(),
+                language_id: "rust".to_owned(),
+                version: 0,
+                text: "".to_owned(),
+            },
+        });
+
+        // See if deleting ratoml file will make the config of interest to return to its default value.
+        self.server.notification::<DidSaveTextDocument>(DidSaveTextDocumentParams {
+            text_document: TextDocumentIdentifier { uri: self.urls[file_idx].clone() },
+            text: Some("".to_owned()),
+        });
+    }
+
+    fn edit(&mut self, file_idx: usize, text: String) {
+        self.server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
+            text_document: TextDocumentItem {
+                uri: self.urls[file_idx].clone(),
+                language_id: "rust".to_owned(),
+                version: 0,
+                text: String::new(),
+            },
+        });
+
+        self.server.notification::<DidChangeTextDocument>(DidChangeTextDocumentParams {
+            text_document: VersionedTextDocumentIdentifier {
+                uri: self.urls[file_idx].clone(),
+                version: 0,
+            },
+            content_changes: vec![TextDocumentContentChangeEvent {
+                range: None,
+                range_length: None,
+                text,
+            }],
+        });
+    }
+
+    fn query(&self, query: QueryType, source_file_idx: usize) -> bool {
+        match query {
+            QueryType::AssistEmitMustUse => {
+                let res = self.server.send_request::<CodeActionRequest>(CodeActionParams {
+                    text_document: TextDocumentIdentifier {
+                        uri: self.urls[source_file_idx].clone(),
+                    },
+                    range: lsp_types::Range {
+                        start: Position::new(2, 13),
+                        end: Position::new(2, 15),
+                    },
+                    context: CodeActionContext {
+                        diagnostics: vec![],
+                        only: None,
+                        trigger_kind: None,
+                    },
+                    work_done_progress_params: WorkDoneProgressParams { work_done_token: None },
+                    partial_result_params: lsp_types::PartialResultParams {
+                        partial_result_token: None,
+                    },
+                });
+
+                let res = serde_json::de::from_str::<CodeActionResponse>(res.to_string().as_str())
+                    .unwrap();
+
+                // The difference setting the new config key will cause can be seen in the lower layers of this nested response
+                // so here are some ugly unwraps and other obscure stuff.
+                let ca: CodeAction = res
+                    .into_iter()
+                    .find_map(|it| {
+                        if let CodeActionOrCommand::CodeAction(ca) = it {
+                            if ca.title.as_str() == "Generate an `as_` method for this enum variant"
+                            {
+                                return Some(ca);
+                            }
+                        }
+
+                        None
+                    })
+                    .unwrap();
+
+                if let lsp_types::DocumentChanges::Edits(edits) =
+                    ca.edit.unwrap().document_changes.unwrap()
+                {
+                    if let lsp_types::OneOf::Left(l) = &edits[0].edits[0] {
+                        return l.new_text.as_str() == RatomlTest::EMIT_MUST_USE_SNIPPET;
+                    }
+                }
+            }
+            QueryType::GlobalHover => {
+                let res = self.server.send_request::<HoverRequest>(HoverParams {
+                    work_done_progress_params: WorkDoneProgressParams { work_done_token: None },
+                    text_document_position_params: TextDocumentPositionParams {
+                        text_document: TextDocumentIdentifier {
+                            uri: self.urls[source_file_idx].clone(),
+                        },
+                        position: Position::new(7, 18),
+                    },
+                });
+                let res = serde_json::de::from_str::<Hover>(res.to_string().as_str()).unwrap();
+                assert!(matches!(res.contents, lsp_types::HoverContents::Markup(_)));
+                if let lsp_types::HoverContents::Markup(m) = res.contents {
+                    return m.value == RatomlTest::GLOBAL_TRAIT_ASSOC_ITEMS_SNIPPET;
+                }
+            }
+        }
+
+        panic!()
+    }
+}
+
+// /// Check if we are listening for changes in user's config file ( e.g on Linux `~/.config/rust-analyzer/.rust-analyzer.toml`)
+// #[test]
+// #[cfg(target_os = "windows")]
+// fn listen_to_user_config_scenario_windows() {
+//     todo!()
+// }
+
+// #[test]
+// #[cfg(target_os = "linux")]
+// fn listen_to_user_config_scenario_linux() {
+//     todo!()
+// }
+
+// #[test]
+// #[cfg(target_os = "macos")]
+// fn listen_to_user_config_scenario_macos() {
+//     todo!()
+// }
+
+/// Check if made changes have had any effect on
+/// the client config.
+#[test]
+fn ratoml_client_config_basic() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+        ],
+        vec!["p1"],
+        Some(json!({
+            "assist" : {
+                "emitMustUse" : true
+            }
+        })),
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 1));
+}
+
+/// Checks if client config can be modified.
+/// FIXME @alibektas : This test is atm not valid.
+/// Asking for client config from the client is a 2 way communication
+/// which we cannot imitate with the current slow-tests infrastructure.
+/// See rust-analyzer::handlers::notifications#197
+//     #[test]
+//     fn client_config_update() {
+//         setup();
+
+//         let server = RatomlTest::new(
+//             vec![
+//                 r#"
+// //- /p1/Cargo.toml
+// [package]
+// name = "p1"
+// version = "0.1.0"
+// edition = "2021"
+// "#,
+//                 r#"
+// //- /p1/src/lib.rs
+// enum Value {
+//     Number(i32),
+//     Text(String),
+// }"#,
+//             ],
+//             vec!["p1"],
+//             None,
+//         );
+
+//         assert!(!server.query(QueryType::AssistEmitMustUse, 1));
+
+//         // a.notification::<DidChangeConfiguration>(DidChangeConfigurationParams {
+//         //     settings: json!({
+//         //         "assists" : {
+//         //             "emitMustUse" : true
+//         //         }
+//         //     }),
+//         // });
+
+//         assert!(server.query(QueryType::AssistEmitMustUse, 1));
+//     }
+
+//     #[test]
+//     fn ratoml_create_ratoml_basic() {
+//         let server = RatomlTest::new(
+//             vec![
+//                 r#"
+// //- /p1/Cargo.toml
+// [package]
+// name = "p1"
+// version = "0.1.0"
+// edition = "2021"
+// "#,
+//                 r#"
+// //- /p1/rust-analyzer.toml
+// assist.emitMustUse = true
+// "#,
+//                 r#"
+// //- /p1/src/lib.rs
+// enum Value {
+//     Number(i32),
+//     Text(String),
+// }
+// "#,
+//             ],
+//             vec!["p1"],
+//             None,
+//         );
+
+//         assert!(server.query(QueryType::AssistEmitMustUse, 2));
+//     }
+
+#[test]
+fn ratoml_user_config_detected() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 2));
+}
+
+#[test]
+fn ratoml_create_user_config() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
+    server.create(
+        "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml",
+        RatomlTest::EMIT_MUST_USE.to_owned(),
+    );
+    assert!(server.query(QueryType::AssistEmitMustUse, 1));
+}
+
+#[test]
+fn ratoml_modify_user_config() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021""#,
+            r#"
+//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+assist.emitMustUse = true"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 1));
+    server.edit(2, String::new());
+    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
+}
+
+#[test]
+fn ratoml_delete_user_config() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021""#,
+            r#"
+//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
+assist.emitMustUse = true"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 1));
+    server.delete(2);
+    assert!(!server.query(QueryType::AssistEmitMustUse, 1));
+}
+// #[test]
+// fn delete_user_config() {
+//     todo!()
+// }
+
+// #[test]
+// fn modify_client_config() {
+//     todo!()
+// }
+
+#[test]
+fn ratoml_inherit_config_from_ws_root() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+#[test]
+fn ratoml_modify_ratoml_at_ws_root() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = false
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
+    server.edit(1, "assist.emitMustUse = true".to_owned());
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+#[test]
+fn ratoml_delete_ratoml_at_ws_root() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+    server.delete(1);
+    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+#[test]
+fn ratoml_add_immediate_child_to_ws_root() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+    server.create("//- /p1/p2/rust-analyzer.toml", RatomlTest::EMIT_MUST_NOT_USE.to_owned());
+    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+#[test]
+fn ratoml_rm_ws_root_ratoml_child_has_client_as_parent_now() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+    server.delete(1);
+    assert!(!server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+#[test]
+fn ratoml_crates_both_roots() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+workspace = { members = ["p2"] }
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/p2/Cargo.toml
+[package]
+name = "p2"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/p2/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+            r#"
+//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}"#,
+        ],
+        vec!["p1", "p2"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+    assert!(server.query(QueryType::AssistEmitMustUse, 4));
+}
+
+#[test]
+fn ratoml_multiple_ratoml_in_single_source_root() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+        //- /p1/Cargo.toml
+        [package]
+        name = "p1"
+        version = "0.1.0"
+        edition = "2021"
+        "#,
+            r#"
+        //- /p1/rust-analyzer.toml
+        assist.emitMustUse = true
+        "#,
+            r#"
+        //- /p1/src/rust-analyzer.toml
+        assist.emitMustUse = false
+        "#,
+            r#"
+        //- /p1/src/lib.rs
+        enum Value {
+            Number(i32),
+            Text(String),
+        }
+        "#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+"#,
+            r#"
+//- /p1/src/rust-analyzer.toml
+assist.emitMustUse = false
+"#,
+            r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+            r#"
+//- /p1/src/lib.rs
+enum Value {
+    Number(i32),
+    Text(String),
+}
+"#,
+        ],
+        vec!["p1"],
+        None,
+    );
+
+    assert!(server.query(QueryType::AssistEmitMustUse, 3));
+}
+
+/// If a root is non-local, so we cannot find what its parent is
+/// in our `config.local_root_parent_map`. So if any config should
+/// apply, it must be looked for starting from the client level.
+/// FIXME @alibektas : "locality" is according to ra that, which is simply in the file system.
+/// This doesn't really help us with what we want to achieve here.
+//     #[test]
+//     fn ratoml_non_local_crates_start_inheriting_from_client() {
+//         let server = RatomlTest::new(
+//             vec![
+//                 r#"
+// //- /p1/Cargo.toml
+// [package]
+// name = "p1"
+// version = "0.1.0"
+// edition = "2021"
+
+// [dependencies]
+// p2 = { path = "../p2" }
+// #,
+//                 r#"
+// //- /p1/src/lib.rs
+// enum Value {
+//     Number(i32),
+//     Text(String),
+// }
+
+// use p2;
+
+// pub fn add(left: usize, right: usize) -> usize {
+//     p2::add(left, right)
+// }
+
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+
+//     #[test]
+//     fn it_works() {
+//         let result = add(2, 2);
+//         assert_eq!(result, 4);
+//     }
+// }"#,
+//                 r#"
+// //- /p2/Cargo.toml
+// [package]
+// name = "p2"
+// version = "0.1.0"
+// edition = "2021"
+
+// # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+// [dependencies]
+// "#,
+//                 r#"
+// //- /p2/rust-analyzer.toml
+// # DEF
+// assist.emitMustUse = true
+// "#,
+//                 r#"
+// //- /p2/src/lib.rs
+// enum Value {
+//     Number(i32),
+//     Text(String),
+// }"#,
+//             ],
+//             vec!["p1", "p2"],
+//             None,
+//         );
+
+//         assert!(!server.query(QueryType::AssistEmitMustUse, 5));
+//     }
+
+/// Having a ratoml file at the root of a project enables
+/// configuring global level configurations as well.
+#[test]
+fn ratoml_in_root_is_global() {
+    let server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+        "#,
+            r#"
+//- /rust-analyzer.toml
+hover.show.traitAssocItems = 4
+        "#,
+            r#"
+//- /p1/src/lib.rs
+trait RandomTrait {
+    type B;
+    fn abc() -> i32;
+    fn def() -> i64;
+}
+
+fn main() {
+    let a = RandomTrait;
+}"#,
+        ],
+        vec![],
+        None,
+    );
+
+    server.query(QueryType::GlobalHover, 2);
+}
+
+#[test]
+fn ratoml_root_is_updateable() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+        "#,
+            r#"
+//- /rust-analyzer.toml
+hover.show.traitAssocItems = 4
+        "#,
+            r#"
+//- /p1/src/lib.rs
+trait RandomTrait {
+    type B;
+    fn abc() -> i32;
+    fn def() -> i64;
+}
+
+fn main() {
+    let a = RandomTrait;
+}"#,
+        ],
+        vec![],
+        None,
+    );
+
+    assert!(server.query(QueryType::GlobalHover, 2));
+    server.edit(1, RatomlTest::GLOBAL_TRAIT_ASSOC_ITEMS_ZERO.to_owned());
+    assert!(!server.query(QueryType::GlobalHover, 2));
+}
+
+#[test]
+fn ratoml_root_is_deletable() {
+    let mut server = RatomlTest::new(
+        vec![
+            r#"
+//- /p1/Cargo.toml
+[package]
+name = "p1"
+version = "0.1.0"
+edition = "2021"
+        "#,
+            r#"
+//- /rust-analyzer.toml
+hover.show.traitAssocItems = 4
+        "#,
+            r#"
+//- /p1/src/lib.rs
+trait RandomTrait {
+    type B;
+    fn abc() -> i32;
+    fn def() -> i64;
+}
+
+fn main() {
+    let a = RandomTrait;
+}"#,
+        ],
+        vec![],
+        None,
+    );
+
+    assert!(server.query(QueryType::GlobalHover, 2));
+    server.delete(1);
+    assert!(!server.query(QueryType::GlobalHover, 2));
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 17485ee3ae8..d12d0be5392 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -207,8 +207,8 @@ impl Project<'_> {
 
         change.change_client_config(self.config);
         let mut error_sink = ConfigError::default();
-        assert!(error_sink.is_empty());
-        config = config.apply_change(change, &mut error_sink);
+        assert!(error_sink.is_empty(), "{error_sink:?}");
+        (config, _) = config.apply_change(change, &mut error_sink);
         config.rediscover_workspaces();
 
         Server::new(tmp_dir.keep(), config)