|
1 | 1 | use std::collections::HashMap; |
2 | 2 | use std::sync::atomic::Ordering; |
3 | 3 |
|
| 4 | +use rayon::prelude::*; |
4 | 5 | use tower_lsp::lsp_types::*; |
5 | 6 |
|
6 | 7 | use crate::build::BuildOptions; |
@@ -51,7 +52,12 @@ impl Backend { |
51 | 52 | let ready = self.ready.clone(); |
52 | 53 | let progress_token = token.clone(); |
53 | 54 |
|
54 | | - tokio::task::spawn_blocking(move || { |
| 55 | + let rt_handle = tokio::runtime::Handle::current(); |
| 56 | + std::thread::Builder::new() |
| 57 | + .name("pfc-load-sources".to_string()) |
| 58 | + .stack_size(16 * 1024 * 1024) // 16 MB — typechecker needs deep recursion |
| 59 | + .spawn(move || { |
| 60 | + let _guard = rt_handle.enter(); |
55 | 61 | // Run the shell command to get source globs |
56 | 62 | let output = match std::process::Command::new("sh") |
57 | 63 | .arg("-c") |
@@ -101,34 +107,40 @@ impl Backend { |
101 | 107 | .await; |
102 | 108 | }); |
103 | 109 |
|
104 | | - // Resolve globs to file paths |
105 | | - let mut sources: Vec<(String, String)> = Vec::new(); |
| 110 | + // Resolve globs to file paths (collect paths first, then read in parallel) |
| 111 | + let mut file_paths: Vec<std::path::PathBuf> = Vec::new(); |
106 | 112 | for pattern in &globs { |
107 | 113 | match glob::glob(pattern) { |
108 | 114 | Ok(entries) => { |
109 | 115 | for entry in entries.flatten() { |
110 | 116 | if entry.extension().map_or(false, |ext| ext == "purs") { |
111 | | - match std::fs::read_to_string(&entry) { |
112 | | - Ok(source) => { |
113 | | - let abs_path = entry |
114 | | - .canonicalize() |
115 | | - .unwrap_or_else(|_| entry.clone()); |
116 | | - sources.push(( |
117 | | - abs_path.to_string_lossy().into_owned(), |
118 | | - source, |
119 | | - )); |
120 | | - } |
121 | | - Err(e) => { |
122 | | - log::warn!("Failed to read {}: {e}", entry.display()) |
123 | | - } |
124 | | - } |
| 117 | + file_paths.push(entry); |
125 | 118 | } |
126 | 119 | } |
127 | 120 | } |
128 | 121 | Err(e) => log::warn!("Invalid glob pattern {pattern}: {e}"), |
129 | 122 | } |
130 | 123 | } |
131 | 124 |
|
| 125 | + // Read all files in parallel |
| 126 | + let sources: Vec<(String, String)> = file_paths |
| 127 | + .par_iter() |
| 128 | + .filter_map(|entry| { |
| 129 | + match std::fs::read_to_string(entry) { |
| 130 | + Ok(source) => { |
| 131 | + let abs_path = entry |
| 132 | + .canonicalize() |
| 133 | + .unwrap_or_else(|_| entry.clone()); |
| 134 | + Some((abs_path.to_string_lossy().into_owned(), source)) |
| 135 | + } |
| 136 | + Err(e) => { |
| 137 | + log::warn!("Failed to read {}: {e}", entry.display()); |
| 138 | + None |
| 139 | + } |
| 140 | + } |
| 141 | + }) |
| 142 | + .collect(); |
| 143 | + |
132 | 144 | // Report progress: building |
133 | 145 | rt.block_on(async { |
134 | 146 | client |
@@ -179,29 +191,37 @@ impl Backend { |
179 | 191 | .filter(|m| !m.type_errors.is_empty()) |
180 | 192 | .count(); |
181 | 193 |
|
182 | | - // Build definition index and resolution exports from parsed sources |
183 | | - let mut index = DefinitionIndex::new(); |
184 | | - let mut smap = HashMap::new(); |
185 | | - let mut mfmap = HashMap::new(); |
186 | | - let mut parsed_modules = Vec::new(); |
187 | | - for (path, source) in &sources { |
188 | | - if let Ok(module) = crate::parser::parse(source) { |
189 | | - index.add_module(&module, path); |
190 | | - let mod_name = format!("{}", module.name.value); |
| 194 | + // Parse all sources in parallel for definition index |
| 195 | + let parse_results: Vec<_> = sources |
| 196 | + .par_iter() |
| 197 | + .map(|(path, source)| { |
191 | 198 | let file_uri = Url::from_file_path(path) |
192 | 199 | .map(|u| u.to_string()) |
193 | 200 | .unwrap_or_default(); |
| 201 | + match crate::parser::parse(source) { |
| 202 | + Ok(module) => { |
| 203 | + let mod_name = format!("{}", module.name.value); |
| 204 | + (path.clone(), file_uri, source.clone(), Some((module, mod_name))) |
| 205 | + } |
| 206 | + Err(_) => { |
| 207 | + (path.clone(), file_uri, source.clone(), None) |
| 208 | + } |
| 209 | + } |
| 210 | + }) |
| 211 | + .collect(); |
| 212 | + |
| 213 | + // Merge results sequentially (add_module takes &mut self) |
| 214 | + let mut index = DefinitionIndex::new(); |
| 215 | + let mut smap = HashMap::with_capacity(parse_results.len()); |
| 216 | + let mut mfmap = HashMap::new(); |
| 217 | + let mut parsed_modules = Vec::new(); |
| 218 | + for (path, file_uri, source, parsed) in parse_results { |
| 219 | + if let Some((module, mod_name)) = parsed { |
| 220 | + index.add_module(&module, &path); |
194 | 221 | mfmap.insert(mod_name, file_uri.clone()); |
195 | 222 | parsed_modules.push(module); |
196 | | - smap.insert(file_uri, source.clone()); |
197 | | - } else { |
198 | | - smap.insert( |
199 | | - Url::from_file_path(path) |
200 | | - .map(|u| u.to_string()) |
201 | | - .unwrap_or_default(), |
202 | | - source.clone(), |
203 | | - ); |
204 | 223 | } |
| 224 | + smap.insert(file_uri, source); |
205 | 225 | } |
206 | 226 |
|
207 | 227 | let exports = crate::lsp::utils::resolve::ResolutionExports::new(&parsed_modules); |
@@ -234,6 +254,7 @@ impl Backend { |
234 | 254 | }) |
235 | 255 | .await; |
236 | 256 | }); |
237 | | - }); |
| 257 | + }) |
| 258 | + .expect("failed to spawn load-sources thread"); |
238 | 259 | } |
239 | 260 | } |
0 commit comments