22//! metadata` or `rust-project.json`) into representation stored in the salsa
33//! database -- `CrateGraph`.
44
5- use std:: { collections:: VecDeque , fmt, fs, iter, sync} ;
5+ use std:: { collections:: VecDeque , fmt, fs, io :: BufRead , iter, sync} ;
66
77use anyhow:: { format_err, Context } ;
88use base_db:: {
@@ -115,9 +115,55 @@ pub enum ProjectWorkspace {
115115 target_layout : TargetLayoutLoadResult ,
116116 /// A set of cfg overrides for the files.
117117 cfg_overrides : CfgOverrides ,
118+ /// Is this file a cargo script file?
119+ cargo_script : Option < CargoWorkspace > ,
118120 } ,
119121}
120122
123+ /// Tracks the cargo toml parts in cargo scripts, to detect if they
124+ /// changed and reload workspace in that case.
125+ pub struct CargoScriptTomls ( pub FxHashMap < AbsPathBuf , String > ) ;
126+
127+ impl CargoScriptTomls {
128+ fn extract_toml_part ( p : & AbsPath ) -> Option < String > {
129+ let mut r = String :: new ( ) ;
130+ let f = std:: fs:: File :: open ( p) . ok ( ) ?;
131+ let f = std:: io:: BufReader :: new ( f) ;
132+ let mut started = false ;
133+ for line in f. lines ( ) {
134+ let line = line. ok ( ) ?;
135+ if started {
136+ if line. trim ( ) == "//! ```" {
137+ return Some ( r) ;
138+ }
139+ r += & line;
140+ } else {
141+ if line. trim ( ) == "//! ```cargo" {
142+ started = true ;
143+ }
144+ }
145+ }
146+ None
147+ }
148+
149+ pub fn track_file ( & mut self , p : AbsPathBuf ) {
150+ let toml = CargoScriptTomls :: extract_toml_part ( & p) . unwrap_or_default ( ) ;
151+ self . 0 . insert ( p, toml) ;
152+ }
153+
154+ pub fn need_reload ( & mut self , p : & AbsPath ) -> bool {
155+ let Some ( prev) = self . 0 . get_mut ( p) else {
156+ return false ; // File is not tracked
157+ } ;
158+ let next = CargoScriptTomls :: extract_toml_part ( p) . unwrap_or_default ( ) ;
159+ if * prev == next {
160+ return false ;
161+ }
162+ * prev = next;
163+ true
164+ }
165+ }
166+
121167impl fmt:: Debug for ProjectWorkspace {
122168 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
123169 // Make sure this isn't too verbose.
@@ -174,10 +220,12 @@ impl fmt::Debug for ProjectWorkspace {
174220 toolchain,
175221 target_layout,
176222 cfg_overrides,
223+ cargo_script,
177224 } => f
178225 . debug_struct ( "DetachedFiles" )
179226 . field ( "n_files" , & files. len ( ) )
180227 . field ( "sysroot" , & sysroot. is_ok ( ) )
228+ . field ( "cargo_script" , & cargo_script. is_some ( ) )
181229 . field ( "n_rustc_cfg" , & rustc_cfg. len ( ) )
182230 . field ( "toolchain" , & toolchain)
183231 . field ( "data_layout" , & target_layout)
@@ -431,6 +479,7 @@ impl ProjectWorkspace {
431479 pub fn load_detached_files (
432480 detached_files : Vec < AbsPathBuf > ,
433481 config : & CargoConfig ,
482+ cargo_script_tomls : & mut CargoScriptTomls ,
434483 ) -> anyhow:: Result < ProjectWorkspace > {
435484 let dir = detached_files
436485 . first ( )
@@ -469,13 +518,31 @@ impl ProjectWorkspace {
469518 None ,
470519 & config. extra_env ,
471520 ) ;
521+ let cargo_toml = ManifestPath :: try_from ( detached_files[ 0 ] . clone ( ) ) . unwrap ( ) ;
522+ let meta = CargoWorkspace :: fetch_metadata (
523+ & cargo_toml,
524+ cargo_toml. parent ( ) ,
525+ config,
526+ sysroot_ref,
527+ & |_| ( ) ,
528+ )
529+ . with_context ( || {
530+ format ! ( "Failed to read Cargo metadata from Cargo.toml file {cargo_toml}" )
531+ } ) ?;
532+ let cargo = CargoWorkspace :: new ( meta) ;
533+
534+ for file in & detached_files {
535+ cargo_script_tomls. track_file ( file. clone ( ) ) ;
536+ }
537+
472538 Ok ( ProjectWorkspace :: DetachedFiles {
473539 files : detached_files,
474540 sysroot,
475541 rustc_cfg,
476542 toolchain,
477543 target_layout : data_layout. map ( Arc :: from) . map_err ( |it| Arc :: from ( it. to_string ( ) ) ) ,
478544 cfg_overrides : config. cfg_overrides . clone ( ) ,
545+ cargo_script : Some ( cargo) ,
479546 } )
480547 }
481548
@@ -788,14 +855,27 @@ impl ProjectWorkspace {
788855 toolchain : _,
789856 target_layout : _,
790857 cfg_overrides,
858+ cargo_script,
791859 } => (
792- detached_files_to_crate_graph (
793- rustc_cfg. clone ( ) ,
794- load,
795- files,
796- sysroot. as_ref ( ) . ok ( ) ,
797- cfg_overrides,
798- ) ,
860+ if let Some ( cargo) = cargo_script {
861+ cargo_to_crate_graph (
862+ load,
863+ None ,
864+ cargo,
865+ sysroot. as_ref ( ) . ok ( ) ,
866+ rustc_cfg. clone ( ) ,
867+ cfg_overrides,
868+ & WorkspaceBuildScripts :: default ( ) ,
869+ )
870+ } else {
871+ detached_files_to_crate_graph (
872+ rustc_cfg. clone ( ) ,
873+ load,
874+ files,
875+ sysroot. as_ref ( ) . ok ( ) ,
876+ cfg_overrides,
877+ )
878+ } ,
799879 sysroot,
800880 ) ,
801881 } ;
@@ -873,6 +953,7 @@ impl ProjectWorkspace {
873953 files,
874954 sysroot,
875955 rustc_cfg,
956+ cargo_script,
876957 toolchain,
877958 target_layout,
878959 cfg_overrides,
@@ -881,6 +962,7 @@ impl ProjectWorkspace {
881962 files : o_files,
882963 sysroot : o_sysroot,
883964 rustc_cfg : o_rustc_cfg,
965+ cargo_script : o_cargo_script,
884966 toolchain : o_toolchain,
885967 target_layout : o_target_layout,
886968 cfg_overrides : o_cfg_overrides,
@@ -892,6 +974,7 @@ impl ProjectWorkspace {
892974 && toolchain == o_toolchain
893975 && target_layout == o_target_layout
894976 && cfg_overrides == o_cfg_overrides
977+ && cargo_script == o_cargo_script
895978 }
896979 _ => false ,
897980 }
0 commit comments