@@ -2,8 +2,10 @@ use std::fmt::Display;
22
33use tower_lsp:: lsp_types:: * ;
44
5+ use crate :: cst:: Module ;
56use crate :: interner;
67use crate :: build:: cache:: ModuleCache ;
8+ use crate :: typechecker:: registry:: ModuleRegistry ;
79
810use super :: super :: { Backend , FileState } ;
911
@@ -14,7 +16,34 @@ impl Backend {
1416 . await ;
1517 }
1618
19+ /// Ensure all modules imported by `module` have their exports loaded into the registry.
20+ /// Loads missing exports lazily from the ModuleCache (which reads from disk on demand).
21+ async fn ensure_imports_loaded ( & self , module : & Module , registry : & mut ModuleRegistry ) {
22+ for import_decl in & module. imports {
23+ let import_parts = & import_decl. module . parts ;
24+
25+ // Skip if already in registry
26+ if registry. lookup ( import_parts) . is_some ( ) {
27+ continue ;
28+ }
29+
30+ let import_name = interner:: resolve_module_name ( import_parts) ;
31+
32+ // Try to load from module cache (lazy disk load)
33+ let exports = {
34+ let mut cache = self . module_cache . write ( ) . await ;
35+ cache. get_exports ( & import_name) . cloned ( )
36+ } ;
37+
38+ if let Some ( exports) = exports {
39+ registry. register ( import_parts, exports) ;
40+ log:: debug!( "Lazy-loaded exports for {import_name}" ) ;
41+ }
42+ }
43+ }
44+
1745 pub ( crate ) async fn on_change ( & self , uri : Url , source : String ) {
46+ let on_change_start = std:: time:: Instant :: now ( ) ;
1847 {
1948 let mut files = self . files . write ( ) . await ;
2049 files. insert (
@@ -31,6 +60,7 @@ impl Backend {
3160 return ;
3261 }
3362
63+ let t = std:: time:: Instant :: now ( ) ;
3464 let module = match crate :: parser:: parse ( & source) {
3565 Ok ( module) => {
3666 let module_name = format ! ( "{}" , module. name. value) ;
@@ -58,13 +88,21 @@ impl Backend {
5888 return ;
5989 }
6090 } ;
91+ self . info ( format ! ( "[on_change] parse: {:.2?}" , t. elapsed( ) ) ) . await ;
6192
6293 let module_name = interner:: resolve_module_name ( & module. name . value . parts ) ;
6394 let module_parts: Vec < interner:: Symbol > = module. name . value . parts . clone ( ) ;
6495
65- // Type-check against the registry (use stacker to extend stack for deep recursion)
96+ // Ensure imported modules' exports are in the registry (lazy load from cache)
97+ let t = std:: time:: Instant :: now ( ) ;
6698 let mut registry = self . registry . write ( ) . await ;
99+ self . ensure_imports_loaded ( & module, & mut registry) . await ;
100+ self . info ( format ! ( "[on_change] ensure_imports_loaded: {:.2?}" , t. elapsed( ) ) ) . await ;
101+
102+ // Type-check against the registry
103+ let t = std:: time:: Instant :: now ( ) ;
67104 let check_result = crate :: typechecker:: check_module_with_registry ( & module, & registry) ;
105+ self . info ( format ! ( "[on_change] typecheck {module_name}: {:.2?}" , t. elapsed( ) ) ) . await ;
68106
69107 // Update registry with new exports
70108 registry. register ( & module_parts, check_result. exports . clone ( ) ) ;
@@ -76,10 +114,6 @@ impl Backend {
76114 . collect ( ) ;
77115 let mut cache = self . module_cache . write ( ) . await ;
78116 cache. update ( module_name. clone ( ) , source_hash, check_result. exports , import_names) ;
79- cache. build_reverse_deps ( ) ;
80-
81- // Find transitive dependents that need re-checking
82- let dependents = cache. transitive_dependents ( & module_name) ;
83117 drop ( cache) ;
84118
85119 // Publish diagnostics for the changed module
@@ -88,63 +122,7 @@ impl Backend {
88122 . publish_diagnostics ( uri, diagnostics, None )
89123 . await ;
90124
91- // Update source map
92- {
93- let mfmap = self . module_file_map . read ( ) . await ;
94- let mut smap = self . source_map . write ( ) . await ;
95- // Update the changed module's source in source_map
96- if let Some ( file_uri) = mfmap. get ( & module_name) {
97- smap. insert ( file_uri. clone ( ) , source) ;
98- }
99- }
100-
101- // Cascade: re-typecheck dependents
102- if !dependents. is_empty ( ) {
103- log:: debug!( "Cascade rebuild: {} dependents of {}" , dependents. len( ) , module_name) ;
104-
105- let mfmap = self . module_file_map . read ( ) . await ;
106- let smap = self . source_map . read ( ) . await ;
107-
108- for dep_name in & dependents {
109- let dep_uri_str = match mfmap. get ( dep_name) {
110- Some ( u) => u. clone ( ) ,
111- None => continue ,
112- } ;
113- let dep_source = match smap. get ( & dep_uri_str) {
114- Some ( s) => s. clone ( ) ,
115- None => continue ,
116- } ;
117- let dep_uri = match Url :: parse ( & dep_uri_str) {
118- Ok ( u) => u,
119- Err ( _) => continue ,
120- } ;
121-
122- let dep_module = match crate :: parser:: parse ( & dep_source) {
123- Ok ( m) => m,
124- Err ( _) => continue ,
125- } ;
126-
127- let dep_result = crate :: typechecker:: check_module_with_registry ( & dep_module, & registry) ;
128-
129- // Update registry with dependent's exports
130- let dep_parts: Vec < interner:: Symbol > = dep_module. name . value . parts . clone ( ) ;
131- registry. register ( & dep_parts, dep_result. exports . clone ( ) ) ;
132-
133- // Update cache for dependent
134- let dep_hash = ModuleCache :: content_hash ( & dep_source) ;
135- let dep_imports: Vec < String > = dep_module. imports . iter ( )
136- . map ( |imp| interner:: resolve_module_name ( & imp. module . parts ) )
137- . collect ( ) ;
138- let mut cache = self . module_cache . write ( ) . await ;
139- cache. update ( dep_name. clone ( ) , dep_hash, dep_result. exports , dep_imports) ;
140- drop ( cache) ;
141-
142- let dep_diagnostics = type_errors_to_diagnostics ( & dep_result. errors , & dep_source) ;
143- self . client
144- . publish_diagnostics ( dep_uri, dep_diagnostics, None )
145- . await ;
146- }
147- }
125+ self . info ( format ! ( "[on_change] total: {:.2?}" , on_change_start. elapsed( ) ) ) . await ;
148126 }
149127}
150128
0 commit comments