@@ -779,7 +779,6 @@ pub struct Config {
779779 /// | Windows | `{FOLDERID_RoamingAppData}` | C:\Users\Alice\AppData\Roaming |
780780 user_config_path : VfsPath ,
781781
782- /// FIXME @alibektas : Change this to sth better.
783782 /// Config node whose values apply to **every** Rust project.
784783 user_config : Option < ( GlobalLocalConfigInput , ConfigErrors ) > ,
785784
@@ -795,6 +794,12 @@ pub struct Config {
795794 /// Clone of the value that is stored inside a `GlobalState`.
796795 source_root_parent_map : Arc < FxHashMap < SourceRootId , SourceRootId > > ,
797796
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+
798803 detached_files : Vec < AbsPathBuf > ,
799804}
800805
@@ -824,6 +829,7 @@ impl Config {
824829 /// The return tuple's bool component signals whether the `GlobalState` should call its `update_configuration()` method.
825830 fn apply_change_with_sink ( & self , change : ConfigChange ) -> ( Config , bool ) {
826831 let mut config = self . clone ( ) ;
832+ config. validation_errors = ConfigErrors :: default ( ) ;
827833
828834 let mut should_update = false ;
829835
@@ -852,6 +858,7 @@ impl Config {
852858
853859 if let Some ( mut json) = change. client_config_change {
854860 tracing:: info!( "updating config from JSON: {:#}" , json) ;
861+
855862 if !( json. is_null ( ) || json. as_object ( ) . map_or ( false , |it| it. is_empty ( ) ) ) {
856863 let mut json_errors = vec ! [ ] ;
857864 let detached_files = get_field_json :: < Vec < Utf8PathBuf > > (
@@ -867,6 +874,37 @@ impl Config {
867874
868875 patch_old_style:: patch_json_for_outdated_configs ( & mut json) ;
869876
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+ }
870908 config. client_config = (
871909 FullConfigInput :: from_json ( json, & mut json_errors) ,
872910 ConfigErrors (
@@ -906,8 +944,15 @@ impl Config {
906944 ) ) ;
907945 should_update = true ;
908946 }
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+ }
911956 }
912957 }
913958
@@ -942,8 +987,18 @@ impl Config {
942987 ) ,
943988 ) ;
944989 }
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+ }
9471002 }
9481003 }
9491004 }
@@ -953,48 +1008,13 @@ impl Config {
9531008 config. source_root_parent_map = source_root_map;
9541009 }
9551010
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+ } ) ) ;
9891016 }
9901017
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- // }
9981018 ( config, should_update)
9991019 }
10001020
@@ -1012,6 +1032,7 @@ impl Config {
10121032 . chain ( config. root_ratoml . as_ref ( ) . into_iter ( ) . flat_map ( |it| it. 1 . 0 . iter ( ) ) )
10131033 . chain ( config. user_config . as_ref ( ) . into_iter ( ) . flat_map ( |it| it. 1 . 0 . iter ( ) ) )
10141034 . chain ( config. ratoml_files . values ( ) . flat_map ( |it| it. 1 . 0 . iter ( ) ) )
1035+ . chain ( config. validation_errors . 0 . iter ( ) )
10151036 . cloned ( )
10161037 . collect ( ) ,
10171038 ) ;
@@ -1259,9 +1280,10 @@ pub struct ClientCommandsConfig {
12591280pub enum ConfigErrorInner {
12601281 Json { config_key : String , error : serde_json:: Error } ,
12611282 Toml { config_key : String , error : toml:: de:: Error } ,
1283+ ParseError { reason : String } ,
12621284}
12631285
1264- #[ derive( Clone , Debug ) ]
1286+ #[ derive( Clone , Debug , Default ) ]
12651287pub struct ConfigErrors ( Vec < Arc < ConfigErrorInner > > ) ;
12661288
12671289impl ConfigErrors {
@@ -1283,6 +1305,7 @@ impl fmt::Display for ConfigErrors {
12831305 f ( & ": " ) ?;
12841306 f ( e)
12851307 }
1308+ ConfigErrorInner :: ParseError { reason } => f ( reason) ,
12861309 } ) ;
12871310 write ! ( f, "invalid config value{}:\n {}" , if self . 0 . len( ) == 1 { "" } else { "s" } , errors)
12881311 }
@@ -1336,6 +1359,7 @@ impl Config {
13361359 root_ratoml : None ,
13371360 root_ratoml_path,
13381361 detached_files : Default :: default ( ) ,
1362+ validation_errors : Default :: default ( ) ,
13391363 }
13401364 }
13411365
@@ -2575,6 +2599,7 @@ macro_rules! _impl_for_config_data {
25752599 }
25762600 }
25772601
2602+
25782603 & self . default_config. global. $field
25792604 }
25802605 ) *
@@ -3304,7 +3329,7 @@ fn validate_toml_table(
33043329 ptr. push_str ( k) ;
33053330
33063331 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
33083333 toml:: Value :: Table ( _) if verify ( ptr) => ( ) ,
33093334 toml:: Value :: Table ( table) => validate_toml_table ( known_ptrs, table, ptr, error_sink) ,
33103335 _ if !verify ( ptr) => error_sink
0 commit comments