extern crate alloc;
use alloc::vec::Vec;
use crate::kairos::{Clock, TickCounter};
use crate::metis::{Composer, DotSet, Extent, Locus, Rhapsody, Scholia, Scholion, Verge};
use super::super::support::dot;
use super::{Marks, mint};
use crate::metis::dot::RawDot;
fn clock(station: u32) -> Clock<TickCounter> {
Clock::with_default_config(TickCounter::new(), station).unwrap()
}
#[test]
fn test_a_shared_mark_converges_edits_and_retracts() {
let mut alice: Marks = Composer::new(1);
let mut bob: Marks = Composer::new(2);
let span = |tag| Scholion {
start: Verge::Origin,
end: Verge::Terminus,
tag,
};
let (mark, _delta) = mint(&mut alice, span("comment:open"));
let _ = bob.absorb(1, &alice.owed_to(bob.state().context()));
assert_eq!(bob.state().store().get(mark), Some(&span("comment:open")));
let mut superseded = DotSet::new();
assert!(superseded.insert(mark));
let (edited, delta) = bob.compose_super(
|dot| Scholia::singleton(dot, span("comment:resolved")),
|_| superseded.clone(),
);
let _ = alice.absorb(2, &delta);
for replica in [&alice, &bob] {
assert_eq!(replica.state().store().len(), 1);
assert_eq!(
replica.state().store().get(edited),
Some(&span("comment:resolved")),
);
}
let mut displaced = DotSet::new();
assert!(displaced.insert(edited));
let (_, from_alice) = alice.compose_super(
|dot| Scholia::singleton(dot, span("alice:wording")),
|_| displaced.clone(),
);
let (_, from_bob) = bob.compose_super(
|dot| Scholia::singleton(dot, span("bob:wording")),
|_| displaced.clone(),
);
let _ = alice.absorb(2, &from_bob);
let _ = bob.absorb(1, &from_alice);
assert_eq!(alice.state(), bob.state());
assert_eq!(
alice.state().store().marks().count(),
2,
"concurrent edits surface as siblings"
);
let retract = alice.retract(alice.state().store().observed());
let _ = bob.absorb(1, &retract);
assert!(alice.state().store().is_empty());
assert!(bob.state().store().is_empty());
}
#[test]
fn test_marks_ride_identity_through_concurrent_text_edits() {
let mut text_a: Composer<Rhapsody> = Composer::new(1);
let mut text_b: Composer<Rhapsody> = Composer::new(2);
let mut marks_a: Marks = Composer::new(1);
let mut marks_b: Marks = Composer::new(2);
let clk_a = clock(1);
let mut caret: Option<RawDot> = None;
let mut typed = Vec::new();
for _ in 0..3 {
let anchor = text_a.state().store().anchor_for_visual_insert(caret);
if let Some(top) = text_a.state().store().children_of(anchor).next()
&& let Some(locus) = text_a.state().store().locus(top)
{
clk_a.observe(locus.rank);
}
let rank = clk_a.now(0u16);
let (woven, _) = text_a.compose(|assigned| {
let mut delta = Rhapsody::new();
assert!(delta.weave(assigned, Locus { anchor, rank }));
(delta, DotSet::new())
});
typed.push(woven);
caret = Some(woven.into());
}
let (first, marked, last) = (typed[0], typed[1], typed[2]);
let scholion = Scholion {
start: Verge::After(first.into()),
end: Verge::Before(last.into()),
tag: "bold",
};
let (_, mark_delta) = mint(&mut marks_a, scholion);
let _ = text_b.absorb(1, &text_a.owed_to(text_b.state().context()));
let _ = marks_b.absorb(1, &mark_delta);
let clk_b = clock(2);
let anchor = text_b
.state()
.store()
.anchor_for_visual_insert(Some(marked.into()));
if let Some(top) = text_b.state().store().children_of(anchor).next()
&& let Some(locus) = text_b.state().store().locus(top)
{
clk_b.observe(locus.rank);
}
let rank_x = clk_b.now(0u16);
let (inside_insert, bob_delta) = text_b.compose(|dot| {
let mut delta = Rhapsody::new();
assert!(delta.weave(
dot,
Locus {
anchor,
rank: rank_x
}
));
(delta, DotSet::new())
});
let anchor = text_a.state().store().anchor_for_visual_insert(None);
if let Some(top) = text_a.state().store().children_of(anchor).next()
&& let Some(locus) = text_a.state().store().locus(top)
{
clk_a.observe(locus.rank);
}
let rank_y = clk_a.now(0u16);
let (outside_insert, alice_delta) = text_a.compose(|dot| {
let mut delta = Rhapsody::new();
assert!(delta.weave(
dot,
Locus {
anchor,
rank: rank_y
}
));
(delta, DotSet::new())
});
let _ = text_a.absorb(2, &bob_delta);
let _ = text_b.absorb(1, &alice_delta);
assert_eq!(text_a.state(), text_b.state());
assert_eq!(
text_a.state().store().order(),
[outside_insert, first, marked, inside_insert, last],
);
let held = marks_b.state().store().marks().next().unwrap();
for text in [&text_a, &text_b] {
assert_eq!(
text.state().store().extent(held.start, held.end),
Extent::Covered([marked, inside_insert].into()),
);
}
let mut gone = DotSet::new();
assert!(gone.insert(marked));
assert!(gone.insert(inside_insert));
let delta = text_a.retract(gone);
let _ = text_b.absorb(1, &delta);
assert_eq!(
text_b.state().store().extent(held.start, held.end),
Extent::Covered(Vec::new()),
);
}
#[test]
fn test_anchor_dots_reads_the_pin_set() {
let mut marks: Marks = Composer::new(1);
let (first, _) = mint(
&mut marks,
Scholion {
start: Verge::After(RawDot {
station: 7,
counter: 1,
}),
end: Verge::Before(RawDot {
station: 7,
counter: 4,
}),
tag: "comment",
},
);
let (_, _) = mint(
&mut marks,
Scholion {
start: Verge::Origin,
end: Verge::After(RawDot {
station: 8,
counter: 2,
}),
tag: "selection",
},
);
let pins = marks.state().store().anchor_dots();
let pinned: Vec<_> = pins.dots().collect();
assert_eq!(pinned, [dot(7, 1), dot(7, 4), dot(8, 2)]);
let mut superseded = DotSet::new();
assert!(superseded.insert(first));
let _ = marks.retract(superseded);
let pinned: Vec<_> = marks.state().store().anchor_dots().dots().collect();
assert_eq!(pinned, [dot(8, 2)]);
}