use std::{
collections::BTreeSet,
ops::{ControlFlow, Deref, DerefMut, Range, RangeBounds},
sync::Arc,
};
use generic_btree::{
rle::{CanRemove, HasLength, Mergeable, Sliceable, TryInsert},
BTree, BTreeTrait, ElemSlice, LengthFinder, UseLengthFinder,
};
use rustc_hash::FxHashMap;
use once_cell::sync::Lazy;
use crate::delta::StyleMeta;
use super::{AnchorType, StyleKey, StyleOp};
#[derive(Debug, Clone)]
pub(super) struct StyleRangeMap {
pub(super) tree: BTree<RangeNumMapTrait>,
has_style: bool,
}
#[derive(Debug, Clone)]
pub(super) struct RangeNumMapTrait;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub(crate) struct Styles {
pub(crate) styles: FxHashMap<StyleKey, StyleValue>,
}
impl Styles {
pub(crate) fn has_key_value(&self, key: &str, value: &loro_common::LoroValue) -> bool {
match self.get(&StyleKey::Key(key.into())) {
Some(v) => match v.get() {
Some(v) => &v.value == value,
_ => false,
},
_ => false,
}
}
fn infer_anchors(&self, next: &Self) -> (Option<Arc<StyleOp>>, Option<Arc<StyleOp>>) {
let mut left_anchor = None;
let mut right_anchor = None;
let empty_set: BTreeSet<_> = Default::default();
for (key, set) in self.styles.iter() {
let right_set = next.styles.get(key).map(|x| &x.set).unwrap_or(&empty_set);
for diff in set.set.difference(right_set) {
assert!(left_anchor.is_none(), "left anchor should be unique");
left_anchor = Some(diff.clone());
}
}
for (key, set) in next.styles.iter() {
let left_set = self.styles.get(key).map(|x| &x.set).unwrap_or(&empty_set);
for diff in set.set.difference(left_set) {
assert!(right_anchor.is_none(), "right anchor should be unique");
right_anchor = Some(diff.clone());
}
}
(left_anchor, right_anchor)
}
}
impl Deref for Styles {
type Target = FxHashMap<StyleKey, StyleValue>;
fn deref(&self) -> &Self::Target {
&self.styles
}
}
impl DerefMut for Styles {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.styles
}
}
pub(super) static EMPTY_STYLES: Lazy<Styles> = Lazy::new(Default::default);
#[derive(Debug, Clone)]
pub(crate) struct Elem {
pub(crate) styles: Styles,
pub(crate) len: usize,
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub(crate) struct StyleValue {
set: BTreeSet<Arc<StyleOp>>,
}
impl StyleValue {
pub fn insert(&mut self, value: Arc<StyleOp>) {
self.set.insert(value);
}
pub fn get(&self) -> Option<&Arc<StyleOp>> {
self.set.last()
}
}
impl Default for StyleRangeMap {
fn default() -> Self {
Self::new()
}
}
type YieldStyle<'a> = Option<&'a mut dyn FnMut(&Styles, usize)>;
impl StyleRangeMap {
pub fn new() -> Self {
let mut tree = BTree::new();
tree.push(Elem {
styles: Default::default(),
len: usize::MAX / 4,
});
Self {
tree,
has_style: false,
}
}
pub fn annotate(
&mut self,
range: Range<usize>,
style: Arc<StyleOp>,
mut yield_style: YieldStyle,
) {
let range = self.tree.range::<LengthFinder>(range);
if range.is_none() {
unreachable!();
}
self.has_style = true;
let range = range.unwrap();
self.tree
.update(range.start.cursor..range.end.cursor, &mut |x| {
if let Some(set) = x.styles.get_mut(&style.get_style_key()) {
set.set.insert(style.clone());
} else {
let key = style.get_style_key();
let mut value = StyleValue::default();
value.insert(style.clone());
x.styles.insert(key, value);
}
if let Some(y) = yield_style.as_mut() {
y(&x.styles, x.len);
}
None
});
}
pub(crate) fn get_styles_of_range(&self, range: Range<usize>) -> Option<&Styles> {
if !self.has_style {
return None;
}
let right = self
.tree
.query::<LengthFinder>(&(range.end - 1))
.unwrap()
.cursor;
let left = self
.tree
.query::<LengthFinder>(&range.start)
.unwrap()
.cursor;
if left.leaf == right.leaf {
Some(&self.tree.get_elem(left.leaf).unwrap().styles)
} else {
None
}
}
pub(crate) fn range_contains_key(&self, range: Range<usize>, key: &StyleKey) -> bool {
if range.is_empty() || !self.has_style {
return false;
}
let mut query = self.tree.query::<LengthFinder>(&range.start).unwrap();
let mut pos = range.start;
loop {
let elem = self.tree.get_elem(query.cursor.leaf).unwrap();
if elem.styles.contains_key(key) {
return true;
}
let remaining_in_elem = elem.len - query.cursor.offset;
let next_pos = pos + remaining_in_elem;
if next_pos >= range.end {
break;
}
match self.tree.next_elem(query.cursor) {
Some(next_cursor) => {
pos = next_pos;
query.cursor = next_cursor;
}
None => break,
}
}
false
}
pub fn insert(&mut self, pos: usize, len: usize) -> &Styles {
if !self.has_style {
return &EMPTY_STYLES;
}
if pos == 0 {
self.tree.prepend(Elem {
len,
styles: Default::default(),
});
return &EMPTY_STYLES;
}
if pos as isize == *self.tree.root_cache() {
self.tree.push(Elem {
len,
styles: Default::default(),
});
return &EMPTY_STYLES;
}
let right = self.tree.query::<LengthFinder>(&pos).unwrap().cursor;
let left = self.tree.query::<LengthFinder>(&(pos - 1)).unwrap().cursor;
if left.leaf == right.leaf {
self.tree.update_leaf(left.leaf, |x| {
x.len += len;
(true, None, None)
});
return &self.tree.get_elem(left.leaf).unwrap().styles;
}
let mut styles = self.tree.get_elem(left.leaf).unwrap().styles.clone();
let right_styles = &self.tree.get_elem(right.leaf).unwrap().styles;
styles.retain(|key, value| {
if let Some(right_value) = right_styles.get(key) {
value.set.retain(|x| right_value.set.contains(x));
return !value.set.is_empty();
}
false
});
let (target, _) = self.tree.insert_by_path(right, Elem { len, styles });
&self.tree.get_elem(target.leaf).unwrap().styles
}
pub fn get_styles_for_insert(&self, index: usize) -> StyleMeta {
if index == 0 || !self.has_style {
return StyleMeta::default();
}
let left = self
.tree
.query::<LengthFinder>(&(index - 1))
.unwrap()
.cursor;
let right = self.tree.shift_path_by_one_offset(left).unwrap();
if left.leaf == right.leaf {
let styles = &self.tree.get_elem(left.leaf).unwrap().styles;
styles.into()
} else {
let mut styles = self.tree.get_elem(left.leaf).unwrap().styles.clone();
let right_styles = &self.tree.get_elem(right.leaf).unwrap().styles;
styles.retain(|key, value| {
if let Some(right_value) = right_styles.get(key) {
value.set.retain(|x| right_value.set.contains(x));
return !value.set.is_empty();
}
false
});
styles.into()
}
}
pub fn iter(&self) -> impl Iterator<Item = (Range<usize>, &Styles)> + '_ {
let mut index = 0;
self.tree.iter().filter_map(move |elem| {
let len = elem.len;
let value = &elem.styles;
let range = index..index + len;
index += len;
if elem.styles.is_empty() {
return None;
}
Some((range, value))
})
}
fn update_styles_scanning_backward(
&mut self,
pos: usize,
mut f: impl FnMut(&mut Elem) -> ControlFlow<()>,
) {
let mut cursor = self.tree.query::<LengthFinder>(&pos).map(|x| x.cursor);
while let Some(inner_cursor) = cursor {
cursor = self.tree.prev_elem(inner_cursor);
let node = self.tree.get_elem_mut(inner_cursor.leaf).unwrap();
match f(node) {
ControlFlow::Continue(_) => {}
ControlFlow::Break(_) => {
break;
}
}
}
}
pub(crate) fn iter_range(
&self,
range: impl RangeBounds<usize>,
) -> impl Iterator<Item = ElemSlice<'_, Elem>> + '_ {
let start = match range.start_bound() {
std::ops::Bound::Included(x) => *x,
std::ops::Bound::Excluded(x) => *x + 1,
std::ops::Bound::Unbounded => 0,
};
let end = match range.end_bound() {
std::ops::Bound::Included(x) => *x + 1,
std::ops::Bound::Excluded(x) => *x,
std::ops::Bound::Unbounded => usize::MAX,
};
let start = self.tree.query::<LengthFinder>(&start).unwrap();
let end = self.tree.query::<LengthFinder>(&end).unwrap();
self.tree.iter_range(start.cursor..end.cursor)
}
pub(super) fn iter_anchors(&self) -> impl Iterator<Item = IterAnchorItem> + '_ {
let mut index = 0;
let empty_styles = &EMPTY_STYLES;
let mut last: Option<&Elem> = None;
let mut vec = Vec::new();
for cur in self.tree.iter() {
let last_styles = last.map(|x| &x.styles).unwrap_or(empty_styles);
let (left_anchor, right_anchor) = last_styles.infer_anchors(&cur.styles);
if let Some(left) = left_anchor {
vec.push(IterAnchorItem {
index: index - 1,
op: left.clone(),
anchor_type: AnchorType::End,
});
}
if let Some(right) = right_anchor {
vec.push(IterAnchorItem {
index,
op: right.clone(),
anchor_type: AnchorType::Start,
});
}
last = Some(cur);
index += cur.len;
}
let last_styles = last.map(|x| &x.styles).unwrap_or(empty_styles);
let (left_anchor, right_anchor) = last_styles.infer_anchors(empty_styles);
if let Some(left) = left_anchor {
vec.push(IterAnchorItem {
index: index - 1,
op: left.clone(),
anchor_type: AnchorType::End,
});
}
if let Some(right) = right_anchor {
vec.push(IterAnchorItem {
index,
op: right.clone(),
anchor_type: AnchorType::Start,
});
}
vec.into_iter()
}
pub fn remove_style_scanning_backward(
&mut self,
to_remove: &Arc<StyleOp>,
last_index: usize,
) -> usize {
let mut removed_len = 0;
self.update_styles_scanning_backward(last_index, |elem| {
removed_len += elem.len;
let styles = &mut elem.styles;
let key = to_remove.get_style_key();
let mut has_removed = false;
if let Some(value) = styles.get_mut(&key) {
has_removed = value.set.remove(to_remove);
if value.set.is_empty() {
styles.remove(&key);
}
}
if has_removed {
ControlFlow::Continue(())
} else {
ControlFlow::Break(())
}
});
last_index + 1 - removed_len
}
pub fn delete(&mut self, range: Range<usize>) {
if !self.has_style {
return;
}
let start = self.tree.query::<LengthFinder>(&range.start).unwrap();
let end = self.tree.query::<LengthFinder>(&range.end).unwrap();
if start.cursor.leaf == end.cursor.leaf {
self.tree.update_leaf(start.cursor.leaf, |x| {
x.len -= range.len();
(true, None, None)
});
return;
}
self.tree.drain(start..end);
}
pub(crate) fn has_style(&self) -> bool {
self.has_style
}
}
pub(super) struct IterAnchorItem {
pub(super) index: usize,
pub(super) op: Arc<StyleOp>,
pub(super) anchor_type: AnchorType,
}
impl UseLengthFinder<RangeNumMapTrait> for RangeNumMapTrait {
fn get_len(cache: &isize) -> usize {
*cache as usize
}
}
impl HasLength for Elem {
fn rle_len(&self) -> usize {
self.len
}
}
impl Mergeable for Elem {
fn can_merge(&self, rhs: &Self) -> bool {
self.styles == rhs.styles || rhs.len == 0
}
fn merge_right(&mut self, rhs: &Self) {
self.len += rhs.len
}
fn merge_left(&mut self, left: &Self) {
self.len += left.len;
}
}
impl Sliceable for Elem {
fn _slice(&self, range: std::ops::Range<usize>) -> Self {
let len = range.len();
Elem {
styles: self.styles.clone(),
len,
}
}
}
impl TryInsert for Elem {
fn try_insert(&mut self, _pos: usize, elem: Self) -> Result<(), Self>
where
Self: Sized,
{
if self.styles == elem.styles {
self.len += elem.len;
Ok(())
} else {
Err(elem)
}
}
}
impl CanRemove for Elem {
fn can_remove(&self) -> bool {
self.len == 0
}
}
impl BTreeTrait for RangeNumMapTrait {
type Elem = Elem;
type Cache = isize;
type CacheDiff = isize;
const USE_DIFF: bool = true;
fn calc_cache_internal(
cache: &mut Self::Cache,
caches: &[generic_btree::Child<Self>],
) -> isize {
let new_cache = caches.iter().map(|c| c.cache).sum();
let diff = new_cache - *cache;
*cache = new_cache;
diff
}
fn merge_cache_diff(diff1: &mut Self::CacheDiff, diff2: &Self::CacheDiff) {
*diff1 += diff2;
}
fn apply_cache_diff(cache: &mut Self::Cache, diff: &Self::CacheDiff) {
*cache += diff;
}
fn get_elem_cache(elem: &Self::Elem) -> Self::Cache {
elem.len as isize
}
fn new_cache_to_diff(cache: &Self::Cache) -> Self::CacheDiff {
*cache
}
fn sub_cache(cache_lhs: &Self::Cache, cache_rhs: &Self::Cache) -> Self::CacheDiff {
*cache_lhs - *cache_rhs
}
}
#[cfg(test)]
mod test {
use loro_common::PeerID;
use crate::{change::Lamport, container::richtext::TextStyleInfoFlag};
use super::*;
fn new_style(n: i32) -> Arc<StyleOp> {
Arc::new(StyleOp {
lamport: n as Lamport,
peer: n as PeerID,
cnt: n,
key: n.to_string().into(),
info: TextStyleInfoFlag::default(),
value: loro_common::LoroValue::Bool(true),
})
}
#[test]
fn test_basic_insert() {
let mut map = StyleRangeMap::default();
map.annotate(1..10, new_style(1), None);
{
map.insert(0, 1);
assert_eq!(map.iter().count(), 1);
for (range, map) in map.iter() {
assert_eq!(range, 2..11);
assert_eq!(map.len(), 1);
}
}
{
map.insert(11, 1);
assert_eq!(map.iter().count(), 1);
for (range, map) in map.iter() {
assert_eq!(range, 2..11);
assert_eq!(map.len(), 1);
}
}
{
map.insert(10, 1);
assert_eq!(map.iter().count(), 1);
for (range, map) in map.iter() {
assert_eq!(range, 2..12);
assert_eq!(map.len(), 1);
}
}
}
#[test]
fn delete_style() {
let mut map = StyleRangeMap::default();
map.annotate(1..10, new_style(1), None);
{
map.delete(0..2);
assert_eq!(map.iter().count(), 1);
for (range, map) in map.iter() {
assert_eq!(range, 0..8);
assert_eq!(map.len(), 1);
}
}
{
map.delete(2..4);
for (range, map) in map.iter() {
assert_eq!(range, 0..6);
assert_eq!(map.len(), 1);
}
assert_eq!(map.iter().count(), 1);
}
{
map.delete(6..8);
assert_eq!(map.iter().count(), 1);
for (range, map) in map.iter() {
assert_eq!(range, 0..6);
assert_eq!(map.len(), 1);
}
}
}
}