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