@@ -22,8 +22,10 @@ use utils::{convert_match_to_diagnostic, diagnostic_to_code_action, RewriteData}
2222
2323pub use tower_lsp_server:: { LspService , Server } ;
2424
25- pub trait LSPLang : LanguageExt + Eq + Send + Sync + ' static { }
26- impl < T > LSPLang for T where T : LanguageExt + Eq + Send + Sync + ' static { }
25+ use serde:: Deserialize ;
26+
27+ pub trait LSPLang : LanguageExt + Eq + Send + Sync + for < ' a > Deserialize < ' a > + ' static { }
28+ impl < T > LSPLang for T where T : LanguageExt + Eq + Send + Sync + for < ' a > Deserialize < ' a > + ' static { }
2729
2830type Notes = BTreeMap < ( u32 , u32 , u32 , u32 ) , Arc < String > > ;
2931
@@ -650,14 +652,45 @@ impl<L: LSPLang> Backend<L> {
650652
651653 /// Reload rules from configuration and republish diagnostics for all open files
652654 async fn reload_rules ( & self ) -> anyhow:: Result < ( ) > {
653- // For now, this is a minimal implementation that just clears errors
654- // and republishes diagnostics. In a complete implementation, we would
655- // actually reload rules from the file system.
655+ self
656+ . client
657+ . log_message ( MessageType :: INFO , "Starting rule reload..." )
658+ . await ;
659+
660+ // Try to reload rules from the file system
661+ let result = self . load_rules_from_filesystem ( ) . await ;
656662
657- // Clear any previous errors
658- {
659- let mut errors = self . errors . write ( ) . map_err ( |e| anyhow:: anyhow!( "Lock error: {e}" ) ) ?;
660- * errors = None ;
663+ match result {
664+ Ok ( new_rules) => {
665+ // Update the rules
666+ {
667+ let mut rules = self . rules . write ( ) . map_err ( |e| anyhow:: anyhow!( "Lock error: {e}" ) ) ?;
668+ * rules = new_rules;
669+ }
670+
671+ // Clear any previous errors
672+ {
673+ let mut errors = self . errors . write ( ) . map_err ( |e| anyhow:: anyhow!( "Lock error: {e}" ) ) ?;
674+ * errors = None ;
675+ }
676+
677+ self
678+ . client
679+ . log_message ( MessageType :: INFO , "Rules reloaded successfully from filesystem" )
680+ . await ;
681+ }
682+ Err ( e) => {
683+ // Store the error
684+ {
685+ let mut errors = self . errors . write ( ) . map_err ( |e| anyhow:: anyhow!( "Lock error: {e}" ) ) ?;
686+ * errors = Some ( e. to_string ( ) ) ;
687+ }
688+
689+ self
690+ . client
691+ . log_message ( MessageType :: ERROR , format ! ( "Failed to reload rules: {e}" ) )
692+ . await ;
693+ }
661694 }
662695
663696 // Clear the interner since rule IDs might have changed
@@ -669,6 +702,64 @@ impl<L: LSPLang> Backend<L> {
669702 Ok ( ( ) )
670703 }
671704
705+ /// Load rules from the filesystem - simplified version of CLI config loading
706+ async fn load_rules_from_filesystem ( & self ) -> anyhow:: Result < RuleCollection < L > > {
707+ use ast_grep_config:: { from_yaml_string, GlobalRules } ;
708+ use ast_grep_language:: config_file_type;
709+ use ignore:: WalkBuilder ;
710+ use std:: fs:: read_to_string;
711+
712+ // Look for sgconfig.yml in the base directory
713+ let config_path = self . base . join ( "sgconfig.yml" ) ;
714+
715+ let rule_dirs = if config_path. exists ( ) {
716+ let config_content = read_to_string ( & config_path) ?;
717+ let config: serde_yaml:: Value = serde_yaml:: from_str ( & config_content) ?;
718+
719+ if let Some ( rule_dirs) = config. get ( "ruleDirs" ) . and_then ( |v| v. as_sequence ( ) ) {
720+ rule_dirs
721+ . iter ( )
722+ . filter_map ( |v| v. as_str ( ) )
723+ . map ( |s| self . base . join ( s) )
724+ . collect :: < Vec < _ > > ( )
725+ } else {
726+ vec ! [ self . base. join( "rules" ) ] // Default rules directory
727+ }
728+ } else {
729+ vec ! [ self . base. join( "rules" ) ] // Default rules directory
730+ } ;
731+
732+ // Read all rule files
733+ let mut configs = Vec :: new ( ) ;
734+ let global_rules = GlobalRules :: default ( ) ; // Simplified - no util rules for now
735+
736+ for rule_dir in rule_dirs {
737+ if !rule_dir. exists ( ) {
738+ continue ;
739+ }
740+
741+ let walker = WalkBuilder :: new ( & rule_dir)
742+ . types ( config_file_type ( ) )
743+ . build ( ) ;
744+
745+ for entry in walker {
746+ let entry = entry?;
747+ if !entry. file_type ( ) . unwrap ( ) . is_file ( ) {
748+ continue ;
749+ }
750+
751+ let path = entry. path ( ) ;
752+ let yaml = read_to_string ( path) ?;
753+ let parsed = from_yaml_string ( & yaml, & global_rules) ?;
754+ configs. extend ( parsed) ;
755+ }
756+ }
757+
758+ // Create the rule collection
759+ let collection = RuleCollection :: try_new ( configs) ?;
760+ Ok ( collection)
761+ }
762+
672763 /// Republish diagnostics for all currently open files
673764 async fn republish_all_diagnostics ( & self ) {
674765 // Get all currently open file URIs
0 commit comments