Skip to main content

devpage_design/resolve/
again.rs

1//! Resolving again: a design kept resolved, and each change to a file it
2//! read resolved again only as far as the change can reach — in a saved
3//! design file, the elements whose own attributes changed and those
4//! inserted, removed or moved, in every instance of the file; for a
5//! `themes.yml`, a font or an image, the nodes that read it. What was
6//! resolved again is the [`Edits`] that bring the scene across: no scene is
7//! compared with another.
8//!
9//! What stays linear is reading the saved file: parsing it, matching its
10//! elements against its last parse ([`crate::ids`]) and walking the two
11//! side by side to see what changed.
12
13use std::collections::{BTreeSet, HashMap, HashSet};
14use std::sync::Arc;
15
16use super::{Caller, Instance, Record, Resolver, resolved};
17use crate::Problem;
18use crate::parse::{self, Child, Element, TextPart};
19use crate::scene::{self, Applied, Edit, Edits, Files, Holder, Layout, Node, Scene};
20use crate::tree::NodeId;
21
22/// A design kept resolved: where each node of its scene came from, the
23/// files it was made from, and the files changed since it was last valid.
24/// It keeps no scene: the design's one scene is its holder's, which
25/// resolving it again reads and brings across by the edits it makes.
26#[derive(Debug, Clone)]
27pub struct Resolution {
28    path: String,
29    record: Record,
30    /// The files the scene was made from.
31    files: Vec<String>,
32    /// The files changed since the scene was valid, which the next valid
33    /// resolve brings it across.
34    pending: BTreeSet<String>,
35    /// How many times the scene was brought across whole, by comparing it
36    /// with a fresh resolve: what the tests hold to none.
37    pub(crate) wholes: usize,
38    /// How many elements the last update resolved again, beside reading the
39    /// files it saw saved: what the tests hold to what the change reached,
40    /// whatever the design's size. A whole resolve is counted by `wholes`.
41    pub(crate) elements: usize,
42}
43
44/// What a change made of a design.
45#[derive(Debug)]
46pub enum Outcome {
47    /// Its scene is as it was, and valid.
48    Same,
49    /// Its scene changed by these edits — what the browser is sent, as a
50    /// patch — applied to it once, in its holder, as `applied` says.
51    Changed(Edits, Applied),
52    /// It has these problems, every one; its holder's scene is left the
53    /// last valid one.
54    Problems(Vec<Problem>),
55}
56
57impl Resolution {
58    /// The design at `path` resolved — what to resolve it again from, and
59    /// its scene, for a holder to keep — or every problem it has.
60    pub fn new(files: &dyn Files, path: &str) -> Result<(Resolution, Scene), Vec<Problem>> {
61        let (scene, record, read) = resolved(files, path)?;
62        let resolution = Resolution {
63            path: path.to_string(),
64            record,
65            files: read,
66            pending: BTreeSet::new(),
67            wholes: 0,
68            elements: 0,
69        };
70        Ok((resolution, scene))
71    }
72
73    /// The files the design's scene is made from: the design, its imports,
74    /// images, themes and fonts.
75    pub fn files(&self) -> &[String] {
76        &self.files
77    }
78
79    /// How many times the scene was brought across whole since it was first
80    /// resolved: by a comparison with a fresh resolve, where resolving parts
81    /// of it again couldn't.
82    pub fn wholes(&self) -> usize {
83        self.wholes
84    }
85
86    /// The design after the files `changed` — as the store names them, a
87    /// folder's listing by its path and a `/` — changed: what they reach
88    /// resolved again against the scene `held` holds — the design's last
89    /// valid one — and the edits that bring it across applied to it there,
90    /// once; or every problem the design now has, `held` left as it was. A
91    /// change while the design has problems is kept, and the change that
92    /// makes it valid again brings the scene across all of them.
93    pub fn update(
94        &mut self,
95        held: &mut dyn Holder,
96        files: &dyn Files,
97        changed: &[String],
98    ) -> Outcome {
99        self.pending.extend(changed.iter().cloned());
100        match self.again(held, files) {
101            Ok(outcome) => {
102                self.pending.clear();
103                outcome
104            }
105            Err(()) => match resolved(files, &self.path) {
106                Err(problems) => Outcome::Problems(problems),
107                // The parts resolved again had a problem the whole design
108                // hasn't: it is brought across whole.
109                Ok((scene, record, read)) => {
110                    self.pending.clear();
111                    self.whole(held, scene, record, read)
112                }
113            },
114        }
115    }
116
117    /// The scene `held` holds brought across to `scene`, resolved afresh, by
118    /// the edits between the two.
119    fn whole(
120        &mut self,
121        held: &mut dyn Holder,
122        scene: Scene,
123        record: Record,
124        read: Vec<String>,
125    ) -> Outcome {
126        self.wholes += 1;
127        self.record = record;
128        self.files = read;
129        let was_themed = held.scene().themed;
130        let edits = Edits::between(held.scene(), &scene);
131        if edits.list.is_empty() && was_themed == edits.themed {
132            return Outcome::Same;
133        }
134        match held.apply(&edits) {
135            Ok(applied) => Outcome::Changed(edits, applied),
136            // Edits made between two scenes apply to the first; if they
137            // ever didn't, the scene starts again from the new one: its
138            // root replaces the held one's, whatever that holds.
139            Err(_) => {
140                let edits = Edits::whole(&scene);
141                match held.apply(&edits) {
142                    Ok(applied) => Outcome::Changed(edits, applied),
143                    Err(e) => Outcome::Problems(vec![Problem {
144                        file: self.path.clone(),
145                        pos: parse::Pos { line: 1, col: 1 },
146                        message: format!("the design engine failed on this design: {e}"),
147                    }]),
148                }
149            }
150        }
151    }
152
153    /// The pending changes resolved again against `held`'s scene and brought
154    /// across; or `Err` when the design has a problem now, `held` as it was.
155    fn again(&mut self, held: &mut dyn Holder, files: &dyn Files) -> Result<Outcome, ()> {
156        let pending: Vec<String> = self.pending.iter().cloned().collect();
157        let scene = held.scene();
158        let mut w = Work {
159            r: Resolver::new(files),
160            files,
161            res: self,
162            scene,
163            edits: Vec::new(),
164            forget: Vec::new(),
165            forget_instances: Vec::new(),
166            covered: HashSet::new(),
167            covered_instances: Vec::new(),
168            own: HashMap::new(),
169            parses: HashMap::new(),
170            indexes: HashMap::new(),
171            failed: false,
172            whole: false,
173        };
174        for key in &pending {
175            if w.res.record.parses.contains_key(key) {
176                w.file(key);
177            }
178        }
179        for key in &pending {
180            let readers: Vec<Vec<u32>> = w
181                .res
182                .record
183                .readers
184                .get(key)
185                .map(|r| r.iter().cloned().collect())
186                .unwrap_or_default();
187            for id in readers {
188                w.reader(key, &id);
189            }
190        }
191        let Work {
192            r,
193            edits,
194            forget,
195            forget_instances,
196            parses,
197            failed,
198            whole,
199            ..
200        } = w;
201        self.elements = r.elements;
202        if failed || !r.problems.is_empty() {
203            return Err(());
204        }
205        if whole {
206            let (scene, record, read) = resolved(files, &self.path).map_err(|_| ())?;
207            return Ok(self.whole(held, scene, record, read));
208        }
209        // What the parts resolved again read and made replaces what the
210        // nodes they replace did. Were the edits refused, the design is
211        // resolved again whole, and that record replaces this one.
212        let record = &mut self.record;
213        for prefix in &forget_instances {
214            record.forget_instances(prefix);
215        }
216        for id in &forget {
217            record.forget(&id.0);
218            record.themed.remove(id);
219        }
220        record.merge(r.record);
221        record.parses.extend(parses);
222        let themed = !record.themed.is_empty();
223        if edits.is_empty() && themed == scene.themed {
224            return Ok(Outcome::Same);
225        }
226        let edits = Edits::onto(scene, edits, &r.images, &r.fonts, themed);
227        // Applied once, where the scene is held.
228        let applied = held.apply(&edits).map_err(|_| ())?;
229        for file in r.read {
230            if !self.files.contains(&file) {
231                self.files.push(file);
232            }
233        }
234        // Even no node changed when only whether the scene is themed did.
235        Ok(Outcome::Changed(edits, applied))
236    }
237}
238
239impl Record {
240    /// Every instance under `prefix` — and it — gone, with what their nodes
241    /// read and whether a theme reached them.
242    fn forget_instances(&mut self, prefix: &[u32]) {
243        let gone: Vec<Vec<u32>> = self
244            .instances
245            .range(prefix.to_vec()..)
246            .take_while(|(k, _)| k.starts_with(prefix))
247            .map(|(k, _)| k.clone())
248            .collect();
249        for k in gone {
250            if let Some(i) = self.instances.remove(&k)
251                && let Some(set) = self.by_file.get_mut(&i.file)
252            {
253                set.remove(&k);
254            }
255        }
256        let read: Vec<Vec<u32>> = self
257            .reads
258            .range(prefix.to_vec()..)
259            .take_while(|(k, _)| k.starts_with(prefix))
260            .map(|(k, _)| k.clone())
261            .collect();
262        for k in read {
263            self.forget(&k);
264        }
265        let themed: Vec<NodeId> = self
266            .themed
267            .range(NodeId(prefix.to_vec())..)
268            .take_while(|k| k.0.starts_with(prefix))
269            .cloned()
270            .collect();
271        for k in themed {
272            self.themed.remove(&k);
273        }
274    }
275
276    /// What `other`, made resolving parts again, recorded, added.
277    fn merge(&mut self, other: Record) {
278        self.instances.extend(other.instances);
279        for (file, set) in other.by_file {
280            self.by_file.entry(file).or_default().extend(set);
281        }
282        for (key, set) in other.readers {
283            self.readers.entry(key).or_default().extend(set);
284        }
285        for (id, set) in other.reads {
286            self.reads.entry(id).or_default().extend(set);
287        }
288        self.themed.extend(other.themed);
289        self.parses.extend(other.parses);
290    }
291}
292
293/// One update's work: the parts resolved again, the edits they make, and
294/// what the nodes they replace recorded, to forget.
295struct Work<'a, 'r> {
296    r: Resolver<'a>,
297    files: &'a dyn Files,
298    res: &'r Resolution,
299    /// The scene as it is held: what the edits are found against.
300    scene: &'r Scene,
301    edits: Vec<Edit>,
302    /// Nodes whose record goes.
303    forget: Vec<NodeId>,
304    /// Instances whose record goes, with everything in them.
305    forget_instances: Vec<Vec<u32>>,
306    /// Nodes resolved again whole, or removed, this update: nothing more to
307    /// do for them.
308    covered: HashSet<NodeId>,
309    covered_instances: Vec<Vec<u32>>,
310    /// The own attributes of the nodes resolved again this update.
311    own: HashMap<NodeId, Node>,
312    /// Each design file saved, parsed anew.
313    parses: HashMap<String, Arc<parse::File>>,
314    /// Each file's elements by id, where in its tree each is.
315    indexes: HashMap<String, HashMap<u32, Vec<usize>>>,
316    /// Whether something already showed the design has a problem.
317    failed: bool,
318    /// Whether the design is resolved again whole.
319    whole: bool,
320}
321
322/// What changed in a design file, element by element: the walk of its last
323/// parse and its new one side by side.
324enum Act<'p> {
325    /// Its own attributes, resolved again; whether it is the file's root.
326    Own(&'p Element, bool),
327    /// It, with its subtree, resolved again in place of the old one.
328    Whole(&'p Element, &'p Element),
329    Insert {
330        parent: &'p Element,
331        after: Option<&'p Element>,
332        el: &'p Element,
333    },
334    Remove(&'p Element),
335    Move {
336        parent: &'p Element,
337        after: Option<&'p Element>,
338        el: &'p Element,
339    },
340}
341
342impl Work<'_, '_> {
343    /// The design file `path` was saved: its new parse walked beside the last,
344    /// and what changed resolved again in every instance of it.
345    fn file(&mut self, path: &str) {
346        let old = self.res.record.parses[path].clone();
347        let Some(source) = self.files.read(path) else {
348            self.failed = true;
349            return;
350        };
351        let new = match self.files.parse(path, &source) {
352            Ok(new) => new,
353            Err(_) => {
354                self.failed = true;
355                return;
356            }
357        };
358        self.parses.insert(path.to_string(), new.clone());
359        self.indexes.remove(path);
360        // The file's own problems: its imports and declared props.
361        let mut scope = parse::File {
362            imports: new.imports.clone(),
363            root: Element {
364                id: new.root.id,
365                name: new.root.name.clone(),
366                attrs: new.root.attrs.clone(),
367                children: Vec::new(),
368                pos: new.root.pos,
369            },
370            next_id: new.next_id,
371        };
372        self.r.instance = Vec::new();
373        self.r.current = None;
374        // What binding it records belongs to no node.
375        let saved = std::mem::take(&mut self.r.record);
376        self.r.bind_own(path, &mut scope.root);
377        let scoped = self.r.scope(path, &scope);
378        self.r.record = saved;
379        let Some((imports, _)) = scoped else {
380            self.failed = true;
381            return;
382        };
383        let old_imports = import_map(path, &old);
384        let props = |f: &parse::File| f.root.attrs.iter().find(|a| a.name == "props").cloned();
385        let props_same = match (props(&old), props(&new)) {
386            (Some(a), Some(b)) => crate::ids::same_value(&a.value, &b.value),
387            (None, None) => true,
388            _ => false,
389        };
390        let instances: Vec<Vec<u32>> = self
391            .res
392            .record
393            .by_file
394            .get(path)
395            .map(|s| s.iter().cloned().collect())
396            .unwrap_or_default();
397        if new.root.id != old.root.id || new.root.name != old.root.name || !props_same {
398            // What every element of the file is resolved in changed.
399            for i in instances {
400                self.whole_instance(&i);
401            }
402            return;
403        }
404        // The names whose import changed: their instances are made anew.
405        let retargeted: HashSet<&str> = imports
406            .keys()
407            .chain(old_imports.keys())
408            .filter(|n| imports.get(*n) != old_imports.get(*n))
409            .map(String::as_str)
410            .collect();
411        let is_instance = |el: &Element| imports.contains_key(&el.name);
412        let was_instance = |el: &Element| old_imports.contains_key(&el.name);
413        let mut acts = Vec::new();
414        let mut dup = false;
415        walk(
416            &old.root,
417            &new.root,
418            true,
419            &|el| is_instance(el),
420            &|el| was_instance(el),
421            &retargeted,
422            &mut acts,
423            &mut dup,
424        );
425        if dup {
426            self.failed = true;
427            return;
428        }
429        for i in instances {
430            if self.is_covered_instance(&i) {
431                continue;
432            }
433            let Some(inst) = self.res.record.instances.get(&i).cloned() else {
434                continue;
435            };
436            for act in &acts {
437                self.act(&i, &inst, path, act);
438            }
439        }
440    }
441
442    /// One change to an element, done in the instance `i` of its file.
443    fn act(&mut self, i: &[u32], inst: &Instance, path: &str, act: &Act) {
444        match *act {
445            Act::Own(el, root) => {
446                let id = if root {
447                    inst.root.clone()
448                } else {
449                    node_id(i, el)
450                };
451                self.own_again(i, inst, path, el, &id, root);
452            }
453            Act::Whole(el, old) => {
454                let was = self.node_of(i, inst, old, true);
455                self.whole_again(i, inst, path, el, &was);
456            }
457            Act::Insert { parent, after, el } => {
458                let parent_id = self.node_of(i, inst, parent, false);
459                let after = after.map(|a| self.node_of(i, inst, a, false));
460                if self.covered.contains(&parent_id) {
461                    return;
462                }
463                let layout = self.layout(&parent_id);
464                let Some(node) = self.subtree(i, inst, path, el, &parent_id, layout) else {
465                    return;
466                };
467                self.edits.push(Edit::Insert {
468                    parent: Some(parent_id),
469                    after,
470                    node,
471                });
472            }
473            Act::Remove(old) => {
474                let id = self.node_of(i, inst, old, true);
475                if self.covered.contains(&id) {
476                    return;
477                }
478                self.drop_subtree(&id);
479                self.edits.push(Edit::Remove(id));
480            }
481            Act::Move { parent, after, el } => {
482                let id = self.node_of(i, inst, el, false);
483                let parent = self.node_of(i, inst, parent, false);
484                let after = after.map(|a| self.node_of(i, inst, a, false));
485                self.edits.push(Edit::Move { id, parent, after });
486            }
487        }
488    }
489
490    /// The node an element of the instance `i` makes: its own, or — for an
491    /// instance of another design — that instance's root. `old` when the
492    /// element is as the last parse had it.
493    fn node_of(&self, i: &[u32], inst: &Instance, el: &Element, old: bool) -> NodeId {
494        if el.id == root_id(inst) {
495            return inst.root.clone();
496        }
497        let mut key = i.to_vec();
498        key.push(el.id);
499        // An instance of another design: made this update, or kept.
500        let made = if old {
501            self.res.record.instances.get(&key)
502        } else {
503            self.r
504                .record
505                .instances
506                .get(&key)
507                .or_else(|| self.res.record.instances.get(&key))
508        };
509        match made {
510            Some(child) => child.root.clone(),
511            None => NodeId(key),
512        }
513    }
514
515    /// The layout of the frame `parent`, as this update leaves it.
516    fn layout(&self, parent: &NodeId) -> Option<Layout> {
517        self.own
518            .get(parent)
519            .or_else(|| self.scene.node(parent))
520            .map(|n| n.layout)
521    }
522
523    /// The node or instance `id`, which read the file `key`, resolved again.
524    fn reader(&mut self, key: &str, id: &[u32]) {
525        if self.is_covered_instance(id) {
526            return;
527        }
528        if let Some(inst) = self.res.record.instances.get(id).cloned() {
529            if key.ends_with(super::THEMES_FILE) {
530                // An instance given a theme color: what its props hold.
531                self.whole_instance(id);
532            } else {
533                // A file an instance's design imports: its imports, checked.
534                let Some(file) = self.parse_of(&inst.file) else {
535                    self.failed = true;
536                    return;
537                };
538                self.r.instance = id.to_vec();
539                let mut scope = (*file).clone();
540                scope.root.children.clear();
541                let saved = std::mem::take(&mut self.r.record);
542                if self.r.scope(&inst.file, &scope).is_none() {
543                    self.failed = true;
544                }
545                self.r.record = saved;
546            }
547            return;
548        }
549        let node = NodeId(id.to_vec());
550        if self.covered.contains(&node) || self.own.contains_key(&node) {
551            return;
552        }
553        let (prefix, el_id) = id.split_at(id.len() - 1);
554        let Some(inst) = self.res.record.instances.get(prefix).cloned() else {
555            return;
556        };
557        let Some(file) = self.parse_of(&inst.file) else {
558            self.failed = true;
559            return;
560        };
561        let Some(el) = self.element(&inst.file, &file, el_id[0]) else {
562            return;
563        };
564        let root = node == inst.root;
565        self.own_again(prefix, &inst, &inst.file.clone(), &el, &node, root);
566    }
567
568    /// The parse a design file's elements are resolved from this update.
569    fn parse_of(&self, path: &str) -> Option<Arc<parse::File>> {
570        self.parses
571            .get(path)
572            .or_else(|| self.res.record.parses.get(path))
573            .cloned()
574    }
575
576    /// The element of `file`, `path`'s parse, whose id is `id`.
577    fn element(&mut self, path: &str, file: &parse::File, id: u32) -> Option<Element> {
578        let index = self
579            .indexes
580            .entry(path.to_string())
581            .or_insert_with(|| index(&file.root));
582        let at = index.get(&id)?;
583        let mut el = &file.root;
584        for &i in at {
585            match &el.children[i] {
586                Child::Element(e) => el = e,
587                Child::Text(..) => return None,
588            }
589        }
590        Some(shallow(el))
591    }
592
593    /// The instance `i` resolved again whole, from the element that makes it
594    /// in its caller's file — or, for the design's own root, the design.
595    fn whole_instance(&mut self, i: &[u32]) {
596        if self.is_covered_instance(i) {
597            return;
598        }
599        let Some((caller, el_id)) = i.split_last().map(|(l, p)| (p, *l)) else {
600            self.whole = true;
601            return;
602        };
603        let (Some(inst), Some(at)) = (
604            self.res.record.instances.get(caller).cloned(),
605            self.res.record.instances.get(i).cloned(),
606        ) else {
607            self.whole = true;
608            return;
609        };
610        let Some(file) = self.parse_of(&inst.file) else {
611            self.failed = true;
612            return;
613        };
614        let Some(el) = self.element_whole(&inst.file, &file, el_id) else {
615            // The element that made it is gone: its file's walk removes it.
616            return;
617        };
618        self.whole_again(caller, &inst, &inst.file.clone(), &el, &at.root);
619    }
620
621    /// The element of `file` whose id is `id`, with its subtree.
622    fn element_whole(&mut self, path: &str, file: &parse::File, id: u32) -> Option<Element> {
623        let index = self
624            .indexes
625            .entry(path.to_string())
626            .or_insert_with(|| index(&file.root));
627        let at = index.get(&id)?;
628        let mut el = &file.root;
629        for &i in at {
630            match &el.children[i] {
631                Child::Element(e) => el = e,
632                Child::Text(..) => return None,
633            }
634        }
635        Some(el.clone())
636    }
637
638    fn is_covered_instance(&self, i: &[u32]) -> bool {
639        self.covered_instances.iter().any(|p| i.starts_with(p))
640    }
641
642    /// Sets the resolver up to resolve in the instance `i`: the designs
643    /// enclosing it, and how deep the node at `order` is.
644    fn enter(&mut self, i: &[u32], depth: usize) {
645        self.r.instance = i.to_vec();
646        self.r.stack = (0..=i.len())
647            .filter_map(|k| self.res.record.instances.get(&i[..k]))
648            .map(|inst| inst.file.clone())
649            .collect();
650        self.r.depth = depth;
651        self.r.current = None;
652    }
653
654    /// How deep, counting through instances, the node `id` at `order` is.
655    fn depth_of(&self, id: &NodeId) -> usize {
656        let order = self.scene.ids.get(id).map_or(0, |o| o.len());
657        order + id.0.len() - 1
658    }
659
660    /// `el`'s own attributes — in the instance `i` of its file, `path` —
661    /// resolved again: its node `id` takes them if they differ.
662    fn own_again(
663        &mut self,
664        i: &[u32],
665        inst: &Instance,
666        path: &str,
667        el: &Element,
668        id: &NodeId,
669        root: bool,
670    ) {
671        if self.covered.contains(id) || self.own.contains_key(id) {
672            return;
673        }
674        let Some(was) = self.scene.node(id) else {
675            return;
676        };
677        let parent = if root {
678            inst.parent
679        } else {
680            let order = &self.scene.ids.get(id).expect("indexed")[..];
681            let parent = self.scene.at(&order[..order.len() - 1]);
682            parent
683                .and_then(|p| self.own.get(&p.id).or(Some(p)))
684                .map(|p| p.layout)
685        };
686        let depth = self.depth_of(id);
687        self.enter(i, depth);
688        let mut el = shallow(el);
689        self.r.bind_own(path, &mut el);
690        self.r.own_only = true;
691        let node = self
692            .r
693            .element(path, &el, &inst.props, &inst.imports, root, parent);
694        self.r.own_only = false;
695        let Some(mut node) = node else {
696            self.failed = true;
697            return;
698        };
699        if root && let Some(caller) = &inst.caller {
700            overrides(&mut self.r, caller, &mut node, inst.parent);
701        }
702        self.forget.push(id.clone());
703        if !node.same_own(was) {
704            self.edits.push(Edit::Change(node.own()));
705        }
706        self.own.insert(id.clone(), node);
707    }
708
709    /// `el` — in the instance `i` of its file, `path` — resolved again whole,
710    /// in place of the subtree of the node `was`.
711    fn whole_again(&mut self, i: &[u32], inst: &Instance, path: &str, el: &Element, was: &NodeId) {
712        if self.covered.contains(was) {
713            return;
714        }
715        let Some(order) = self.scene.ids.get(was).cloned() else {
716            return;
717        };
718        let parent_order = &order[..order.len().saturating_sub(1)];
719        let Some(parent) = (!order.is_empty())
720            .then(|| self.scene.at(parent_order))
721            .flatten()
722        else {
723            // The scene's root: the design, resolved again whole.
724            self.whole = true;
725            return;
726        };
727        let parent_id = parent.id.clone();
728        let after = parent
729            .children
730            .key_before(order.last().expect("not the root"))
731            .and_then(|k| parent.children.get(k))
732            .map(|n| n.id.clone());
733        let layout = self.layout(&parent_id);
734        let Some(node) = self.subtree(i, inst, path, el, &parent_id, layout) else {
735            return;
736        };
737        self.drop_subtree(was);
738        if node.id == *was {
739            self.edits.extend(scene::subtree(self.scene, &order, &node));
740        } else {
741            self.edits.push(Edit::Remove(was.clone()));
742            self.edits.push(Edit::Insert {
743                parent: Some(parent_id),
744                after,
745                node,
746            });
747        }
748    }
749
750    /// `el` and every element in it — in the instance `i` of its file,
751    /// `path` — resolved under the node `parent`, of layout `layout`.
752    fn subtree(
753        &mut self,
754        i: &[u32],
755        inst: &Instance,
756        path: &str,
757        el: &Element,
758        parent: &NodeId,
759        layout: Option<Layout>,
760    ) -> Option<Node> {
761        let depth = self.depth_of(parent) + 1;
762        self.enter(i, depth);
763        let mut el = el.clone();
764        self.r.bind_element(path, &mut el);
765        let node = self
766            .r
767            .element(path, &el, &inst.props, &inst.imports, false, layout);
768        if node.is_none() {
769            self.failed = true;
770        }
771        node
772    }
773
774    /// The subtree of the node `id` is going: what its nodes and instances
775    /// recorded goes with it.
776    fn drop_subtree(&mut self, id: &NodeId) {
777        let Some(node) = self.scene.node(id) else {
778            return;
779        };
780        let mut todo = vec![node];
781        while let Some(n) = todo.pop() {
782            self.covered.insert(n.id.clone());
783            self.forget.push(n.id.clone());
784            let (prefix, _) = n.id.0.split_at(n.id.0.len() - 1);
785            let instance_root = !prefix.is_empty()
786                && self
787                    .res
788                    .record
789                    .instances
790                    .get(prefix)
791                    .is_some_and(|inst| inst.root == n.id)
792                && n.id != *id;
793            if instance_root || (n.id == *id && self.is_root_of_instance(&n.id)) {
794                self.forget_instances.push(prefix.to_vec());
795                self.covered_instances.push(prefix.to_vec());
796                // Its nodes are the instance's; marked covered as they come.
797                for c in &n.children {
798                    todo.push(c);
799                }
800                continue;
801            }
802            for c in &n.children {
803                todo.push(c);
804            }
805        }
806    }
807
808    fn is_root_of_instance(&self, id: &NodeId) -> bool {
809        let (prefix, _) = id.0.split_at(id.0.len() - 1);
810        !prefix.is_empty()
811            && self
812                .res
813                .record
814                .instances
815                .get(prefix)
816                .is_some_and(|inst| inst.root == *id)
817    }
818}
819
820/// The attributes an instance's caller lays over its root, applied again.
821fn overrides(r: &mut Resolver, caller: &Caller, root: &mut Node, parent: Option<Layout>) {
822    r.current = Some(root.id.clone());
823    for (attr, by) in &caller.overrides {
824        r.common(&caller.file, by, root, attr, attr.value.clone());
825        r.fill_needs_layout(&caller.file, attr, &attr.value, parent);
826        r.place_needs_no_layout(&caller.file, attr, parent);
827    }
828}
829
830/// The element id of an instance's root.
831fn root_id(inst: &Instance) -> u32 {
832    *inst.root.0.last().expect("an id")
833}
834
835fn node_id(i: &[u32], el: &Element) -> NodeId {
836    let mut id = i.to_vec();
837    id.push(el.id);
838    NodeId(id)
839}
840
841/// A design file's imports, at `path`, by name: the files they name.
842fn import_map(path: &str, file: &parse::File) -> HashMap<String, String> {
843    file.imports
844        .iter()
845        .filter_map(|i| Some((i.name.clone(), super::join(path, &i.path)?)))
846        .collect()
847}
848
849/// An element without its child elements: what resolving its own attributes
850/// needs — a `Text`'s text included.
851fn shallow(el: &Element) -> Element {
852    Element {
853        id: el.id,
854        name: el.name.clone(),
855        attrs: el.attrs.clone(),
856        children: el
857            .children
858            .iter()
859            .filter(|c| matches!(c, Child::Text(..)))
860            .cloned()
861            .collect(),
862        pos: el.pos,
863    }
864}
865
866/// Where in `root`'s tree each element is, by id: its path of child places.
867fn index(root: &Element) -> HashMap<u32, Vec<usize>> {
868    let mut out = HashMap::new();
869    let mut todo = vec![(root, Vec::new())];
870    while let Some((el, at)) = todo.pop() {
871        for (i, c) in el.children.iter().enumerate() {
872            if let Child::Element(e) = c {
873                let mut here = at.clone();
874                here.push(i);
875                todo.push((e, here));
876            }
877        }
878        out.insert(el.id, at);
879    }
880    out
881}
882
883/// Whether two elements' text is the same but for where it is written.
884fn same_text(a: &Element, b: &Element) -> bool {
885    let texts = |e: &Element| {
886        e.children
887            .iter()
888            .filter_map(|c| match c {
889                Child::Text(parts, _) => Some(
890                    parts
891                        .iter()
892                        .map(|p| match p {
893                            TextPart::Literal(s) => (0, s.clone()),
894                            TextPart::Prop(s) => (1, s.clone()),
895                            TextPart::Theme(s, _) => (2, s.clone()),
896                        })
897                        .collect::<Vec<_>>(),
898                ),
899                Child::Element(_) => None,
900            })
901            .collect::<Vec<_>>()
902    };
903    texts(a) == texts(b)
904}
905
906/// Whether an element is written in another place than before, in a way a
907/// node keeps: where its shadow and its image's `src` are written.
908fn moved_origin(a: &Element, b: &Element) -> bool {
909    let at = |e: &Element| {
910        e.attrs
911            .iter()
912            .filter(|x| x.name == "shadow" || x.name == "src")
913            .map(|x| (x.pos.line, x.pos.col))
914            .collect::<Vec<_>>()
915    };
916    at(a) != at(b)
917}
918
919fn children(el: &Element) -> Vec<&Element> {
920    el.children
921        .iter()
922        .filter_map(|c| match c {
923            Child::Element(e) => Some(e),
924            Child::Text(..) => None,
925        })
926        .collect()
927}
928
929/// Walks `old` and `new`, the same element in a file's last parse and its
930/// new one, and says what changed: its own attributes, then its children —
931/// moved, inserted, removed — each kept child walked in turn. A sibling list
932/// with a key twice is a problem.
933#[allow(clippy::too_many_arguments)]
934fn walk<'p>(
935    old: &'p Element,
936    new: &'p Element,
937    root: bool,
938    is_instance: &dyn Fn(&Element) -> bool,
939    was_instance: &dyn Fn(&Element) -> bool,
940    retargeted: &HashSet<&str>,
941    out: &mut Vec<Act<'p>>,
942    dup: &mut bool,
943) {
944    let own_same = crate::ids::same_own(old, new) && same_text(old, new);
945    let instance = is_instance(new) || was_instance(old);
946    if !root
947        && (old.name != new.name
948            || is_instance(new) != was_instance(old)
949            || (instance && (!own_same || retargeted.contains(new.name.as_str()))))
950    {
951        out.push(Act::Whole(new, old));
952        return;
953    }
954    if !own_same || moved_origin(old, new) {
955        out.push(Act::Own(new, root));
956    }
957    let layout = |e: &Element| e.attrs.iter().find(|a| a.name == "layout").cloned();
958    let relayout = match (layout(old), layout(new)) {
959        (Some(a), Some(b)) => !crate::ids::same_value(&a.value, &b.value),
960        (None, None) => false,
961        _ => true,
962    };
963    let olds = children(old);
964    let news = children(new);
965    // A key twice among siblings.
966    let mut keys = HashSet::new();
967    for c in &news {
968        if let Some(k) = crate::ids::key(c)
969            && !keys.insert(k)
970        {
971            *dup = true;
972        }
973    }
974    let rank: HashMap<u32, usize> = olds.iter().enumerate().map(|(i, e)| (e.id, i)).collect();
975    let ranks: Vec<Option<usize>> = news.iter().map(|e| rank.get(&e.id).copied()).collect();
976    let stay = longest_run(&ranks);
977    let by_id: HashMap<u32, &Element> = olds.iter().map(|e| (e.id, *e)).collect();
978    let mut prev: Option<&Element> = None;
979    for (n, c) in news.iter().enumerate() {
980        match by_id.get(&c.id) {
981            Some(o) => {
982                if !stay[n] {
983                    out.push(Act::Move {
984                        parent: new,
985                        after: prev,
986                        el: c,
987                    });
988                }
989                walk(o, c, false, is_instance, was_instance, retargeted, out, dup);
990                // A child of a frame whose layout changed is checked against it.
991                if relayout {
992                    if is_instance(c) {
993                        out.push(Act::Whole(c, o));
994                    } else {
995                        out.push(Act::Own(c, false));
996                    }
997                }
998            }
999            None => out.push(Act::Insert {
1000                parent: new,
1001                after: prev,
1002                el: c,
1003            }),
1004        }
1005        prev = Some(c);
1006    }
1007    let kept: HashSet<u32> = news.iter().map(|e| e.id).collect();
1008    for o in olds {
1009        if !kept.contains(&o.id) {
1010            out.push(Act::Remove(o));
1011        }
1012    }
1013}
1014
1015/// Which of a list stay where they are: the longest increasing run of their
1016/// places before, `None` for those that weren't there.
1017fn longest_run(ranks: &[Option<usize>]) -> Vec<bool> {
1018    let mut tails: Vec<usize> = Vec::new();
1019    let mut before: Vec<Option<usize>> = vec![None; ranks.len()];
1020    for (i, r) in ranks.iter().enumerate() {
1021        let Some(r) = r else { continue };
1022        let at = tails.partition_point(|&t| ranks[t].expect("ranked") < *r);
1023        before[i] = at.checked_sub(1).map(|a| tails[a]);
1024        if at == tails.len() {
1025            tails.push(i);
1026        } else {
1027            tails[at] = i;
1028        }
1029    }
1030    let mut stay = vec![false; ranks.len()];
1031    let mut at = tails.last().copied();
1032    while let Some(i) = at {
1033        stay[i] = true;
1034        at = before[i];
1035    }
1036    stay
1037}
1038
1039#[cfg(test)]
1040mod tests;