1use crate::engine::VertexId;
2use crate::engine::VertexKind;
3use crate::engine::eval::Engine;
4use crate::traits::{
5 EvaluationContext, FunctionProvider, NamedRangeResolver, Range, RangeResolver,
6 ReferenceResolver, Resolver, SourceResolver, Table, TableResolver,
7};
8use formualizer_common::{ExcelError, LiteralValue};
9use formualizer_parse::parser::{ReferenceType, TableReference};
10use rustc_hash::FxHashSet;
11use std::sync::Mutex;
12
13use crate::interpreter::Interpreter;
14
15pub struct DynamicRefCollector<'a, R: EvaluationContext> {
16 pub engine: &'a Engine<R>,
17 pub current_sheet: &'a str,
18 pub collected: Mutex<FxHashSet<VertexId>>,
19}
20
21impl<'a, R: EvaluationContext> DynamicRefCollector<'a, R> {
22 pub fn new(engine: &'a Engine<R>, current_sheet: &'a str) -> Self {
23 Self {
24 engine,
25 current_sheet,
26 collected: Mutex::new(FxHashSet::default()),
27 }
28 }
29
30 fn collect_formula_vertices_in_rect(
31 &self,
32 sheet_name: &str,
33 sr: u32,
34 sc: u32,
35 er: u32,
36 ec: u32,
37 ) {
38 let Some(sheet_id) = self.engine.graph.sheet_id(sheet_name) else {
39 return;
40 };
41 let Some(index) = self.engine.graph.sheet_index(sheet_id) else {
42 return;
43 };
44
45 let sr0 = sr.saturating_sub(1);
46 let er0 = er.saturating_sub(1);
47 let sc0 = sc.saturating_sub(1);
48 let ec0 = ec.saturating_sub(1);
49
50 let mut out = self.collected.lock().unwrap();
51 for u in index.vertices_in_col_range(sc0, ec0) {
52 let row0 = self.engine.graph.vertex_coord(u).row();
53 if row0 < sr0 || row0 > er0 {
54 continue;
55 }
56 match self.engine.graph.get_vertex_kind(u) {
57 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
58 if self.engine.graph.is_dirty(u) || self.engine.graph.is_volatile(u) {
59 out.insert(u);
60 }
61 }
62 _ => {}
63 }
64 }
65 }
66
67 fn collect_formula_vertices_for_range(
68 &self,
69 sheet_name: &str,
70 start_row: Option<u32>,
71 start_col: Option<u32>,
72 end_row: Option<u32>,
73 end_col: Option<u32>,
74 ) {
75 let mut sr = start_row;
76 let mut sc = start_col;
77 let mut er = end_row;
78 let mut ec = end_col;
79
80 if sr.is_none() && er.is_none() {
81 let scv = sc.unwrap_or(1u32);
82 let ecv = ec.unwrap_or(scv);
83 sr = Some(1);
84 if let Some((_, max_r)) = self.engine.used_rows_for_columns(sheet_name, scv, ecv) {
85 er = Some(max_r);
86 } else if self.engine.sheet_bounds(sheet_name).is_some() {
87 er = Some(self.engine.config.max_open_ended_rows);
88 }
89 }
90 if sc.is_none() && ec.is_none() {
91 let srv = sr.unwrap_or(1u32);
92 let erv = er.unwrap_or(srv);
93 sc = Some(1);
94 if let Some((_, max_c)) = self.engine.used_cols_for_rows(sheet_name, srv, erv) {
95 ec = Some(max_c);
96 } else if self.engine.sheet_bounds(sheet_name).is_some() {
97 ec = Some(self.engine.config.max_open_ended_cols);
98 }
99 }
100 if sr.is_some() && er.is_none() {
101 let scv = sc.unwrap_or(1u32);
102 let ecv = ec.unwrap_or(scv);
103 if let Some((_, max_r)) = self.engine.used_rows_for_columns(sheet_name, scv, ecv) {
104 er = Some(max_r);
105 } else if self.engine.sheet_bounds(sheet_name).is_some() {
106 er = Some(self.engine.config.max_open_ended_rows);
107 }
108 }
109 if er.is_some() && sr.is_none() {
110 sr = Some(1);
111 }
112 if sc.is_some() && ec.is_none() {
113 let srv = sr.unwrap_or(1u32);
114 let erv = er.unwrap_or(srv);
115 if let Some((_, max_c)) = self.engine.used_cols_for_rows(sheet_name, srv, erv) {
116 ec = Some(max_c);
117 } else if self.engine.sheet_bounds(sheet_name).is_some() {
118 ec = Some(self.engine.config.max_open_ended_cols);
119 }
120 }
121 if ec.is_some() && sc.is_none() {
122 sc = Some(1);
123 }
124
125 let sr = sr.unwrap_or(1);
126 let sc = sc.unwrap_or(1);
127 let er = er.unwrap_or(sr.saturating_sub(1));
128 let ec = ec.unwrap_or(sc.saturating_sub(1));
129 if er < sr || ec < sc {
130 return;
131 }
132
133 self.collect_formula_vertices_in_rect(sheet_name, sr, sc, er, ec);
134 }
135}
136
137impl<'a, R: EvaluationContext> ReferenceResolver for DynamicRefCollector<'a, R> {
138 fn resolve_cell_reference(
139 &self,
140 sheet: Option<&str>,
141 row: u32,
142 col: u32,
143 ) -> Result<LiteralValue, ExcelError> {
144 let sheet_name = sheet.unwrap_or(self.current_sheet);
145 if let Some(&vid) = self
146 .engine
147 .graph
148 .get_vertex_id_for_address(&self.engine.graph.make_cell_ref(sheet_name, row, col))
149 {
150 self.collected.lock().unwrap().insert(vid);
151 }
152 self.engine.resolve_cell_reference(sheet, row, col)
153 }
154}
155
156impl<'a, R: EvaluationContext> RangeResolver for DynamicRefCollector<'a, R> {
157 fn resolve_range_reference(
158 &self,
159 sheet: Option<&str>,
160 sr: Option<u32>,
161 sc: Option<u32>,
162 er: Option<u32>,
163 ec: Option<u32>,
164 ) -> Result<Box<dyn Range>, ExcelError> {
165 let sheet_name = sheet.unwrap_or(self.current_sheet);
166 self.collect_formula_vertices_for_range(sheet_name, sr, sc, er, ec);
167 self.engine.resolve_range_reference(sheet, sr, sc, er, ec)
168 }
169}
170
171impl<'a, R: EvaluationContext> NamedRangeResolver for DynamicRefCollector<'a, R> {
172 fn resolve_named_range_reference(
173 &self,
174 name: &str,
175 ) -> Result<Vec<Vec<LiteralValue>>, ExcelError> {
176 self.engine.resolve_named_range_reference(name)
177 }
178}
179
180impl<'a, R: EvaluationContext> TableResolver for DynamicRefCollector<'a, R> {
181 fn resolve_table_reference(&self, tref: &TableReference) -> Result<Box<dyn Table>, ExcelError> {
182 self.engine.resolve_table_reference(tref)
183 }
184}
185
186impl<'a, R: EvaluationContext> SourceResolver for DynamicRefCollector<'a, R> {
187 fn source_scalar_version(&self, name: &str) -> Option<u64> {
188 self.engine.source_scalar_version(name)
189 }
190 fn resolve_source_scalar(&self, name: &str) -> Result<LiteralValue, ExcelError> {
191 self.engine.resolve_source_scalar(name)
192 }
193 fn source_table_version(&self, name: &str) -> Option<u64> {
194 self.engine.source_table_version(name)
195 }
196 fn resolve_source_table(&self, name: &str) -> Result<Box<dyn Table>, ExcelError> {
197 self.engine.resolve_source_table(name)
198 }
199}
200
201impl<'a, R: EvaluationContext> Resolver for DynamicRefCollector<'a, R> {}
202
203impl<'a, R: EvaluationContext> FunctionProvider for DynamicRefCollector<'a, R> {
204 fn get_function(
205 &self,
206 ns: &str,
207 name: &str,
208 ) -> Option<std::sync::Arc<dyn crate::traits::Function>> {
209 self.engine.get_function(ns, name)
210 }
211}
212
213impl<'a, R: EvaluationContext> EvaluationContext for DynamicRefCollector<'a, R> {
214 fn resolve_range_view<'c>(
215 &'c self,
216 reference: &ReferenceType,
217 current_sheet: &str,
218 ) -> Result<crate::engine::range_view::RangeView<'c>, ExcelError> {
219 match reference {
221 ReferenceType::Cell {
222 sheet, row, col, ..
223 } => {
224 let sheet_name = sheet.as_deref().unwrap_or(current_sheet);
225 self.collect_formula_vertices_in_rect(sheet_name, *row, *col, *row, *col);
226 }
227 ReferenceType::Range {
228 sheet,
229 start_row,
230 start_col,
231 end_row,
232 end_col,
233 ..
234 } => {
235 let sheet_name = sheet.as_deref().unwrap_or(current_sheet);
236 self.collect_formula_vertices_for_range(
237 sheet_name, *start_row, *start_col, *end_row, *end_col,
238 );
239 }
240 ReferenceType::NamedRange(name) => {
241 let sid = self.engine.sheet_id(current_sheet);
242 if let Some(s) = sid
243 && let Some(nr) = self.engine.graph.resolve_name_entry(name, s)
244 {
245 let vid = nr.vertex;
246 self.collected.lock().unwrap().insert(vid);
247 }
248 }
249 ReferenceType::Table(_) => {
250 }
252 _ => {}
253 }
254
255 self.engine.resolve_range_view(reference, current_sheet)
256 }
257}
258
259pub struct RangeVirtualDepProvider;
260
261impl RangeVirtualDepProvider {
262 pub fn get_virtual_deps<R: EvaluationContext>(
263 engine: &Engine<R>,
264 v: VertexId,
265 ) -> Vec<VertexId> {
266 let mut deps = Vec::new();
267 if let Some(ranges) = engine.graph.get_range_dependencies(v) {
268 let current_sheet_id = engine.graph.get_vertex_sheet_id(v);
269 for r in ranges {
270 let sheet_id = match r.sheet {
271 formualizer_common::SheetLocator::Id(id) => id,
272 _ => current_sheet_id,
273 };
274 let sheet_name = engine.graph.sheet_name(sheet_id);
275
276 let mut sr = r.start_row.map(|b| b.index + 1);
277 let mut sc = r.start_col.map(|b| b.index + 1);
278 let mut er = r.end_row.map(|b| b.index + 1);
279 let mut ec = r.end_col.map(|b| b.index + 1);
280
281 if sr.is_none() && er.is_none() {
282 let scv = sc.unwrap_or(1u32);
283 let ecv = ec.unwrap_or(scv);
284 if let Some((min_r, max_r)) = engine.used_rows_for_columns(sheet_name, scv, ecv)
285 {
286 sr = Some(min_r);
287 er = Some(max_r);
288 } else if let Some((_max_rows, _)) = engine.sheet_bounds(sheet_name) {
289 sr = Some(1);
290 er = Some(engine.config.max_open_ended_rows);
291 }
292 }
293 if sc.is_none() && ec.is_none() {
294 let srv = sr.unwrap_or(1u32);
295 let erv = er.unwrap_or(srv);
296 if let Some((min_c, max_c)) = engine.used_cols_for_rows(sheet_name, srv, erv) {
297 sc = Some(min_c);
298 ec = Some(max_c);
299 } else if let Some((_, _max_cols)) = engine.sheet_bounds(sheet_name) {
300 sc = Some(1);
301 ec = Some(engine.config.max_open_ended_cols);
302 }
303 }
304 if sr.is_some() && er.is_none() {
305 let scv = sc.unwrap_or(1u32);
306 let ecv = ec.unwrap_or(scv);
307 if let Some((_, max_r)) = engine.used_rows_for_columns(sheet_name, scv, ecv) {
308 er = Some(max_r);
309 } else if let Some((_max_rows, _)) = engine.sheet_bounds(sheet_name) {
310 er = Some(engine.config.max_open_ended_rows);
311 }
312 }
313 if er.is_some() && sr.is_none() {
314 let scv = sc.unwrap_or(1u32);
315 let ecv = ec.unwrap_or(scv);
316 if let Some((min_r, _)) = engine.used_rows_for_columns(sheet_name, scv, ecv) {
317 sr = Some(min_r);
318 } else {
319 sr = Some(1);
320 }
321 }
322 if sc.is_some() && ec.is_none() {
323 let srv = sr.unwrap_or(1u32);
324 let erv = er.unwrap_or(srv);
325 if let Some((_, max_c)) = engine.used_cols_for_rows(sheet_name, srv, erv) {
326 ec = Some(max_c);
327 } else if let Some((_, _max_cols)) = engine.sheet_bounds(sheet_name) {
328 ec = Some(engine.config.max_open_ended_cols);
329 }
330 }
331 if ec.is_some() && sc.is_none() {
332 let srv = sr.unwrap_or(1u32);
333 let erv = er.unwrap_or(srv);
334 if let Some((min_c, _)) = engine.used_cols_for_rows(sheet_name, srv, erv) {
335 sc = Some(min_c);
336 } else {
337 sc = Some(1);
338 }
339 }
340
341 let sr = sr.unwrap_or(1);
342 let sc = sc.unwrap_or(1);
343 let er = er.unwrap_or(sr.saturating_sub(1));
344 let ec = ec.unwrap_or(sc.saturating_sub(1));
345 if er < sr || ec < sc {
346 continue;
347 }
348
349 if let Some(index) = engine.graph.sheet_index(sheet_id) {
350 let sr0 = sr.saturating_sub(1);
351 let er0 = er.saturating_sub(1);
352 let sc0 = sc.saturating_sub(1);
353 let ec0 = ec.saturating_sub(1);
354 for u in index.vertices_in_col_range(sc0, ec0) {
355 let pc = engine.graph.vertex_coord(u);
356 let row0 = pc.row();
357 if row0 < sr0 || row0 > er0 {
358 continue;
359 }
360 match engine.graph.get_vertex_kind(u) {
361 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
362 if (engine.graph.is_dirty(u) || engine.graph.is_volatile(u))
363 && u != v
364 {
365 deps.push(u);
366 }
367 }
368 _ => {}
369 }
370 }
371 }
372 }
373 }
374 deps
375 }
376}
377
378pub struct VirtualDepBuilder<'a, R: EvaluationContext> {
379 engine: &'a Engine<R>,
380}
381
382impl<'a, R: EvaluationContext> VirtualDepBuilder<'a, R> {
383 pub fn new(engine: &'a Engine<R>) -> Self {
384 Self { engine }
385 }
386 pub fn build(
387 &self,
388 candidates: &[VertexId],
389 ) -> (
390 rustc_hash::FxHashMap<VertexId, Vec<VertexId>>,
391 Vec<VertexId>,
392 ) {
393 let mut vdeps: rustc_hash::FxHashMap<VertexId, Vec<VertexId>> =
394 rustc_hash::FxHashMap::default();
395 let augmented_vertices: Vec<VertexId> = Vec::new(); for &v in candidates {
398 let mut deps = RangeVirtualDepProvider::get_virtual_deps(self.engine, v);
399 let dynamic_deps = DynamicRefVirtualDepProvider::get_virtual_deps(self.engine, v);
400
401 deps.extend(dynamic_deps);
402 deps.sort_unstable();
403 deps.dedup();
404
405 if !deps.is_empty() {
406 vdeps.insert(v, deps);
407 }
408 }
409
410 (vdeps, augmented_vertices)
411 }
412}
413
414pub struct DynamicRefVirtualDepProvider;
415
416impl DynamicRefVirtualDepProvider {
417 pub fn get_virtual_deps<R: EvaluationContext>(
418 engine: &Engine<R>,
419 v: VertexId,
420 ) -> Vec<VertexId> {
421 let mut deps = Vec::new();
422
423 if engine.graph.is_dynamic(v) {
424 if let Some(ast_id) = engine.graph.get_formula_id(v) {
426 let sheet_id = engine.graph.get_vertex_sheet_id(v);
427 let sheet_name = engine.graph.sheet_name(sheet_id);
428
429 let collector = DynamicRefCollector::new(engine, sheet_name);
430
431 let cell_ref = engine
432 .graph
433 .get_cell_ref(v)
434 .unwrap_or_else(|| engine.graph.make_cell_ref(sheet_name, 0, 0));
435
436 let interpreter = Interpreter::new_with_cell(&collector, sheet_name, cell_ref);
437
438 let _ = interpreter.evaluate_arena_ast(
440 ast_id,
441 engine.graph.data_store(),
442 engine.graph.sheet_reg(),
443 );
444
445 deps.extend(
446 collector
447 .collected
448 .lock()
449 .unwrap()
450 .iter()
451 .copied()
452 .filter(|&u| u != v),
453 );
454 }
455 }
456
457 deps
458 }
459}