@@ -515,9 +515,13 @@ pub struct ConfigEntryRaw {
515
515
#[ serde( default , serialize_with = "serialize_option_as_default" ) ]
516
516
add_workspace_addon_path : Option < Sourced < bool > > ,
517
517
518
+
518
519
#[ serde( default , rename( serialize = "$version" , deserialize = "$version" ) , serialize_with = "serialize_option_as_default" ) ]
519
520
version : Option < Sourced < String > > ,
520
521
522
+ #[ serde( default , rename( serialize = "$base" , deserialize = "$base" ) , serialize_with = "serialize_option_as_default" ) ]
523
+ base : Option < Sourced < String > > ,
524
+
521
525
#[ serde( default ) ]
522
526
diagnostic_settings : HashMap < DiagnosticCode , Sourced < DiagnosticSetting > > ,
523
527
@@ -543,6 +547,7 @@ impl Default for ConfigEntryRaw {
543
547
auto_refresh_delay : None ,
544
548
add_workspace_addon_path : None ,
545
549
version : None ,
550
+ base : None ,
546
551
diagnostic_settings : Default :: default ( ) ,
547
552
abstract_ : false ,
548
553
}
@@ -653,10 +658,13 @@ fn process_paths(
653
658
ws_folders : & HashMap < String , String > ,
654
659
workspace_name : Option < & String > ,
655
660
) {
656
- let var_map: HashMap < String , String > = match entry. version . clone ( ) {
657
- Some ( v) => HashMap :: from ( [ ( S ! ( "version" ) , v. value ( ) . clone ( ) ) ] ) ,
658
- None => HashMap :: new ( ) ,
659
- } ;
661
+ let mut var_map: HashMap < String , String > = HashMap :: new ( ) ;
662
+ if let Some ( v) = entry. version . clone ( ) {
663
+ var_map. insert ( S ! ( "version" ) , v. value ( ) . clone ( ) ) ;
664
+ }
665
+ if let Some ( b) = entry. base . clone ( ) {
666
+ var_map. insert ( S ! ( "base" ) , b. value ( ) . clone ( ) ) ;
667
+ }
660
668
entry. odoo_path = entry. odoo_path . as_ref ( )
661
669
. and_then ( |p| fill_or_canonicalize ( p, ws_folders, workspace_name, & is_odoo_path, var_map. clone ( ) ) ) ;
662
670
@@ -788,6 +796,7 @@ fn apply_merge(child: &ConfigEntryRaw, parent: &ConfigEntryRaw) -> ConfigEntryRa
788
796
let auto_refresh_delay = child. auto_refresh_delay . clone ( ) . or ( parent. auto_refresh_delay . clone ( ) ) ;
789
797
let add_workspace_addon_path = child. add_workspace_addon_path . clone ( ) . or ( parent. add_workspace_addon_path . clone ( ) ) ;
790
798
let version = child. version . clone ( ) . or ( parent. version . clone ( ) ) ;
799
+ let base = child. base . clone ( ) . or ( parent. base . clone ( ) ) ;
791
800
let diagnostic_settings = merge_sourced_diagnostic_setting_map ( & child. diagnostic_settings , & parent. diagnostic_settings ) ;
792
801
793
802
ConfigEntryRaw {
@@ -806,6 +815,7 @@ fn apply_merge(child: &ConfigEntryRaw, parent: &ConfigEntryRaw) -> ConfigEntryRa
806
815
auto_refresh_delay,
807
816
add_workspace_addon_path,
808
817
version,
818
+ base,
809
819
diagnostic_settings : diagnostic_settings,
810
820
..Default :: default ( )
811
821
}
@@ -939,42 +949,56 @@ fn load_config_from_workspace(
939
949
break ;
940
950
}
941
951
}
942
- let mut merged_config = process_config ( merged_config, ws_folders, Some ( workspace_name) ) ?;
943
-
944
- for entry in merged_config. values_mut ( ) {
945
- if entry. abstract_ { continue ; }
946
- if ( matches ! ( entry. add_workspace_addon_path. as_ref( ) . map( |a| a. value) , Some ( true ) ) || entry. addons_paths . is_none ( ) ) && is_addon_path ( workspace_path) {
947
- let addon_path = Sourced { value : workspace_path. clone ( ) , sources : HashSet :: from ( [ S ! ( format!( "$workspaceFolder:{workspace_name}" ) ) ] ) , ..Default :: default ( ) } ;
948
- match entry. addons_paths {
949
- Some ( ref mut paths) => paths. push ( addon_path) ,
950
- None => entry. addons_paths = Some ( vec ! [ addon_path] ) ,
952
+ let mut new_configs = vec ! [ ] ;
953
+ for config in merged_config. values_mut ( ) {
954
+ // Make $base always an absolute path
955
+ if let Some ( base_var) = config. base . clone ( ) {
956
+ let base_path = PathBuf :: from ( base_var. value ( ) ) ;
957
+
958
+ // If $base ends with ${detectVersion}, match workspace path and set $version using path components
959
+ if base_path. components ( ) . last ( ) . map ( |c| c. as_os_str ( ) . to_string_lossy ( ) == "${detectVersion}" ) . unwrap_or ( false ) {
960
+ let Some ( base_prefix_pb) = base_path. parent ( ) . map ( PathBuf :: from) else {
961
+ return Err ( S ! ( "$base must be a valid path with a parent directory" ) ) ;
962
+ } ;
963
+ let abs_base = match base_prefix_pb. canonicalize ( ) {
964
+ Ok ( p) => p,
965
+ Err ( e) => return Err ( S ! ( format!( "Failed to canonicalize base path: {} ({})" , base_prefix_pb. display( ) , e) ) ) ,
966
+ } ;
967
+ let base_prefix_pb = PathBuf :: from ( abs_base. sanitize ( ) ) ;
968
+ let ws_path_pb: PathBuf = PathBuf :: from ( workspace_path) ;
969
+ let base_prefix_components: Vec < _ > = base_prefix_pb. components ( ) . collect ( ) ;
970
+ let ws_path_components: Vec < _ > = ws_path_pb. components ( ) . collect ( ) ;
971
+ if ws_path_components. len ( ) > base_prefix_components. len ( )
972
+ && ws_path_components[ ..base_prefix_components. len ( ) ] == base_prefix_components[ ..] {
973
+ let version_component = ws_path_components[ base_prefix_components. len ( ) ] ;
974
+ let version_str = version_component. as_os_str ( ) . to_string_lossy ( ) . to_string ( ) ;
975
+ if !version_str. is_empty ( ) {
976
+ config. version = Some ( Sourced { value : version_str. clone ( ) , ..Default :: default ( ) } ) ;
977
+ let mut resolved_base = base_prefix_pb. clone ( ) ;
978
+ resolved_base. push ( & version_str) ;
979
+ config. base . as_mut ( ) . map ( |b| b. value = resolved_base. sanitize ( ) ) ;
980
+ } else {
981
+ return Err ( S ! ( "Could not extract version from workspace path using $base" ) ) ;
982
+ }
983
+ } else {
984
+ return Err ( S ! ( "$base does not match the current workspace folder" ) ) ;
985
+ }
951
986
}
952
987
}
953
- }
954
- Ok ( merged_config)
955
- }
956
-
957
- fn process_config (
958
- mut config_map : HashMap < String , ConfigEntryRaw > ,
959
- ws_folders : & HashMap < String , String > ,
960
- workspace_name : Option < & String > ,
961
- ) -> Result < HashMap < String , ConfigEntryRaw > , String > {
962
- apply_extends ( & mut config_map) ?;
963
- let mut new_configs = vec ! [ ] ;
964
- for config in config_map. values_mut ( ) {
988
+ // Also handle legacy $version with ${splitVersion}
965
989
let Some ( version_var) = config. version . clone ( ) else {
966
990
continue ;
967
991
} ;
968
992
let version_path = PathBuf :: from ( version_var. value ( ) ) ;
969
- if version_path. components ( ) . last ( ) . map ( |c| c. as_os_str ( ) . to_string_lossy ( ) == "${detectVersion }" ) . unwrap_or ( false ) {
993
+ if version_path. components ( ) . last ( ) . map ( |c| c. as_os_str ( ) . to_string_lossy ( ) == "${splitVersion }" ) . unwrap_or ( false ) {
970
994
config. abstract_ = true ;
971
995
let Some ( parent_dir) = version_path. parent ( ) else {
972
996
continue ;
973
997
} ;
974
998
let Some ( parent_dir) = fill_or_canonicalize (
975
999
& { Sourced { value : parent_dir. sanitize ( ) , sources : version_var. sources . clone ( ) , ..Default :: default ( ) } } ,
976
1000
ws_folders,
977
- workspace_name,
1001
+ Some ( workspace_name) ,
978
1002
& |p| PathBuf :: from ( p) . is_dir ( ) ,
979
1003
HashMap :: new ( ) ,
980
1004
) else {
@@ -999,9 +1023,29 @@ fn process_config(
999
1023
}
1000
1024
}
1001
1025
for new_entry in new_configs {
1002
- config_map. insert ( new_entry. name . clone ( ) , new_entry) ;
1026
+ merged_config. insert ( new_entry. name . clone ( ) , new_entry) ;
1027
+ }
1028
+ let mut merged_config = process_config ( merged_config, ws_folders, Some ( workspace_name) ) ?;
1029
+
1030
+ for entry in merged_config. values_mut ( ) {
1031
+ if entry. abstract_ { continue ; }
1032
+ if ( matches ! ( entry. add_workspace_addon_path. as_ref( ) . map( |a| a. value) , Some ( true ) ) || entry. addons_paths . is_none ( ) ) && is_addon_path ( workspace_path) {
1033
+ let addon_path = Sourced { value : workspace_path. clone ( ) , sources : HashSet :: from ( [ S ! ( format!( "$workspaceFolder:{workspace_name}" ) ) ] ) , ..Default :: default ( ) } ;
1034
+ match entry. addons_paths {
1035
+ Some ( ref mut paths) => paths. push ( addon_path) ,
1036
+ None => entry. addons_paths = Some ( vec ! [ addon_path] ) ,
1037
+ }
1038
+ }
1003
1039
}
1040
+ Ok ( merged_config)
1041
+ }
1004
1042
1043
+ fn process_config (
1044
+ mut config_map : HashMap < String , ConfigEntryRaw > ,
1045
+ ws_folders : & HashMap < String , String > ,
1046
+ workspace_name : Option < & String > ,
1047
+ ) -> Result < HashMap < String , ConfigEntryRaw > , String > {
1048
+ apply_extends ( & mut config_map) ?;
1005
1049
// Process vars
1006
1050
config_map. values_mut ( )
1007
1051
. for_each ( |entry| {
0 commit comments