@@ -779,7 +779,6 @@ pub struct Config {
779
779
/// | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
780
780
user_config_path: VfsPath,
781
781
782
- /// FIXME @alibektas : Change this to sth better.
783
782
/// Config node whose values apply to **every** Rust project.
784
783
user_config: Option<(GlobalLocalConfigInput, ConfigErrors)>,
785
784
@@ -795,6 +794,12 @@ pub struct Config {
795
794
/// Clone of the value that is stored inside a `GlobalState`.
796
795
source_root_parent_map: Arc<FxHashMap<SourceRootId, SourceRootId>>,
797
796
797
+ /// Use case : It is an error to have an empty value for `check_command`.
798
+ /// Since it is a `global` command at the moment, its final value can only be determined by
799
+ /// traversing through `global` configs and the `client` config. However the non-null value constraint
800
+ /// is config level agnostic, so this requires an independent error storage
801
+ validation_errors: ConfigErrors,
802
+
798
803
detached_files: Vec<AbsPathBuf>,
799
804
}
800
805
@@ -824,6 +829,7 @@ impl Config {
824
829
/// The return tuple's bool component signals whether the `GlobalState` should call its `update_configuration()` method.
825
830
fn apply_change_with_sink(&self, change: ConfigChange) -> (Config, bool) {
826
831
let mut config = self.clone();
832
+ config.validation_errors = ConfigErrors::default();
827
833
828
834
let mut should_update = false;
829
835
@@ -852,6 +858,7 @@ impl Config {
852
858
853
859
if let Some(mut json) = change.client_config_change {
854
860
tracing::info!("updating config from JSON: {:#}", json);
861
+
855
862
if !(json.is_null() || json.as_object().map_or(false, |it| it.is_empty())) {
856
863
let mut json_errors = vec![];
857
864
let detached_files = get_field_json::<Vec<Utf8PathBuf>>(
@@ -867,6 +874,37 @@ impl Config {
867
874
868
875
patch_old_style::patch_json_for_outdated_configs(&mut json);
869
876
877
+ // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`.
878
+ config.snippets.clear();
879
+
880
+ let snips = self.completion_snippets_custom().to_owned();
881
+
882
+ for (name, def) in snips.iter() {
883
+ if def.prefix.is_empty() && def.postfix.is_empty() {
884
+ continue;
885
+ }
886
+ let scope = match def.scope {
887
+ SnippetScopeDef::Expr => SnippetScope::Expr,
888
+ SnippetScopeDef::Type => SnippetScope::Type,
889
+ SnippetScopeDef::Item => SnippetScope::Item,
890
+ };
891
+ match Snippet::new(
892
+ &def.prefix,
893
+ &def.postfix,
894
+ &def.body,
895
+ def.description.as_ref().unwrap_or(name),
896
+ &def.requires,
897
+ scope,
898
+ ) {
899
+ Some(snippet) => config.snippets.push(snippet),
900
+ None => json_errors.push((
901
+ name.to_owned(),
902
+ <serde_json::Error as serde::de::Error>::custom(format!(
903
+ "snippet {name} is invalid or triggers are missing",
904
+ )),
905
+ )),
906
+ }
907
+ }
870
908
config.client_config = (
871
909
FullConfigInput::from_json(json, &mut json_errors),
872
910
ConfigErrors(
@@ -906,8 +944,15 @@ impl Config {
906
944
));
907
945
should_update = true;
908
946
}
909
- // FIXME
910
- Err(_) => (),
947
+ Err(e) => {
948
+ config.root_ratoml = Some((
949
+ GlobalLocalConfigInput::from_toml(toml::map::Map::default(), &mut vec![]),
950
+ ConfigErrors(vec![ConfigErrorInner::ParseError {
951
+ reason: e.message().to_owned(),
952
+ }
953
+ .into()]),
954
+ ));
955
+ }
911
956
}
912
957
}
913
958
@@ -942,8 +987,18 @@ impl Config {
942
987
),
943
988
);
944
989
}
945
- // FIXME
946
- Err(_) => (),
990
+ Err(e) => {
991
+ config.root_ratoml = Some((
992
+ GlobalLocalConfigInput::from_toml(
993
+ toml::map::Map::default(),
994
+ &mut vec![],
995
+ ),
996
+ ConfigErrors(vec![ConfigErrorInner::ParseError {
997
+ reason: e.message().to_owned(),
998
+ }
999
+ .into()]),
1000
+ ));
1001
+ }
947
1002
}
948
1003
}
949
1004
}
@@ -953,48 +1008,13 @@ impl Config {
953
1008
config.source_root_parent_map = source_root_map;
954
1009
}
955
1010
956
- // IMPORTANT : This holds as long as ` completion_snippets_custom` is declared `client`.
957
- config.snippets.clear();
958
-
959
- let snips = self.completion_snippets_custom().to_owned();
960
-
961
- for (name, def) in snips.iter() {
962
- if def.prefix.is_empty() && def.postfix.is_empty() {
963
- continue;
964
- }
965
- let scope = match def.scope {
966
- SnippetScopeDef::Expr => SnippetScope::Expr,
967
- SnippetScopeDef::Type => SnippetScope::Type,
968
- SnippetScopeDef::Item => SnippetScope::Item,
969
- };
970
- #[allow(clippy::single_match)]
971
- match Snippet::new(
972
- &def.prefix,
973
- &def.postfix,
974
- &def.body,
975
- def.description.as_ref().unwrap_or(name),
976
- &def.requires,
977
- scope,
978
- ) {
979
- Some(snippet) => config.snippets.push(snippet),
980
- // FIXME
981
- // None => error_sink.0.push(ConfigErrorInner::Json {
982
- // config_key: "".to_owned(),
983
- // error: <serde_json::Error as serde::de::Error>::custom(format!(
984
- // "snippet {name} is invalid or triggers are missing",
985
- // )),
986
- // }),
987
- None => (),
988
- }
1011
+ if config.check_command().is_empty() {
1012
+ config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json {
1013
+ config_key: "/check/command".to_owned(),
1014
+ error: serde_json::Error::custom("expected a non-empty string"),
1015
+ }));
989
1016
}
990
1017
991
- // FIXME: bring this back
992
- // if config.check_command().is_empty() {
993
- // error_sink.0.push(ConfigErrorInner::Json {
994
- // config_key: "/check/command".to_owned(),
995
- // error: serde_json::Error::custom("expected a non-empty string"),
996
- // });
997
- // }
998
1018
(config, should_update)
999
1019
}
1000
1020
@@ -1012,6 +1032,7 @@ impl Config {
1012
1032
.chain(config.root_ratoml.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
1013
1033
.chain(config.user_config.as_ref().into_iter().flat_map(|it| it.1 .0.iter()))
1014
1034
.chain(config.ratoml_files.values().flat_map(|it| it.1 .0.iter()))
1035
+ .chain(config.validation_errors.0.iter())
1015
1036
.cloned()
1016
1037
.collect(),
1017
1038
);
@@ -1259,9 +1280,10 @@ pub struct ClientCommandsConfig {
1259
1280
pub enum ConfigErrorInner {
1260
1281
Json { config_key: String, error: serde_json::Error },
1261
1282
Toml { config_key: String, error: toml::de::Error },
1283
+ ParseError { reason: String },
1262
1284
}
1263
1285
1264
- #[derive(Clone, Debug)]
1286
+ #[derive(Clone, Debug, Default )]
1265
1287
pub struct ConfigErrors(Vec<Arc<ConfigErrorInner>>);
1266
1288
1267
1289
impl ConfigErrors {
@@ -1283,6 +1305,7 @@ impl fmt::Display for ConfigErrors {
1283
1305
f(&": ")?;
1284
1306
f(e)
1285
1307
}
1308
+ ConfigErrorInner::ParseError { reason } => f(reason),
1286
1309
});
1287
1310
write!(f, "invalid config value{}:\n{}", if self.0.len() == 1 { "" } else { "s" }, errors)
1288
1311
}
@@ -1336,6 +1359,7 @@ impl Config {
1336
1359
root_ratoml: None,
1337
1360
root_ratoml_path,
1338
1361
detached_files: Default::default(),
1362
+ validation_errors: Default::default(),
1339
1363
}
1340
1364
}
1341
1365
@@ -2575,6 +2599,7 @@ macro_rules! _impl_for_config_data {
2575
2599
}
2576
2600
}
2577
2601
2602
+
2578
2603
&self.default_config.global.$field
2579
2604
}
2580
2605
)*
@@ -3304,7 +3329,7 @@ fn validate_toml_table(
3304
3329
ptr.push_str(k);
3305
3330
3306
3331
match v {
3307
- // This is a table config, any entry in it is therefor valid
3332
+ // This is a table config, any entry in it is therefore valid
3308
3333
toml::Value::Table(_) if verify(ptr) => (),
3309
3334
toml::Value::Table(table) => validate_toml_table(known_ptrs, table, ptr, error_sink),
3310
3335
_ if !verify(ptr) => error_sink
0 commit comments