@@ -31,20 +31,68 @@ impl Project {
3131 }
3232 }
3333
34+ /// Create instance from given configuration.
35+ /// Files referred by configuration are parsed into corresponding libraries.
3436 pub fn from_config ( config : & Config , messages : & mut dyn MessageHandler ) -> Project {
3537 let mut project = Project :: new ( ) ;
36- let mut files_to_parse: FnvHashMap < PathBuf , FnvHashSet < Symbol > > = FnvHashMap :: default ( ) ;
38+
39+ let files = project. load_files_from_config ( config, messages) ;
40+ project. parse_and_add_files ( files, messages) ;
41+
42+ project
43+ }
44+
45+ /// Replace active project configuration.
46+ /// The design state is reset, new files are added and parsed. Existing source files will be
47+ /// kept and parsed from in-memory source (required for incremental document updates).
48+ pub fn update_config ( & mut self , config : & Config , messages : & mut dyn MessageHandler ) {
49+ self . parser = VHDLParser :: default ( ) ;
50+ self . root = DesignRoot :: new ( self . parser . symbols . clone ( ) ) ;
51+
52+ // Reset library associations for known files,
53+ // all project files are added to the corresponding libraries later on.
54+ self . files
55+ . values_mut ( )
56+ . for_each ( |source_file| source_file. library_names . clear ( ) ) ;
57+
58+ // Files might already be part of self.files, these have to be parsed
59+ // from in-memory source. New files can be parsed as usual.
60+ let ( known_files, new_files) = self
61+ . load_files_from_config ( config, messages)
62+ . into_iter ( )
63+ . partition ( |( file_name, _library_names) | self . files . contains_key ( file_name) ) ;
64+
65+ for ( file_name, library_names) in known_files {
66+ if let Some ( source_file) = self . files . get_mut ( & file_name) {
67+ source_file. parser_diagnostics . clear ( ) ;
68+ source_file. library_names = library_names;
69+ source_file. design_file = self
70+ . parser
71+ . parse_design_source ( & source_file. source , & mut source_file. parser_diagnostics ) ;
72+ }
73+ }
74+
75+ self . parse_and_add_files ( new_files, messages) ;
76+ }
77+
78+ fn load_files_from_config (
79+ & mut self ,
80+ config : & Config ,
81+ messages : & mut dyn MessageHandler ,
82+ ) -> FnvHashMap < PathBuf , FnvHashSet < Symbol > > {
83+ let mut files: FnvHashMap < PathBuf , FnvHashSet < Symbol > > = FnvHashMap :: default ( ) ;
84+ self . empty_libraries . clear ( ) ;
3785
3886 for library in config. iter_libraries ( ) {
3987 let library_name =
4088 Latin1String :: from_utf8 ( library. name ( ) ) . expect ( "Library name not latin-1 encoded" ) ;
41- let library_name = project . parser . symbol ( & library_name) ;
89+ let library_name = self . parser . symbol ( & library_name) ;
4290
4391 let mut empty_library = true ;
4492 for file_name in library. file_names ( messages) {
4593 empty_library = false ;
4694
47- match files_to_parse . entry ( file_name. clone ( ) ) {
95+ match files . entry ( file_name. clone ( ) ) {
4896 Entry :: Occupied ( mut entry) => {
4997 entry. get_mut ( ) . insert ( library_name. clone ( ) ) ;
5098 }
@@ -57,16 +105,23 @@ impl Project {
57105 }
58106
59107 if empty_library {
60- project . empty_libraries . insert ( library_name) ;
108+ self . empty_libraries . insert ( library_name) ;
61109 }
62110 }
111+ files
112+ }
63113
114+ fn parse_and_add_files (
115+ & mut self ,
116+ files_to_parse : FnvHashMap < PathBuf , FnvHashSet < Symbol > > ,
117+ messages : & mut dyn MessageHandler ,
118+ ) {
64119 use rayon:: prelude:: * ;
65120
66121 let parsed: Vec < _ > = files_to_parse
67122 . into_par_iter ( )
68123 . map_init (
69- || & project . parser ,
124+ || & self . parser ,
70125 |parser, ( file_name, library_names) | {
71126 let mut diagnostics = Vec :: new ( ) ;
72127 let result = parser. parse_design_file ( & file_name, & mut diagnostics) ;
@@ -84,7 +139,7 @@ impl Project {
84139 }
85140 } ;
86141
87- project . files . insert (
142+ self . files . insert (
88143 source. file_name ( ) . to_owned ( ) ,
89144 SourceFile {
90145 source,
@@ -94,8 +149,6 @@ impl Project {
94149 } ,
95150 ) ;
96151 }
97-
98- project
99152 }
100153
101154 pub fn get_source ( & self , file_name : & Path ) -> Option < Source > {
@@ -400,4 +453,71 @@ end package;
400453 ) ;
401454 check_no_diagnostics ( & project. analyse ( ) ) ;
402455 }
456+
457+ /// Test that the configuration can be updated
458+ #[ test]
459+ fn test_config_update ( ) {
460+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
461+ let root = dunce:: canonicalize ( tempdir. path ( ) ) . unwrap ( ) ;
462+
463+ let path1 = root. join ( "file1.vhd" ) ;
464+ let path2 = root. join ( "file2.vhd" ) ;
465+ std:: fs:: write (
466+ & path1,
467+ "
468+ library unkown;
469+ use unkown.pkg.all;
470+
471+ package pkg is
472+ end package;
473+ " ,
474+ )
475+ . unwrap ( ) ;
476+ let source1 = Source :: from_latin1_file ( & path1) . unwrap ( ) ;
477+
478+ std:: fs:: write (
479+ & path2,
480+ "
481+ library unkown;
482+ use unkown.pkg.all;
483+
484+ package pkg is
485+ end package;
486+ " ,
487+ )
488+ . unwrap ( ) ;
489+ let source2 = Source :: from_latin1_file ( & path2) . unwrap ( ) ;
490+
491+ let config_str1 = "
492+ [libraries]
493+ lib.files = ['file1.vhd']
494+ " ;
495+ let config1 = Config :: from_str ( config_str1, & root) . unwrap ( ) ;
496+
497+ let config_str2 = "
498+ [libraries]
499+ lib.files = ['file2.vhd']
500+ " ;
501+ let config2 = Config :: from_str ( config_str2, & root) . unwrap ( ) ;
502+
503+ let mut messages = Vec :: new ( ) ;
504+ let mut project = Project :: from_config ( & config1, & mut messages) ;
505+ assert_eq ! ( messages, vec![ ] ) ;
506+
507+ // Invalid library should only be reported in source1
508+ let diagnostics = project. analyse ( ) ;
509+ assert_eq ! ( diagnostics. len( ) , 2 ) ;
510+ assert_eq ! ( diagnostics[ 0 ] . pos. source, source1) ; // No such library
511+ assert_eq ! ( diagnostics[ 1 ] . pos. source, source1) ; // No declaration
512+
513+ // Change configuration file
514+ project. update_config ( & config2, & mut messages) ;
515+ assert_eq ! ( messages, vec![ ] ) ;
516+
517+ // Invalid library should only be reported in source2
518+ let diagnostics = project. analyse ( ) ;
519+ assert_eq ! ( diagnostics. len( ) , 2 ) ;
520+ assert_eq ! ( diagnostics[ 0 ] . pos. source, source2) ; // No such library
521+ assert_eq ! ( diagnostics[ 1 ] . pos. source, source2) ; // No declaration
522+ }
403523}
0 commit comments