use crate::model::{Cursor, SeqId, SeqRange, Tree};
pub(crate) fn extend(tree: &Tree, sel: SeqRange, right: bool) -> SeqRange {
let len = tree.len(sel.seq);
if right && sel.focus < len {
return SeqRange { focus: sel.focus + 1, ..sel };
}
if !right && sel.focus > 0 {
return SeqRange { focus: sel.focus - 1, ..sel };
}
match tree.before_parent(sel.seq) {
Some(p) if right => SeqRange { seq: p.seq, anchor: p.index, focus: p.index + 1 },
Some(p) => SeqRange { seq: p.seq, anchor: p.index + 1, focus: p.index },
None => sel,
}
}
pub(crate) fn extend_to(tree: &Tree, anchor: Cursor, target: Cursor) -> SeqRange {
if anchor.seq == target.seq {
return SeqRange { seq: anchor.seq, anchor: anchor.index, focus: target.index };
}
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().max(1);
let common = chain_a[common_depth - 1];
let side = |cur: Cursor, chain: &[SeqId]| -> (usize, usize) {
if cur.seq == common {
return (cur.index, cur.index);
}
match chain.get(common_depth).and_then(|&s| tree.before_parent(s)) {
Some(p) => (p.index, p.index + 1),
None => (0, 0),
}
};
let (lo_a, hi_a) = side(anchor, &chain_a);
let (lo_b, hi_b) = side(target, &chain_b);
let (lo, hi) = (lo_a.min(lo_b), hi_a.max(hi_b));
let target_left = lo_b + hi_b < lo_a + hi_a;
let (anchor_idx, focus_idx) = if target_left { (hi, lo) } else { (lo, hi) };
SeqRange { seq: common, anchor: anchor_idx, focus: focus_idx }
}
fn seq_chain(tree: &Tree, leaf: SeqId) -> Vec<SeqId> {
let mut chain = vec![leaf];
let mut seq = leaf;
while let Some(p) = tree.before_parent(seq) {
chain.push(p.seq);
seq = p.seq;
}
chain.reverse();
chain
}
pub(crate) fn select_all(tree: &Tree) -> SeqRange {
let root = tree.root();
SeqRange { seq: root, anchor: 0, focus: tree.len(root) }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::{FracStyle, 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 }, None, atom("x")).unwrap();
let c = t.attach_script(Cursor { seq: root, index: 1 }, ScriptSlot::Sup, None).unwrap();
t.insert_atom(c, None, atom("2")).unwrap();
t.insert_atom(Cursor { seq: root, index: 1 }, None, atom("+")).unwrap();
t.insert_atom(Cursor { seq: root, index: 2 }, None, atom("2")).unwrap();
let base = t.child_seqs(t.items(root)[0])[0];
(root, base)
}
#[test]
fn same_seq_drag_is_literal() {
let mut t = Tree::new();
let (root, _) = x_squared_plus_2(&mut t);
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 drag_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, sel.anchor, sel.focus), (root, 0, end));
}
#[test]
fn reverse_drag_keeps_the_true_anchor() {
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, sel.anchor, sel.focus), (root, end, 0));
}
#[test]
fn tie_puts_the_focus_on_the_target_side() {
let mut t = Tree::new();
let (root, base) = x_squared_plus_2(&mut t);
let sel = extend_to(&t, Cursor { seq: base, index: 1 }, Cursor { seq: root, index: 0 });
assert_eq!((sel.seq, sel.anchor, sel.focus), (root, 1, 0));
let sel = extend_to(&t, Cursor { seq: base, index: 1 }, Cursor { seq: root, index: 1 });
assert_eq!((sel.seq, sel.anchor, sel.focus), (root, 0, 1));
}
#[test]
fn drag_between_sibling_structures_covers_both() {
let mut t = Tree::new();
let root = t.root();
t.insert_fraction(Cursor { seq: root, index: 0 }, FracStyle::Bar, None).unwrap();
let num_b = t.insert_fraction(Cursor { seq: root, index: 1 }, FracStyle::Bar, None).unwrap();
let den_a = t.child_seqs(t.items(root)[0])[1];
let sel = extend_to(&t, Cursor { seq: den_a, index: 0 }, num_b);
assert_eq!((sel.seq, sel.anchor, sel.focus), (root, 0, 2));
}
}