Turn root_ratoml into workspace_ratomls

This commit is contained in:
Ali Bektas 2024-07-26 02:38:02 +02:00
parent 0ba6f4eda0
commit c4c6b64976
4 changed files with 84 additions and 75 deletions

View file

@ -779,11 +779,8 @@ pub struct Config {
/// Config node whose values apply to **every** Rust project.
user_config: Option<(GlobalLocalConfigInput, ConfigErrors)>,
/// 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<(GlobalLocalConfigInput, ConfigErrors)>,
/// TODO : This file can be used to make global changes while having only a workspace-wide scope.
workspace_ratoml_change: FxHashMap<SourceRootId, (GlobalLocalConfigInput, ConfigErrors)>,
/// For every `SourceRoot` there can be at most one RATOML file.
ratoml_files: FxHashMap<SourceRootId, (LocalConfigInput, ConfigErrors)>,
@ -917,38 +914,44 @@ impl Config {
should_update = true;
}
if let Some(change) = change.root_ratoml_change {
tracing::info!("updating root ra-toml config: {:#}", change);
#[allow(clippy::single_match)]
match toml::from_str(&change) {
Ok(table) => {
if let Some(change) = change.workspace_ratoml_change {
tracing::info!("updating root ra-toml config");
for (source_root_id, (_, text)) in change {
if let Some(text) = text {
let mut toml_errors = vec![];
validate_toml_table(
GlobalLocalConfigInput::FIELDS,
&table,
&mut String::new(),
&mut toml_errors,
);
config.root_ratoml = Some((
GlobalLocalConfigInput::from_toml(table, &mut toml_errors),
ConfigErrors(
toml_errors
.into_iter()
.map(|(a, b)| ConfigErrorInner::Toml { config_key: a, error: b })
.map(Arc::new)
.collect(),
),
));
should_update = true;
}
Err(e) => {
config.root_ratoml = Some((
GlobalLocalConfigInput::from_toml(toml::map::Map::default(), &mut vec![]),
ConfigErrors(vec![ConfigErrorInner::ParseError {
reason: e.message().to_owned(),
match toml::from_str(&text) {
Ok(table) => {
validate_toml_table(
GlobalLocalConfigInput::FIELDS,
&table,
&mut String::new(),
&mut toml_errors,
);
config.workspace_ratoml_change.insert(
source_root_id,
(
GlobalLocalConfigInput::from_toml(table, &mut toml_errors),
ConfigErrors(
toml_errors
.into_iter()
.map(|(a, b)| ConfigErrorInner::Toml {
config_key: a,
error: b,
})
.map(Arc::new)
.collect(),
),
),
);
should_update = true;
}
.into()]),
));
Err(e) => {
config.validation_errors.0.push(
ConfigErrorInner::ParseError { reason: e.message().to_owned() }
.into(),
);
}
}
}
}
}
@ -958,7 +961,6 @@ impl Config {
if let Some(text) = text {
let mut toml_errors = vec![];
tracing::info!("updating ra-toml config: {:#}", text);
#[allow(clippy::single_match)]
match toml::from_str(&text) {
Ok(table) => {
validate_toml_table(
@ -985,16 +987,10 @@ impl Config {
);
}
Err(e) => {
config.root_ratoml = Some((
GlobalLocalConfigInput::from_toml(
toml::map::Map::default(),
&mut vec![],
),
ConfigErrors(vec![ConfigErrorInner::ParseError {
reason: e.message().to_owned(),
}
.into()]),
));
config.validation_errors.0.push(
ConfigErrorInner::ParseError { reason: e.message().to_owned() }
.into(),
);
}
}
}
@ -1026,7 +1022,13 @@ impl Config {
.1
.0
.iter()
.chain(config.root_ratoml.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
.chain(
config
.workspace_ratoml_change
.values()
.into_iter()
.flat_map(|it| it.1 .0.iter()),
)
.chain(config.user_config.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
.chain(config.ratoml_files.values().flat_map(|it| it.1 .0.iter()))
.chain(config.validation_errors.0.iter())
@ -1055,8 +1057,8 @@ impl Config {
#[derive(Default, Debug)]
pub struct ConfigChange {
user_config_change: Option<Arc<str>>,
root_ratoml_change: Option<Arc<str>>,
client_config_change: Option<serde_json::Value>,
workspace_ratoml_change: Option<FxHashMap<SourceRootId, (VfsPath, Option<Arc<str>>)>>,
ratoml_file_change: Option<FxHashMap<SourceRootId, (VfsPath, Option<Arc<str>>)>>,
source_map_change: Option<Arc<FxHashMap<SourceRootId, SourceRootId>>>,
}
@ -1078,9 +1080,15 @@ impl ConfigChange {
self.user_config_change = content;
}
pub fn change_root_ratoml(&mut self, content: Option<Arc<str>>) {
assert!(self.root_ratoml_change.is_none()); // Otherwise it is a double write.
self.root_ratoml_change = content;
pub fn change_workspace_ratoml(
&mut self,
source_root: SourceRootId,
vfs_path: VfsPath,
content: Option<Arc<str>>,
) -> Option<(VfsPath, Option<Arc<str>>)> {
self.workspace_ratoml_change
.get_or_insert_with(Default::default)
.insert(source_root, (vfs_path, content))
}
pub fn change_client_config(&mut self, change: serde_json::Value) {
@ -1333,11 +1341,6 @@ impl Config {
// FIXME @alibektas : Temporary solution. I don't think this is right as at some point we may allow users to specify
// custom USER_CONFIG_PATHs which may also be relative.
let user_config_path = VfsPath::from(AbsPathBuf::assert(user_config_path));
let root_ratoml_path = {
let mut p = root_path.clone();
p.push("rust-analyzer.toml");
VfsPath::new_real_path(p.to_string())
};
Config {
caps: ClientCapabilities::new(caps),
@ -1352,10 +1355,9 @@ impl Config {
source_root_parent_map: Arc::new(FxHashMap::default()),
user_config: None,
user_config_path,
root_ratoml: None,
root_ratoml_path,
detached_files: Default::default(),
validation_errors: Default::default(),
workspace_ratoml_change: Default::default(),
}
}
@ -1398,10 +1400,6 @@ impl Config {
&self.root_path
}
pub fn root_ratoml_path(&self) -> &VfsPath {
&self.root_ratoml_path
}
pub fn caps(&self) -> &ClientCapabilities {
&self.caps
}
@ -3591,7 +3589,7 @@ mod tests {
let mut change = ConfigChange::default();
change.change_root_ratoml(Some(
change.change_user_config(Some(
toml::toml! {
[cargo.cfgs]
these = "these"

View file

@ -10,6 +10,7 @@ use flycheck::{project_json, FlycheckHandle};
use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
use ide_db::base_db::{CrateId, ProcMacroPaths, SourceDatabaseExt};
use itertools::Itertools;
use load_cargo::SourceRootConfig;
use lsp_types::{SemanticTokens, Url};
use nohash_hasher::IntMap;
@ -22,7 +23,7 @@ use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, Worksp
use rustc_hash::{FxHashMap, FxHashSet};
use tracing::{span, trace, Level};
use triomphe::Arc;
use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs};
use vfs::{AbsPathBuf, AnchoredPathBuf, ChangeKind, Vfs, VfsPath};
use crate::{
config::{Config, ConfigChange, ConfigErrors},
@ -382,26 +383,37 @@ impl GlobalState {
{
let config_change = {
let user_config_path = self.config.user_config_path();
let root_ratoml_path = self.config.root_ratoml_path();
let mut change = ConfigChange::default();
let db = self.analysis_host.raw_database();
// FIXME @alibektas : This is silly. There is abs no reason to use VfsPaths when there is SourceRoots. But how
// do I resolve a "workspace_root" to its corresponding id without having to rely on a cargo.toml's ( or project json etc.) file id?
let workspace_roots = self
.workspaces
.iter()
.map(|ws| VfsPath::from(ws.workspace_root().to_owned()))
.collect_vec();
for (file_id, (_change_kind, vfs_path)) in modified_ratoml_files {
if vfs_path == *user_config_path {
change.change_user_config(Some(db.file_text(file_id)));
continue;
}
if vfs_path == *root_ratoml_path {
change.change_root_ratoml(Some(db.file_text(file_id)));
// If change has been made to a ratoml file that
// belongs to a non-local source root, we will ignore it.
let sr_id = db.file_source_root(file_id);
let sr = db.source_root(sr_id);
if workspace_roots.contains(&vfs_path) {
change.change_workspace_ratoml(
sr_id,
vfs_path,
Some(db.file_text(file_id)),
);
continue;
}
// 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,
@ -430,7 +442,7 @@ impl GlobalState {
if should_update {
self.update_configuration(config);
} else {
// No global or client level config was changed. So we can just naively replace config.
// No global or client level config was changed. So we can naively replace config.
self.config = Arc::new(config);
}
}

View file

@ -541,7 +541,6 @@ impl GlobalState {
watchers.extend(
iter::once(self.config.user_config_path().as_path())
.chain(iter::once(self.config.root_ratoml_path().as_path()))
.chain(self.workspaces.iter().map(|ws| ws.manifest().map(ManifestPath::as_ref)))
.flatten()
.map(|glob_pattern| lsp_types::FileSystemWatcher {

View file

@ -808,7 +808,7 @@ enum Value {
/// Having a ratoml file at the root of a project enables
/// configuring global level configurations as well.
#[test]
#[ignore = "Root ratomls are not being looked for on startup. Fix this."]
// #[ignore = "Root ratomls are not being looked for on startup. Fix this."]
fn ratoml_in_root_is_global() {
let server = RatomlTest::new(
vec![