use crate::model::{Cursor, SeqId, Selection, Tree};
pub fn extend(tree: &Tree, sel: Selection, right: bool) -> Selection {
if right {
if sel.focus < tree.len(sel.seq) {
Selection {
seq: sel.seq,
anchor: sel.anchor,
focus: sel.focus + 1,
}
} else if let Some(parent) = tree.seq_parent(sel.seq) {
if let Some((gseq, gi)) = tree.index_in_parent(parent) {
Selection {
seq: gseq,
anchor: gi,
focus: gi + 1,
}
} else {
sel
}
} else {
sel }
} else if sel.focus > 0 {
Selection {
seq: sel.seq,
anchor: sel.anchor,
focus: sel.focus - 1,
}
} else if let Some(parent) = tree.seq_parent(sel.seq) {
if let Some((gseq, gi)) = tree.index_in_parent(parent) {
Selection {
seq: gseq,
anchor: gi + 1,
focus: gi,
}
} else {
sel
}
} else {
sel
}
}
pub fn extend_to(tree: &Tree, anchor: Cursor, target: Cursor) -> Selection {
if anchor.seq == target.seq {
return Selection {
seq: anchor.seq,
anchor: anchor.index,
focus: target.index,
};
}
fn seq_chain(tree: &Tree, leaf: SeqId) -> Vec<SeqId> {
let mut chain = vec![leaf];
let mut seq = leaf;
while let Some(node) = tree.seq_parent(seq) {
let Some((pseq, _)) = tree.index_in_parent(node) else {
break;
};
chain.push(pseq);
seq = pseq;
}
chain.reverse();
chain
}
let chain_a = seq_chain(tree, anchor.seq);
let chain_b = seq_chain(tree, target.seq);
let common_depth = chain_a.iter().zip(&chain_b).take_while(|(a, b)| a == b).count();
let common_seq = chain_a[common_depth - 1];
let side = |cur: Cursor, chain: &[SeqId]| -> (usize, usize) {
if cur.seq == common_seq {
(cur.index, cur.index)
} else {
let next = chain[common_depth];
let node = tree.seq_parent(next).expect("non-root seq has a parent node");
let (_, idx) = tree.index_in_parent(node).expect("node is placed in its parent seq");
(idx, idx + 1)
}
};
let (lo_a, hi_a) = side(anchor, &chain_a);
let (lo_b, hi_b) = side(target, &chain_b);
let lo = lo_a.min(lo_b);
let hi = hi_a.max(hi_b);
let (anchor_idx, focus_idx) = if lo_a <= lo_b { (lo, hi) } else { (hi, lo) };
Selection {
seq: common_seq,
anchor: anchor_idx,
focus: focus_idx,
}
}
pub fn select_all(tree: &Tree) -> Selection {
let root = tree.root();
Selection {
seq: root,
anchor: 0,
focus: tree.len(root),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{FracStyle, Kind, MathClass, ScriptSlot, Symbol};
fn atom(c: &str) -> Symbol {
Symbol { latex: c.into(), class: MathClass::Ord }
}
fn x_squared_plus_2(t: &mut Tree) -> (SeqId, SeqId) {
let root = t.root();
t.insert_atom(Cursor { seq: root, index: 0 }, atom("x"));
let c = t.attach_script(Cursor { seq: root, index: 1 }, ScriptSlot::Sup);
t.insert_atom(c, atom("2"));
t.insert_atom(Cursor { seq: root, index: 1 }, atom("+"));
t.insert_atom(Cursor { seq: root, index: 2 }, atom("2"));
let script = t.items(root)[0];
let base = match t.kind(script) {
Some(Kind::Script { base, .. }) => *base,
_ => panic!("expected a Script"),
};
(root, base)
}
#[test]
fn extend_to_same_seq_is_literal() {
let mut t = Tree::new();
let root = t.root();
t.insert_atom(Cursor { seq: root, index: 0 }, atom("a"));
t.insert_atom(Cursor { seq: root, index: 1 }, atom("b"));
let sel = extend_to(&t, Cursor { seq: root, index: 0 }, Cursor { seq: root, index: 2 });
assert_eq!((sel.seq, sel.anchor, sel.focus), (root, 0, 2));
}
#[test]
fn extend_to_from_inside_a_script_base_past_the_end_selects_everything() {
let mut t = Tree::new();
let (root, base) = x_squared_plus_2(&mut t);
let end = t.len(root);
let sel = extend_to(&t, Cursor { seq: base, index: 1 }, Cursor { seq: root, index: end });
assert_eq!(sel.seq, root);
assert_eq!((sel.anchor.min(sel.focus), sel.anchor.max(sel.focus)), (0, end));
assert_eq!(sel.focus, end);
}
#[test]
fn extend_to_reversed_direction_keeps_the_true_anchor_fixed() {
let mut t = Tree::new();
let (root, base) = x_squared_plus_2(&mut t);
let end = t.len(root);
let sel = extend_to(&t, Cursor { seq: root, index: end }, Cursor { seq: base, index: 1 });
assert_eq!(sel.seq, root);
assert_eq!(sel.anchor, end); assert_eq!(sel.focus, 0); }
#[test]
fn extend_to_between_two_sibling_structures() {
let mut t = Tree::new();
let root = t.root();
let c = t.insert_fraction(Cursor { seq: root, index: 0 }, FracStyle::Bar, None);
let num_a = c.seq;
let fa = t.items(root)[0];
let den_a = t.child_seqs(fa)[1];
let c = t.insert_fraction(Cursor { seq: root, index: 1 }, FracStyle::Bar, None);
let num_b = c.seq;
let _ = (num_a, den_a);
let sel = extend_to(&t, Cursor { seq: den_a, index: 0 }, Cursor { seq: num_b, index: 0 });
assert_eq!((sel.seq, sel.anchor.min(sel.focus), sel.anchor.max(sel.focus)), (root, 0, 2));
}
}