Skip to content

Commit

Permalink
Watch for user config ratoml
Browse files Browse the repository at this point in the history
  • Loading branch information
alibektas committed Sep 7, 2024
1 parent c2ba828 commit 9f70fb3
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 37 deletions.
24 changes: 22 additions & 2 deletions crates/load-cargo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ pub fn load_workspace(
.collect()
};

let project_folders = ProjectFolders::new(std::slice::from_ref(&ws), &[]);
let project_folders = ProjectFolders::new(std::slice::from_ref(&ws), &[], None);
loader.set_config(vfs::loader::Config {
load: project_folders.load,
watch: vec![],
Expand Down Expand Up @@ -153,7 +153,11 @@ pub struct ProjectFolders {
}

impl ProjectFolders {
pub fn new(workspaces: &[ProjectWorkspace], global_excludes: &[AbsPathBuf]) -> ProjectFolders {
pub fn new(
workspaces: &[ProjectWorkspace],
global_excludes: &[AbsPathBuf],
user_config_dir_path: Option<&'static AbsPath>,
) -> ProjectFolders {
let mut res = ProjectFolders::default();
let mut fsc = FileSetConfig::builder();
let mut local_filesets = vec![];
Expand Down Expand Up @@ -286,6 +290,22 @@ impl ProjectFolders {
}
}

if let Some(user_config_path) = user_config_dir_path {
let ratoml_path = {
let mut p = user_config_path.to_path_buf();
p.push("rust-analyzer.toml");
p
};

let file_set_roots: Vec<VfsPath> = vec![VfsPath::from(ratoml_path.to_owned())];
let entry = vfs::loader::Entry::Files(vec![ratoml_path.to_owned()]);

res.watch.push(res.load.len());
res.load.push(entry);
local_filesets.push(fsc.len() as u64);
fsc.add_file_set(file_set_roots)
}

let fsc = fsc.build();
res.source_root_config = SourceRootConfig { fsc, local_filesets };

Expand Down
14 changes: 3 additions & 11 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,22 +761,14 @@ impl std::ops::Deref for Config {
}

impl Config {
/// 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.
/// This path is equal to:
///
/// |Platform | Value | Example |
/// | ------- | ------------------------------------- | ---------------------------------------- |
/// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config | /home/alice/.config |
/// | macOS | `$HOME`/Library/Application Support | /Users/Alice/Library/Application Support |
/// | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
pub fn user_config_path() -> Option<&'static AbsPath> {
/// Path to the user configuration dir. This can be seen as a generic way to define what would be `$XDG_CONFIG_HOME/rust-analyzer` in Linux.
pub fn user_config_dir_path() -> Option<&'static AbsPath> {
static USER_CONFIG_PATH: LazyLock<Option<AbsPathBuf>> = LazyLock::new(|| {
let user_config_path = if let Some(path) = env::var_os("__TEST_RA_USER_CONFIG_DIR") {
std::path::PathBuf::from(path)
} else {
dirs::config_dir()?.join("rust-analyzer")
}
.join("rust-analyzer.toml");
};
Some(AbsPathBuf::assert_utf8(user_config_path))
});
USER_CONFIG_PATH.as_deref()
Expand Down
11 changes: 9 additions & 2 deletions crates/rust-analyzer/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,14 @@ impl GlobalState {
|| !self.config.same_source_root_parent_map(&self.local_roots_parent_map)
{
let config_change = {
let user_config_path = Config::user_config_path();
let user_config_path = {
let mut p = Config::user_config_dir_path().unwrap().to_path_buf();
p.push("rust-analyzer.toml");
p
};

let user_config_abs_path = Some(user_config_path.as_path());

let mut change = ConfigChange::default();
let db = self.analysis_host.raw_database();

Expand All @@ -399,7 +406,7 @@ impl GlobalState {
.collect_vec();

for (file_id, (_change_kind, vfs_path)) in modified_ratoml_files {
if vfs_path.as_path() == user_config_path {
if vfs_path.as_path() == user_config_abs_path {
change.change_user_config(Some(db.file_text(file_id)));
continue;
}
Expand Down
8 changes: 6 additions & 2 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ impl GlobalState {
}

watchers.extend(
iter::once(Config::user_config_path())
iter::once(Config::user_config_dir_path())
.chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
.flatten()
.map(|glob_pattern| lsp_types::FileSystemWatcher {
Expand All @@ -606,7 +606,11 @@ impl GlobalState {
}

let files_config = self.config.files();
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
let project_folders = ProjectFolders::new(
&self.workspaces,
&files_config.exclude,
Config::user_config_dir_path().to_owned(),
);

if (self.proc_macro_clients.is_empty() || !same_workspaces)
&& self.config.expand_proc_macros()
Expand Down
44 changes: 27 additions & 17 deletions crates/rust-analyzer/tests/slow-tests/ratoml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ impl RatomlTest {
fixtures: Vec<&str>,
roots: Vec<&str>,
client_config: Option<serde_json::Value>,
) -> Self {
RatomlTest::new_with_lock(fixtures, roots, client_config, false)
}

fn new_locked(
fixtures: Vec<&str>,
roots: Vec<&str>,
client_config: Option<serde_json::Value>,
) -> Self {
RatomlTest::new_with_lock(fixtures, roots, client_config, true)
}

fn new_with_lock(
fixtures: Vec<&str>,
roots: Vec<&str>,
client_config: Option<serde_json::Value>,
prelock: bool,
) -> Self {
let tmp_dir = TestDir::new();
let tmp_path = tmp_dir.path().to_owned();
Expand All @@ -46,7 +63,7 @@ impl RatomlTest {
project = project.with_config(client_config);
}

let server = project.server().wait_until_workspace_is_loaded();
let server = project.server_with_lock(prelock).wait_until_workspace_is_loaded();

let mut case = Self { urls: vec![], server, tmp_path };
let urls = fixtures.iter().map(|fixture| case.fixture_path(fixture)).collect::<Vec<_>>();
Expand All @@ -72,7 +89,7 @@ impl RatomlTest {
let mut spl = spl.into_iter();
if let Some(first) = spl.next() {
if first == "$$CONFIG_DIR$$" {
path = Config::user_config_path().unwrap().to_path_buf().into();
path = Config::user_config_dir_path().unwrap().to_path_buf().into();
} else {
path = path.join(first);
}
Expand Down Expand Up @@ -285,16 +302,15 @@ enum Value {
// }

#[test]
#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_user_config_detected() {
if skip_slow_tests() {
return;
}

let server = RatomlTest::new(
let server = RatomlTest::new_locked(
vec![
r#"
//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true
"#,
r#"
Expand Down Expand Up @@ -322,13 +338,12 @@ enum Value {
}

#[test]
#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_create_user_config() {
if skip_slow_tests() {
return;
}

let mut server = RatomlTest::new(
let mut server = RatomlTest::new_locked(
vec![
r#"
//- /p1/Cargo.toml
Expand All @@ -353,10 +368,7 @@ enum Value {
1,
InternalTestingFetchConfigResponse::AssistEmitMustUse(false),
);
server.create(
"//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml",
RatomlTest::EMIT_MUST_USE.to_owned(),
);
server.create("//- /$$CONFIG_DIR$$/rust-analyzer.toml", RatomlTest::EMIT_MUST_USE.to_owned());
server.query(
InternalTestingFetchConfigOption::AssistEmitMustUse,
1,
Expand All @@ -365,13 +377,12 @@ enum Value {
}

#[test]
#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_modify_user_config() {
if skip_slow_tests() {
return;
}

let mut server = RatomlTest::new(
let mut server = RatomlTest::new_locked(
vec![
r#"
//- /p1/Cargo.toml
Expand All @@ -386,7 +397,7 @@ enum Value {
Text(String),
}"#,
r#"
//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true"#,
],
vec!["p1"],
Expand All @@ -407,13 +418,12 @@ assist.emitMustUse = true"#,
}

#[test]
#[ignore = "the user config is currently not being watched on startup, fix this"]
fn ratoml_delete_user_config() {
if skip_slow_tests() {
return;
}

let mut server = RatomlTest::new(
let mut server = RatomlTest::new_locked(
vec![
r#"
//- /p1/Cargo.toml
Expand All @@ -428,7 +438,7 @@ enum Value {
Text(String),
}"#,
r#"
//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml
//- /$$CONFIG_DIR$$/rust-analyzer.toml
assist.emitMustUse = true"#,
],
vec!["p1"],
Expand Down
30 changes: 27 additions & 3 deletions crates/rust-analyzer/tests/slow-tests/support.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{
cell::{Cell, RefCell},
fs,
env, fs,
sync::Once,
time::Duration,
};
Expand Down Expand Up @@ -85,7 +85,30 @@ impl Project<'_> {
}

pub(crate) fn server(self) -> Server {
Project::server_with_lock(self, false)
}

/// `prelock` : Forcefully acquire a lock that will maintain the path to the config dir throughout the whole test.
///
/// When testing we set the user config dir by setting an envvar `__TEST_RA_USER_CONFIG_DIR`.
/// This value must be maintained until the end of a test case. When tests run in parallel
/// this value may change thus making the tests flaky. As such, we use a `MutexGuard` that locks
/// the process until `Server` is dropped. To optimize parallelization we use a lock only when it is
/// needed, that is when a test uses config directory to do stuff. Our naive approach is to use a lock
/// if there is a path to config dir in the test fixture. However, in certain cases we create a
/// file in the config dir after server is run, something where our naive approach comes short.
/// Using a `prelock` allows us to force a lock when we know we need it.
pub(crate) fn server_with_lock(self, prelock: bool) -> Server {
static CONFIG_DIR_LOCK: Mutex<()> = Mutex::new(());

let mut config_dir_guard = if prelock {
let v = Some(CONFIG_DIR_LOCK.lock());
env::set_var("__TEST_RA_USER_CONFIG_DIR", TestDir::new().path());
v
} else {
None
};

let tmp_dir = self.tmp_dir.unwrap_or_else(|| {
if self.root_dir_contains_symlink {
TestDir::new_symlink()
Expand Down Expand Up @@ -117,13 +140,14 @@ impl Project<'_> {
assert!(mini_core.is_none());
assert!(toolchain.is_none());

let mut config_dir_guard = None;
for entry in fixture {
if let Some(pth) = entry.path.strip_prefix("/$$CONFIG_DIR$$") {
if config_dir_guard.is_none() {
config_dir_guard = Some(CONFIG_DIR_LOCK.lock());
env::set_var("__TEST_RA_USER_CONFIG_DIR", TestDir::new().path());
}
let path = Config::user_config_path().unwrap().join(&pth['/'.len_utf8()..]);

let path = Config::user_config_dir_path().unwrap().join(&pth['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
} else {
Expand Down

0 comments on commit 9f70fb3

Please sign in to comment.