Skip to main content

sim_lib_view/
set_lens.rs

1//! `intent/set-lens` handling and the per-pane active-lens state.
2//!
3//! Switching the active lens for a pane is data, not a rebuild: the active lens
4//! per pane is a small SIM value (a map of pane id to lens id). Applying an
5//! `intent/set-lens` returns an updated state value and never touches the value
6//! being shown, so the choice persists in the workspace value (P6) by virtue of
7//! being a value itself.
8
9use sim_kernel::{Error, Expr, Result, Symbol};
10use sim_lib_intent::{field, intent_kind_of};
11
12/// An empty pane-lens state.
13pub fn empty_pane_lenses() -> Expr {
14    Expr::Map(Vec::new())
15}
16
17/// The active lens for `pane`, if one has been chosen.
18pub fn active_lens(state: &Expr, pane: &str) -> Option<Symbol> {
19    let Expr::Map(entries) = state else {
20        return None;
21    };
22    entries.iter().find_map(|(key, value)| {
23        let matches = matches!(key, Expr::Symbol(symbol) if &*symbol.name == pane);
24        match value {
25            Expr::Symbol(lens) if matches => Some(lens.clone()),
26            _ => None,
27        }
28    })
29}
30
31/// Apply an `intent/set-lens`, returning the updated pane-lens state. The shown
32/// value is never read or written here; only the pane's active lens changes.
33pub fn apply_set_lens(state: &Expr, intent: &Expr) -> Result<Expr> {
34    match intent_kind_of(intent) {
35        Some(kind) if &*kind.name == "set-lens" => {}
36        _ => {
37            return Err(Error::HostError(
38                "apply_set_lens expects an intent/set-lens".to_owned(),
39            ));
40        }
41    }
42    let pane = match field(intent, "pane") {
43        Some(Expr::Symbol(symbol)) => symbol.clone(),
44        _ => {
45            return Err(Error::HostError(
46                "intent/set-lens 'pane' must be a symbol".to_owned(),
47            ));
48        }
49    };
50    let lens = match field(intent, "lens") {
51        Some(Expr::Symbol(symbol)) => symbol.clone(),
52        _ => {
53            return Err(Error::HostError(
54                "intent/set-lens 'lens' must be a symbol".to_owned(),
55            ));
56        }
57    };
58    let mut entries = match state {
59        Expr::Map(entries) => entries.clone(),
60        _ => Vec::new(),
61    };
62    let key_matches = |key: &Expr| matches!(key, Expr::Symbol(symbol) if symbol == &pane);
63    if let Some(slot) = entries.iter_mut().find(|(key, _)| key_matches(key)) {
64        slot.1 = Expr::Symbol(lens);
65    } else {
66        entries.push((Expr::Symbol(pane), Expr::Symbol(lens)));
67    }
68    Ok(Expr::Map(entries))
69}