@@ -86,8 +86,12 @@ impl LanguageConfiguration {
8686 } )
8787 }
8888
89- pub fn matches_file ( & self , path : & Path , content : Option < & str > ) -> bool {
90- matches_file ( & self . file_types , & self . content_regex , path, content) . is_some ( )
89+ pub fn matches_file (
90+ & self ,
91+ path : & Path ,
92+ content : & mut dyn ContentProvider ,
93+ ) -> std:: io:: Result < bool > {
94+ matches_file ( & self . file_types , & self . content_regex , path, content) . map ( |l| l. is_some ( ) )
9195 }
9296}
9397
@@ -205,7 +209,7 @@ impl Loader {
205209 pub fn load_tree_sitter_language_for_file (
206210 & mut self ,
207211 path : & Path ,
208- content : Option < & str > ,
212+ content : & mut dyn ContentProvider ,
209213 ) -> Result < Option < tree_sitter:: Language > , LoadError > {
210214 match & mut self . 0 {
211215 LoaderImpl :: Paths ( loader) => loader. load_tree_sitter_language_for_file ( path, content) ,
@@ -219,7 +223,7 @@ impl Loader {
219223 pub fn load_for_file (
220224 & mut self ,
221225 path : & Path ,
222- content : Option < & str > ,
226+ content : & mut dyn ContentProvider ,
223227 cancellation_flag : & dyn CancellationFlag ,
224228 ) -> Result < Option < & LanguageConfiguration > , LoadError > {
225229 match & mut self . 0 {
@@ -328,34 +332,28 @@ impl LanguageConfigurationsLoader {
328332 pub fn load_tree_sitter_language_for_file (
329333 & mut self ,
330334 path : & Path ,
331- content : Option < & str > ,
335+ content : & mut dyn ContentProvider ,
332336 ) -> Result < Option < tree_sitter:: Language > , LoadError > {
333- let configuration = match self
334- . configurations
335- . iter ( )
336- . find ( |l| l. matches_file ( path, content) )
337- {
338- Some ( language) => language,
339- None => return Ok ( None ) ,
340- } ;
341- Ok ( Some ( configuration. language ) )
337+ for configuration in self . configurations . iter ( ) {
338+ if configuration. matches_file ( path, content) ? {
339+ return Ok ( Some ( configuration. language ) ) ;
340+ }
341+ }
342+ Ok ( None )
342343 }
343344
344345 /// Load a stack graph language for the given file. Loading is based on the loader configuration and the given file path.
345346 pub fn load_for_file (
346347 & mut self ,
347348 path : & Path ,
348- content : Option < & str > ,
349+ content : & mut dyn ContentProvider ,
349350 ) -> Result < Option < & LanguageConfiguration > , LoadError > {
350- let language = match self
351- . configurations
352- . iter ( )
353- . find ( |l| l. matches_file ( path, content) )
354- {
355- Some ( language) => language,
356- None => return Ok ( None ) ,
357- } ;
358- Ok ( Some ( language) )
351+ for language in self . configurations . iter ( ) {
352+ if language. matches_file ( path, content) ? {
353+ return Ok ( Some ( language) ) ;
354+ }
355+ }
356+ Ok ( None )
359357 }
360358}
361359
@@ -400,7 +398,7 @@ impl PathLoader {
400398 pub fn load_tree_sitter_language_for_file (
401399 & mut self ,
402400 path : & Path ,
403- content : Option < & str > ,
401+ content : & mut dyn ContentProvider ,
404402 ) -> Result < Option < tree_sitter:: Language > , LoadError > {
405403 if let Some ( selected_language) = self . select_language_for_file ( path, content) ? {
406404 return Ok ( Some ( selected_language. language ) ) ;
@@ -411,7 +409,7 @@ impl PathLoader {
411409 pub fn load_for_file (
412410 & mut self ,
413411 path : & Path ,
414- content : Option < & str > ,
412+ content : & mut dyn ContentProvider ,
415413 cancellation_flag : & dyn CancellationFlag ,
416414 ) -> Result < Option < & LanguageConfiguration > , LoadError > {
417415 let selected_language = self . select_language_for_file ( path, content) ?;
@@ -457,7 +455,7 @@ impl PathLoader {
457455 fn select_language_for_file (
458456 & mut self ,
459457 file_path : & Path ,
460- file_content : Option < & str > ,
458+ file_content : & mut dyn ContentProvider ,
461459 ) -> Result < Option < & SupplementedLanguage > , LoadError > {
462460 // The borrow checker is not smart enough to realize that the early returns
463461 // ensure any references from the self.select_* call (which require a mutable
@@ -494,7 +492,7 @@ impl PathLoader {
494492 & mut self ,
495493 language_path : & Path ,
496494 file_path : & Path ,
497- file_content : Option < & str > ,
495+ file_content : & mut dyn ContentProvider ,
498496 ) -> Result < Option < & SupplementedLanguage > , LoadError > {
499497 let scope = self . scope . as_deref ( ) ;
500498 let languages = self . loader . languages_at_path ( language_path, scope) ?;
@@ -506,7 +504,7 @@ impl PathLoader {
506504 ) ) ) ;
507505 }
508506 if let Some ( language) =
509- SupplementedLanguage :: best_for_file ( languages, file_path, file_content)
507+ SupplementedLanguage :: best_for_file ( languages, file_path, file_content) ?
510508 {
511509 return Ok ( Some ( language) ) ;
512510 } ;
@@ -641,27 +639,31 @@ impl SupplementedLanguage {
641639 }
642640
643641 // Extracted from tree_sitter_loader::Loader::language_configuration_for_file_name
644- pub fn matches_file ( & self , path : & Path , content : Option < & str > ) -> Option < isize > {
642+ pub fn matches_file (
643+ & self ,
644+ path : & Path ,
645+ content : & mut dyn ContentProvider ,
646+ ) -> std:: io:: Result < Option < isize > > {
645647 matches_file ( & self . file_types , & self . content_regex , path, content)
646648 }
647649
648650 // Extracted from tree_sitter_loader::Loader::language_configuration_for_file_name
649651 pub fn best_for_file < ' a > (
650652 languages : Vec < & ' a SupplementedLanguage > ,
651653 path : & Path ,
652- content : Option < & str > ,
653- ) -> Option < & ' a SupplementedLanguage > {
654+ content : & mut dyn ContentProvider ,
655+ ) -> std :: io :: Result < Option < & ' a SupplementedLanguage > > {
654656 let mut best_score = -1isize ;
655657 let mut best = None ;
656658 for language in languages {
657- if let Some ( score) = language. matches_file ( path, content) {
659+ if let Some ( score) = language. matches_file ( path, content) ? {
658660 if score > best_score {
659661 best_score = score;
660662 best = Some ( language) ;
661663 }
662664 }
663665 }
664- best
666+ Ok ( best)
665667 }
666668}
667669
@@ -682,28 +684,64 @@ pub fn matches_file(
682684 file_types : & Vec < String > ,
683685 content_regex : & Option < Regex > ,
684686 path : & Path ,
685- content : Option < & str > ,
686- ) -> Option < isize > {
687+ content : & mut dyn ContentProvider ,
688+ ) -> std :: io :: Result < Option < isize > > {
687689 // Check path extension
688690 if !path
689691 . extension ( )
690692 . and_then ( OsStr :: to_str)
691693 . map_or ( false , |ext| file_types. iter ( ) . any ( |ft| ft == ext) )
692694 {
693- return None ;
695+ return Ok ( None ) ;
694696 }
695697
696698 // Apply content regex
699+ let content = content. get ( path) ?;
697700 if let ( Some ( file_content) , Some ( content_regex) ) = ( content, & content_regex) {
698701 // If the language configuration has a content regex, assign
699702 // a score based on the length of the first match.
700703 if let Some ( mat) = content_regex. find ( & file_content) {
701704 let score = ( mat. end ( ) - mat. start ( ) ) as isize ;
702- return Some ( score) ;
705+ return Ok ( Some ( score) ) ;
703706 } else {
704- return None ;
707+ return Ok ( None ) ;
705708 }
706709 }
707710
708- Some ( 0isize )
711+ Ok ( Some ( 0isize ) )
712+ }
713+
714+ pub trait ContentProvider {
715+ fn get ( & mut self , path : & Path ) -> std:: io:: Result < Option < & str > > ;
716+ }
717+
718+ /// FileReader reads files from the filesystem and caches the most recently read file.
719+ pub struct FileReader {
720+ cache : Option < ( PathBuf , String ) > ,
721+ }
722+
723+ impl FileReader {
724+ pub fn new ( ) -> Self {
725+ Self { cache : None }
726+ }
727+
728+ pub fn get ( & mut self , path : & Path ) -> std:: io:: Result < & str > {
729+ if self . cache . as_ref ( ) . map_or ( true , |( p, _) | p != path) {
730+ let content = std:: fs:: read_to_string ( path) ?;
731+ self . cache = Some ( ( path. to_path_buf ( ) , content) ) ;
732+ }
733+ Ok ( & self . cache . as_ref ( ) . unwrap ( ) . 1 )
734+ }
735+ }
736+
737+ impl ContentProvider for FileReader {
738+ fn get ( & mut self , path : & Path ) -> std:: io:: Result < Option < & str > > {
739+ self . get ( path) . map ( |c| Some ( c) )
740+ }
741+ }
742+
743+ impl ContentProvider for Option < & str > {
744+ fn get ( & mut self , _path : & Path ) -> std:: io:: Result < Option < & str > > {
745+ Ok ( self . clone ( ) )
746+ }
709747}
0 commit comments