Merge branch 'master' into new-file
This commit is contained in:
+174
-120
@@ -393,39 +393,48 @@ impl Buffer {
|
||||
|
||||
insertion_splits.insert(
|
||||
base_insertion.id,
|
||||
SumTree::from_item(InsertionSplit {
|
||||
fragment_id: FragmentId::min_value().clone(),
|
||||
extent: 0,
|
||||
}),
|
||||
SumTree::from_item(
|
||||
InsertionSplit {
|
||||
fragment_id: FragmentId::min_value().clone(),
|
||||
extent: 0,
|
||||
},
|
||||
&(),
|
||||
),
|
||||
);
|
||||
fragments.push(
|
||||
Fragment {
|
||||
id: FragmentId::min_value().clone(),
|
||||
insertion: base_insertion.clone(),
|
||||
text: base_insertion.text.slice(0..0),
|
||||
deletions: Default::default(),
|
||||
max_undos: Default::default(),
|
||||
visible: true,
|
||||
},
|
||||
&(),
|
||||
);
|
||||
fragments.push(Fragment {
|
||||
id: FragmentId::min_value().clone(),
|
||||
insertion: base_insertion.clone(),
|
||||
text: base_insertion.text.slice(0..0),
|
||||
deletions: Default::default(),
|
||||
max_undos: Default::default(),
|
||||
visible: true,
|
||||
});
|
||||
|
||||
if base_insertion.text.len() > 0 {
|
||||
let base_fragment_id =
|
||||
FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value());
|
||||
|
||||
insertion_splits
|
||||
.get_mut(&base_insertion.id)
|
||||
.unwrap()
|
||||
.push(InsertionSplit {
|
||||
insertion_splits.get_mut(&base_insertion.id).unwrap().push(
|
||||
InsertionSplit {
|
||||
fragment_id: base_fragment_id.clone(),
|
||||
extent: base_insertion.text.len(),
|
||||
});
|
||||
fragments.push(Fragment {
|
||||
id: base_fragment_id,
|
||||
text: base_insertion.text.clone(),
|
||||
insertion: base_insertion,
|
||||
deletions: Default::default(),
|
||||
max_undos: Default::default(),
|
||||
visible: true,
|
||||
});
|
||||
},
|
||||
&(),
|
||||
);
|
||||
fragments.push(
|
||||
Fragment {
|
||||
id: base_fragment_id,
|
||||
text: base_insertion.text.clone(),
|
||||
insertion: base_insertion,
|
||||
deletions: Default::default(),
|
||||
max_undos: Default::default(),
|
||||
visible: true,
|
||||
},
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
Self {
|
||||
@@ -507,22 +516,22 @@ impl Buffer {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self.fragments.cursor::<usize, usize>();
|
||||
cursor.seek(&range.start, SeekBias::Right);
|
||||
cursor.seek(&range.start, SeekBias::Right, &());
|
||||
|
||||
if let Some(fragment) = cursor.item() {
|
||||
let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
|
||||
let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
|
||||
summary += &fragment.text.slice(summary_start..summary_end).summary();
|
||||
summary += fragment.text.slice(summary_start..summary_end).summary();
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
if range.end > *cursor.start() {
|
||||
summary += &cursor.summary::<TextSummary>(&range.end, SeekBias::Right);
|
||||
summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
|
||||
|
||||
if let Some(fragment) = cursor.item() {
|
||||
let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
|
||||
let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
|
||||
summary += &fragment.text.slice(summary_start..summary_end).summary();
|
||||
summary += fragment.text.slice(summary_start..summary_end).summary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,22 +561,22 @@ impl Buffer {
|
||||
let mut summary = TextSummary::default();
|
||||
|
||||
let mut cursor = self.fragments.cursor::<usize, usize>();
|
||||
cursor.seek(&range.start, SeekBias::Right);
|
||||
cursor.seek(&range.start, SeekBias::Right, &());
|
||||
|
||||
if let Some(fragment) = cursor.item() {
|
||||
let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
|
||||
let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
|
||||
summary += &fragment.text.slice(summary_start..summary_end).summary();
|
||||
summary += fragment.text.slice(summary_start..summary_end).summary();
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
if range.end > *cursor.start() {
|
||||
summary += &cursor.summary::<TextSummary>(&range.end, SeekBias::Right);
|
||||
summary += cursor.summary::<TextSummary>(&range.end, SeekBias::Right, &());
|
||||
|
||||
if let Some(fragment) = cursor.item() {
|
||||
let summary_start = cmp::max(*cursor.start(), range.start) - cursor.start();
|
||||
let summary_end = cmp::min(range.end - cursor.start(), fragment.len());
|
||||
summary += &fragment.text.slice(summary_start..summary_end).summary();
|
||||
summary += fragment.text.slice(summary_start..summary_end).summary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,10 +979,10 @@ impl Buffer {
|
||||
|
||||
let mut cursor = old_fragments.cursor::<FragmentIdRef, ()>();
|
||||
let mut new_fragments =
|
||||
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left);
|
||||
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
|
||||
|
||||
if start_offset == cursor.item().unwrap().end_offset() {
|
||||
new_fragments.push(cursor.item().unwrap().clone());
|
||||
new_fragments.push(cursor.item().unwrap().clone(), &());
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
@@ -1012,30 +1021,33 @@ impl Buffer {
|
||||
None
|
||||
};
|
||||
if let Some(fragment) = before_range {
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(fragment) = insertion {
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(mut fragment) = within_range {
|
||||
if fragment.was_visible(&version_in_range, &self.undo_map) {
|
||||
fragment.deletions.insert(local_timestamp);
|
||||
fragment.visible = false;
|
||||
}
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(fragment) = after_range {
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
} else {
|
||||
if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
|
||||
new_fragments.push(self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
Some(&fragment),
|
||||
new_text.take().unwrap(),
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
));
|
||||
new_fragments.push(
|
||||
self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
Some(&fragment),
|
||||
new_text.take().unwrap(),
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
if fragment.id < end_fragment_id
|
||||
@@ -1044,23 +1056,26 @@ impl Buffer {
|
||||
fragment.deletions.insert(local_timestamp);
|
||||
fragment.visible = false;
|
||||
}
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
if let Some(new_text) = new_text {
|
||||
new_fragments.push(self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
None,
|
||||
new_text,
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
));
|
||||
new_fragments.push(
|
||||
self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
None,
|
||||
new_text,
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right));
|
||||
new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
|
||||
self.fragments = new_fragments;
|
||||
self.local_clock.observe(local_timestamp);
|
||||
self.lamport_clock.observe(lamport_timestamp);
|
||||
@@ -1148,23 +1163,26 @@ impl Buffer {
|
||||
let mut insertion_splits = splits.cursor::<(), ()>().map(|s| &s.fragment_id).peekable();
|
||||
|
||||
let first_split_id = insertion_splits.next().unwrap();
|
||||
new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left);
|
||||
new_fragments = cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &());
|
||||
|
||||
loop {
|
||||
let mut fragment = cursor.item().unwrap().clone();
|
||||
fragment.visible = fragment.is_visible(&self.undo_map);
|
||||
fragment.max_undos.observe(undo.id);
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
if let Some(split_id) = insertion_splits.next() {
|
||||
new_fragments
|
||||
.push_tree(cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left));
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()),
|
||||
&(),
|
||||
);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_fragments = cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left);
|
||||
new_fragments =
|
||||
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
|
||||
while let Some(fragment) = cursor.item() {
|
||||
if fragment.id > end_fragment_id {
|
||||
break;
|
||||
@@ -1176,13 +1194,13 @@ impl Buffer {
|
||||
fragment.visible = fragment.is_visible(&self.undo_map);
|
||||
fragment.max_undos.observe(undo.id);
|
||||
}
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_fragments.push_tree(cursor.suffix());
|
||||
new_fragments.push_tree(cursor.suffix(&()), &());
|
||||
drop(cursor);
|
||||
self.fragments = new_fragments;
|
||||
|
||||
@@ -1246,7 +1264,7 @@ impl Buffer {
|
||||
.get(&edit_id)
|
||||
.ok_or_else(|| anyhow!("invalid operation"))?;
|
||||
let mut cursor = split_tree.cursor::<usize, ()>();
|
||||
cursor.seek(&offset, SeekBias::Left);
|
||||
cursor.seek(&offset, SeekBias::Left, &());
|
||||
Ok(cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("invalid operation"))?
|
||||
@@ -1268,7 +1286,10 @@ impl Buffer {
|
||||
let old_fragments = self.fragments.clone();
|
||||
let mut cursor = old_fragments.cursor::<usize, usize>();
|
||||
let mut new_fragments = SumTree::new();
|
||||
new_fragments.push_tree(cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right));
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
|
||||
let mut start_id = None;
|
||||
let mut start_offset = None;
|
||||
@@ -1290,7 +1311,8 @@ impl Buffer {
|
||||
.remove(&fragment.insertion.id)
|
||||
.unwrap();
|
||||
let mut splits_cursor = old_split_tree.cursor::<usize, ()>();
|
||||
let mut new_split_tree = splits_cursor.slice(&fragment.start_offset(), SeekBias::Right);
|
||||
let mut new_split_tree =
|
||||
splits_cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
|
||||
|
||||
// Find all splices that start or end within the current fragment. Then, split the
|
||||
// fragment and reassemble it in both trees accounting for the deleted and the newly
|
||||
@@ -1303,11 +1325,14 @@ impl Buffer {
|
||||
prefix.id =
|
||||
FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
|
||||
fragment.set_start_offset(prefix.end_offset());
|
||||
new_fragments.push(prefix.clone());
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: prefix.end_offset() - prefix.start_offset(),
|
||||
fragment_id: prefix.id,
|
||||
});
|
||||
new_fragments.push(prefix.clone(), &());
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: prefix.end_offset() - prefix.start_offset(),
|
||||
fragment_id: prefix.id,
|
||||
},
|
||||
&(),
|
||||
);
|
||||
fragment_start = range.start;
|
||||
}
|
||||
|
||||
@@ -1331,7 +1356,7 @@ impl Buffer {
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_fragments.push(new_fragment);
|
||||
new_fragments.push(new_fragment, &());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1347,11 +1372,14 @@ impl Buffer {
|
||||
prefix.visible = false;
|
||||
}
|
||||
fragment.set_start_offset(prefix.end_offset());
|
||||
new_fragments.push(prefix.clone());
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: prefix.end_offset() - prefix.start_offset(),
|
||||
fragment_id: prefix.id,
|
||||
});
|
||||
new_fragments.push(prefix.clone(), &());
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: prefix.end_offset() - prefix.start_offset(),
|
||||
fragment_id: prefix.id,
|
||||
},
|
||||
&(),
|
||||
);
|
||||
fragment_start = range.end;
|
||||
end_id = Some(fragment.insertion.id);
|
||||
end_offset = Some(fragment.start_offset());
|
||||
@@ -1395,16 +1423,21 @@ impl Buffer {
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: fragment.end_offset() - fragment.start_offset(),
|
||||
fragment_id: fragment.id.clone(),
|
||||
});
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: fragment.end_offset() - fragment.start_offset(),
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
);
|
||||
splits_cursor.next();
|
||||
new_split_tree
|
||||
.push_tree(splits_cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right));
|
||||
new_split_tree.push_tree(
|
||||
splits_cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
self.insertion_splits
|
||||
.insert(fragment.insertion.id, new_split_tree);
|
||||
new_fragments.push(fragment);
|
||||
new_fragments.push(fragment, &());
|
||||
|
||||
// Scan forward until we find a fragment that is not fully contained by the current splice.
|
||||
cursor.next();
|
||||
@@ -1420,7 +1453,7 @@ impl Buffer {
|
||||
new_fragment.deletions.insert(local_timestamp);
|
||||
new_fragment.visible = false;
|
||||
}
|
||||
new_fragments.push(new_fragment);
|
||||
new_fragments.push(new_fragment, &());
|
||||
cursor.next();
|
||||
|
||||
if range.end == fragment_end {
|
||||
@@ -1462,7 +1495,8 @@ impl Buffer {
|
||||
// and push all the fragments in between into the new tree.
|
||||
if cur_range.as_ref().map_or(false, |r| r.start > fragment_end) {
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right),
|
||||
cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1494,11 +1528,13 @@ impl Buffer {
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_fragments.push(new_fragment);
|
||||
new_fragments.push(new_fragment, &());
|
||||
}
|
||||
} else {
|
||||
new_fragments
|
||||
.push_tree(cursor.slice(&old_fragments.extent::<usize>(), SeekBias::Right));
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&old_fragments.extent::<usize>(), SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
self.fragments = new_fragments;
|
||||
@@ -1556,32 +1592,43 @@ impl Buffer {
|
||||
.remove(&fragment.insertion.id)
|
||||
.unwrap();
|
||||
let mut cursor = old_split_tree.cursor::<usize, ()>();
|
||||
let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right);
|
||||
let mut new_split_tree = cursor.slice(&fragment.start_offset(), SeekBias::Right, &());
|
||||
|
||||
if let Some(ref fragment) = before_range {
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: range.start - fragment.start_offset(),
|
||||
fragment_id: fragment.id.clone(),
|
||||
});
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: range.start - fragment.start_offset(),
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(ref fragment) = within_range {
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: range.end - range.start,
|
||||
fragment_id: fragment.id.clone(),
|
||||
});
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: range.end - range.start,
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(ref fragment) = after_range {
|
||||
new_split_tree.push(InsertionSplit {
|
||||
extent: fragment.end_offset() - range.end,
|
||||
fragment_id: fragment.id.clone(),
|
||||
});
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: fragment.end_offset() - range.end,
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
cursor.next();
|
||||
new_split_tree
|
||||
.push_tree(cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right));
|
||||
new_split_tree.push_tree(
|
||||
cursor.slice(&old_split_tree.extent::<usize>(), SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
|
||||
self.insertion_splits
|
||||
.insert(fragment.insertion.id, new_split_tree);
|
||||
@@ -1606,10 +1653,13 @@ impl Buffer {
|
||||
);
|
||||
|
||||
let mut split_tree = SumTree::new();
|
||||
split_tree.push(InsertionSplit {
|
||||
extent: text.len(),
|
||||
fragment_id: new_fragment_id.clone(),
|
||||
});
|
||||
split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: text.len(),
|
||||
fragment_id: new_fragment_id.clone(),
|
||||
},
|
||||
&(),
|
||||
);
|
||||
self.insertion_splits.insert(local_timestamp, split_tree);
|
||||
|
||||
Fragment::new(
|
||||
@@ -1658,7 +1708,7 @@ impl Buffer {
|
||||
};
|
||||
|
||||
let mut cursor = self.fragments.cursor::<usize, usize>();
|
||||
cursor.seek(&offset, seek_bias);
|
||||
cursor.seek(&offset, seek_bias, &());
|
||||
let fragment = cursor.item().unwrap();
|
||||
let offset_in_fragment = offset - cursor.start();
|
||||
let offset_in_insertion = fragment.start_offset() + offset_in_fragment;
|
||||
@@ -1690,7 +1740,7 @@ impl Buffer {
|
||||
.get(&insertion_id)
|
||||
.ok_or_else(|| anyhow!("split does not exist for insertion id"))?;
|
||||
let mut splits_cursor = splits.cursor::<usize, ()>();
|
||||
splits_cursor.seek(offset, seek_bias);
|
||||
splits_cursor.seek(offset, seek_bias, &());
|
||||
splits_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("split offset is out of range"))
|
||||
@@ -1718,13 +1768,13 @@ impl Buffer {
|
||||
.get(&insertion_id)
|
||||
.ok_or_else(|| anyhow!("split does not exist for insertion id"))?;
|
||||
let mut splits_cursor = splits.cursor::<usize, ()>();
|
||||
splits_cursor.seek(offset, seek_bias);
|
||||
splits_cursor.seek(offset, seek_bias, &());
|
||||
let split = splits_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("split offset is out of range"))?;
|
||||
|
||||
let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, TextSummary>();
|
||||
fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left);
|
||||
fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &());
|
||||
let fragment = fragments_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("fragment id does not exist"))?;
|
||||
@@ -1744,7 +1794,7 @@ impl Buffer {
|
||||
#[allow(dead_code)]
|
||||
pub fn point_for_offset(&self, offset: usize) -> Result<Point> {
|
||||
let mut fragments_cursor = self.fragments.cursor::<usize, TextSummary>();
|
||||
fragments_cursor.seek(&offset, SeekBias::Left);
|
||||
fragments_cursor.seek(&offset, SeekBias::Left, &());
|
||||
fragments_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("offset is out of range"))
|
||||
@@ -1810,7 +1860,7 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point {
|
||||
impl<'a> CharIter<'a> {
|
||||
fn new(fragments: &'a SumTree<Fragment>, offset: usize) -> Self {
|
||||
let mut fragments_cursor = fragments.cursor::<usize, usize>();
|
||||
fragments_cursor.seek(&offset, SeekBias::Right);
|
||||
fragments_cursor.seek(&offset, SeekBias::Right, &());
|
||||
let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| {
|
||||
let offset_in_fragment = offset - fragments_cursor.start();
|
||||
fragment.text[offset_in_fragment..].chars()
|
||||
@@ -1847,7 +1897,7 @@ impl<'a> Iterator for CharIter<'a> {
|
||||
impl<'a> FragmentIter<'a> {
|
||||
fn new(fragments: &'a SumTree<Fragment>) -> Self {
|
||||
let mut cursor = fragments.cursor::<usize, usize>();
|
||||
cursor.seek(&0, SeekBias::Right);
|
||||
cursor.seek(&0, SeekBias::Right, &());
|
||||
Self {
|
||||
cursor,
|
||||
started: false,
|
||||
@@ -2140,8 +2190,10 @@ impl sum_tree::Item for Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a FragmentSummary> for FragmentSummary {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
impl sum_tree::Summary for FragmentSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.text_summary += &other.text_summary;
|
||||
debug_assert!(self.max_fragment_id <= other.max_fragment_id);
|
||||
self.max_fragment_id = other.max_fragment_id.clone();
|
||||
@@ -2204,8 +2256,10 @@ impl sum_tree::Item for InsertionSplit {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a InsertionSplitSummary> for InsertionSplitSummary {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
impl sum_tree::Summary for InsertionSplitSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.extent += other.extent;
|
||||
}
|
||||
}
|
||||
@@ -2262,7 +2316,7 @@ pub trait ToOffset {
|
||||
impl ToOffset for Point {
|
||||
fn to_offset(&self, buffer: &Buffer) -> Result<usize> {
|
||||
let mut fragments_cursor = buffer.fragments.cursor::<Point, TextSummary>();
|
||||
fragments_cursor.seek(self, SeekBias::Left);
|
||||
fragments_cursor.seek(self, SeekBias::Left, &());
|
||||
fragments_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("point is out of range"))
|
||||
@@ -2306,7 +2360,7 @@ impl ToPoint for Anchor {
|
||||
impl ToPoint for usize {
|
||||
fn to_point(&self, buffer: &Buffer) -> Result<Point> {
|
||||
let mut fragments_cursor = buffer.fragments.cursor::<usize, TextSummary>();
|
||||
fragments_cursor.seek(&self, SeekBias::Left);
|
||||
fragments_cursor.seek(&self, SeekBias::Left, &());
|
||||
fragments_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("offset is out of range"))
|
||||
|
||||
@@ -75,6 +75,7 @@ impl Selection {
|
||||
|
||||
pub fn buffer_rows_for_display_rows(
|
||||
&self,
|
||||
include_end_if_at_line_start: bool,
|
||||
map: &DisplayMap,
|
||||
ctx: &AppContext,
|
||||
) -> (Range<u32>, Range<u32>) {
|
||||
@@ -84,7 +85,8 @@ impl Selection {
|
||||
.unwrap();
|
||||
|
||||
let mut display_end = self.end.to_display_point(map, ctx).unwrap();
|
||||
if display_end.row() != map.max_point(ctx).row()
|
||||
if !include_end_if_at_line_start
|
||||
&& display_end.row() != map.max_point(ctx).row()
|
||||
&& display_start.row() != display_end.row()
|
||||
&& display_end.column() == 0
|
||||
{
|
||||
|
||||
@@ -58,6 +58,14 @@ pub struct TextSummary {
|
||||
pub rightmost_point: Point,
|
||||
}
|
||||
|
||||
impl sum_tree::Summary for TextSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
*self += other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::AddAssign<&'a Self> for TextSummary {
|
||||
fn add_assign(&mut self, other: &'a Self) {
|
||||
let joined_line_len = self.lines.column + other.first_line_len;
|
||||
@@ -85,8 +93,8 @@ impl std::ops::AddAssign<Self> for TextSummary {
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, TextSummary> for TextSummary {
|
||||
fn add_summary(&mut self, summary: &TextSummary) {
|
||||
*self += summary;
|
||||
fn add_summary(&mut self, other: &TextSummary) {
|
||||
*self += other;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +165,7 @@ impl From<Arc<str>> for Text {
|
||||
}
|
||||
|
||||
let mut tree = SumTree::new();
|
||||
tree.extend(runs);
|
||||
tree.extend(runs, &());
|
||||
Text {
|
||||
text,
|
||||
runs: tree,
|
||||
@@ -231,13 +239,14 @@ impl Text {
|
||||
|
||||
pub fn line_len(&self, row: u32) -> u32 {
|
||||
let mut cursor = self.runs.cursor::<usize, Point>();
|
||||
cursor.seek(&self.range.start, SeekBias::Right);
|
||||
cursor.seek(&self.range.start, SeekBias::Right, &());
|
||||
let absolute_row = cursor.start().row + row;
|
||||
|
||||
let mut cursor = self.runs.cursor::<Point, usize>();
|
||||
cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right);
|
||||
cursor.seek(&Point::new(absolute_row, 0), SeekBias::Right, &());
|
||||
let prefix_len = self.range.start.saturating_sub(*cursor.start());
|
||||
let line_len = cursor.summary::<usize>(&Point::new(absolute_row + 1, 0), SeekBias::Left);
|
||||
let line_len =
|
||||
cursor.summary::<usize>(&Point::new(absolute_row + 1, 0), SeekBias::Left, &());
|
||||
let suffix_len = cursor.start().saturating_sub(self.range.end);
|
||||
|
||||
line_len
|
||||
@@ -262,14 +271,15 @@ impl Text {
|
||||
candidates.push(Point::new(0, self.line_len(0)));
|
||||
if lines.row > 1 {
|
||||
let mut cursor = self.runs.cursor::<usize, Point>();
|
||||
cursor.seek(&self.range.start, SeekBias::Right);
|
||||
cursor.seek(&self.range.start, SeekBias::Right, &());
|
||||
let absolute_start_row = cursor.start().row;
|
||||
|
||||
let mut cursor = self.runs.cursor::<Point, usize>();
|
||||
cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right);
|
||||
cursor.seek(&Point::new(absolute_start_row + 1, 0), SeekBias::Right, &());
|
||||
let summary = cursor.summary::<TextSummary>(
|
||||
&Point::new(absolute_start_row + lines.row, 0),
|
||||
SeekBias::Left,
|
||||
&(),
|
||||
);
|
||||
|
||||
candidates.push(Point::new(1, 0) + &summary.rightmost_point);
|
||||
@@ -287,7 +297,7 @@ impl Text {
|
||||
pub fn offset_for_point(&self, point: Point) -> usize {
|
||||
let mut cursor = self.runs.cursor::<Point, TextSummary>();
|
||||
let abs_point = self.abs_point_for_offset(self.range.start) + &point;
|
||||
cursor.seek(&abs_point, SeekBias::Right);
|
||||
cursor.seek(&abs_point, SeekBias::Right, &());
|
||||
let overshoot = abs_point - &cursor.start().lines;
|
||||
let abs_offset = cursor.start().chars + overshoot.column as usize;
|
||||
abs_offset - self.range.start
|
||||
@@ -307,14 +317,14 @@ impl Text {
|
||||
|
||||
fn abs_point_for_offset(&self, offset: usize) -> Point {
|
||||
let mut cursor = self.runs.cursor::<usize, TextSummary>();
|
||||
cursor.seek(&offset, SeekBias::Right);
|
||||
cursor.seek(&offset, SeekBias::Right, &());
|
||||
let overshoot = (offset - cursor.start().chars) as u32;
|
||||
cursor.start().lines + &Point::new(0, overshoot)
|
||||
}
|
||||
|
||||
fn abs_byte_offset_for_offset(&self, offset: usize) -> usize {
|
||||
let mut cursor = self.runs.cursor::<usize, TextSummary>();
|
||||
cursor.seek(&offset, SeekBias::Right);
|
||||
cursor.seek(&offset, SeekBias::Right, &());
|
||||
let overshoot = offset - cursor.start().chars;
|
||||
cursor.start().bytes + overshoot * cursor.item().map_or(0, |run| run.char_size()) as usize
|
||||
}
|
||||
|
||||
+198
-11
@@ -138,6 +138,12 @@ pub fn init(app: &mut MutableAppContext) {
|
||||
),
|
||||
Binding::new("cmd-shift-down", "buffer:select_to_end", Some("BufferView")),
|
||||
Binding::new("cmd-a", "buffer:select_all", Some("BufferView")),
|
||||
Binding::new("cmd-l", "buffer:select_line", Some("BufferView")),
|
||||
Binding::new(
|
||||
"cmd-shift-L",
|
||||
"buffer:split_selection_into_lines",
|
||||
Some("BufferView"),
|
||||
),
|
||||
Binding::new("pageup", "buffer:page_up", Some("BufferView")),
|
||||
Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
|
||||
Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
|
||||
@@ -228,6 +234,11 @@ pub fn init(app: &mut MutableAppContext) {
|
||||
);
|
||||
app.add_action("buffer:select_to_end", BufferView::select_to_end);
|
||||
app.add_action("buffer:select_all", BufferView::select_all);
|
||||
app.add_action("buffer:select_line", BufferView::select_line);
|
||||
app.add_action(
|
||||
"buffer:split_selection_into_lines",
|
||||
BufferView::split_selection_into_lines,
|
||||
);
|
||||
app.add_action("buffer:page_up", BufferView::page_up);
|
||||
app.add_action("buffer:page_down", BufferView::page_down);
|
||||
app.add_action("buffer:fold", BufferView::fold);
|
||||
@@ -704,7 +715,8 @@ impl BufferView {
|
||||
|
||||
let mut selections = self.selections(app).iter().peekable();
|
||||
while let Some(selection) = selections.next() {
|
||||
let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
let (mut rows, _) =
|
||||
selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
let goal_display_column = selection
|
||||
.head()
|
||||
.to_display_point(&self.display_map, app)
|
||||
@@ -714,7 +726,7 @@ impl BufferView {
|
||||
// Accumulate contiguous regions of rows that we want to delete.
|
||||
while let Some(next_selection) = selections.peek() {
|
||||
let (next_rows, _) =
|
||||
next_selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
if next_rows.start <= rows.end {
|
||||
rows.end = next_rows.end;
|
||||
selections.next().unwrap();
|
||||
@@ -795,10 +807,11 @@ impl BufferView {
|
||||
let mut selections_iter = selections.iter_mut().peekable();
|
||||
while let Some(selection) = selections_iter.next() {
|
||||
// Avoid duplicating the same lines twice.
|
||||
let (mut rows, _) = selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
let (mut rows, _) =
|
||||
selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
while let Some(next_selection) = selections_iter.peek() {
|
||||
let (next_rows, _) =
|
||||
next_selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
if next_rows.start <= rows.end - 1 {
|
||||
rows.end = next_rows.end;
|
||||
selections_iter.next().unwrap();
|
||||
@@ -852,10 +865,10 @@ impl BufferView {
|
||||
// Accumulate contiguous regions of rows that we want to move.
|
||||
contiguous_selections.push(selection.range(buffer));
|
||||
let (mut buffer_rows, mut display_rows) =
|
||||
selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
while let Some(next_selection) = selections.peek() {
|
||||
let (next_buffer_rows, next_display_rows) =
|
||||
next_selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
if next_buffer_rows.start <= buffer_rows.end {
|
||||
buffer_rows.end = next_buffer_rows.end;
|
||||
display_rows.end = next_display_rows.end;
|
||||
@@ -942,10 +955,10 @@ impl BufferView {
|
||||
// Accumulate contiguous regions of rows that we want to move.
|
||||
contiguous_selections.push(selection.range(buffer));
|
||||
let (mut buffer_rows, mut display_rows) =
|
||||
selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
while let Some(next_selection) = selections.peek() {
|
||||
let (next_buffer_rows, next_display_rows) =
|
||||
next_selection.buffer_rows_for_display_rows(&self.display_map, app);
|
||||
next_selection.buffer_rows_for_display_rows(false, &self.display_map, app);
|
||||
if next_buffer_rows.start <= buffer_rows.end {
|
||||
buffer_rows.end = next_buffer_rows.end;
|
||||
display_rows.end = next_display_rows.end;
|
||||
@@ -1654,6 +1667,63 @@ impl BufferView {
|
||||
self.update_selections(vec![selection], false, ctx);
|
||||
}
|
||||
|
||||
pub fn select_line(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||
let app = ctx.as_ref();
|
||||
let buffer = self.buffer.read(app);
|
||||
let mut selections = self.selections(app).to_vec();
|
||||
let max_point = buffer.max_point();
|
||||
for selection in &mut selections {
|
||||
let (rows, _) = selection.buffer_rows_for_display_rows(true, &self.display_map, app);
|
||||
selection.start = buffer.anchor_before(Point::new(rows.start, 0)).unwrap();
|
||||
selection.end = buffer
|
||||
.anchor_before(cmp::min(max_point, Point::new(rows.end, 0)))
|
||||
.unwrap();
|
||||
selection.reversed = false;
|
||||
}
|
||||
self.update_selections(selections, true, ctx);
|
||||
}
|
||||
|
||||
pub fn split_selection_into_lines(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
|
||||
use super::RangeExt;
|
||||
|
||||
let app = ctx.as_ref();
|
||||
let buffer = self.buffer.read(app);
|
||||
|
||||
let mut to_unfold = Vec::new();
|
||||
let mut new_selections = Vec::new();
|
||||
for selection in self.selections(app) {
|
||||
let range = selection.range(buffer).sorted();
|
||||
if range.start.row != range.end.row {
|
||||
new_selections.push(Selection {
|
||||
start: selection.start.clone(),
|
||||
end: selection.start.clone(),
|
||||
reversed: false,
|
||||
goal_column: None,
|
||||
});
|
||||
}
|
||||
for row in range.start.row + 1..range.end.row {
|
||||
let cursor = buffer
|
||||
.anchor_before(Point::new(row, buffer.line_len(row).unwrap()))
|
||||
.unwrap();
|
||||
new_selections.push(Selection {
|
||||
start: cursor.clone(),
|
||||
end: cursor,
|
||||
reversed: false,
|
||||
goal_column: None,
|
||||
});
|
||||
}
|
||||
new_selections.push(Selection {
|
||||
start: selection.end.clone(),
|
||||
end: selection.end.clone(),
|
||||
reversed: false,
|
||||
goal_column: None,
|
||||
});
|
||||
to_unfold.push(range);
|
||||
}
|
||||
self.unfold_ranges(to_unfold, ctx);
|
||||
self.update_selections(new_selections, true, ctx);
|
||||
}
|
||||
|
||||
pub fn selections_in_range<'a>(
|
||||
&'a self,
|
||||
range: Range<DisplayPoint>,
|
||||
@@ -1874,9 +1944,8 @@ impl BufferView {
|
||||
.selections(ctx.as_ref())
|
||||
.iter()
|
||||
.map(|s| s.range(buffer).sorted())
|
||||
.collect::<Vec<_>>();
|
||||
self.display_map.fold(ranges, ctx.as_ref()).unwrap();
|
||||
ctx.notify();
|
||||
.collect();
|
||||
self.fold_ranges(ranges, ctx);
|
||||
}
|
||||
|
||||
fn fold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, ctx: &mut ViewContext<Self>) {
|
||||
@@ -3292,6 +3361,124 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_line() {
|
||||
App::test((), |app| {
|
||||
let settings = settings::channel(&app.font_cache()).unwrap().1;
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(6, 5), ctx));
|
||||
let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
|
||||
view.update(app, |view, ctx| {
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
|
||||
DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
|
||||
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
|
||||
DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
|
||||
],
|
||||
ctx,
|
||||
)
|
||||
.unwrap();
|
||||
view.select_line(&(), ctx);
|
||||
});
|
||||
assert_eq!(
|
||||
view.read(app).selection_ranges(app.as_ref()),
|
||||
vec![
|
||||
DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
|
||||
DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
|
||||
]
|
||||
);
|
||||
|
||||
view.update(app, |view, ctx| view.select_line(&(), ctx));
|
||||
assert_eq!(
|
||||
view.read(app).selection_ranges(app.as_ref()),
|
||||
vec![
|
||||
DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
|
||||
DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
|
||||
]
|
||||
);
|
||||
|
||||
view.update(app, |view, ctx| view.select_line(&(), ctx));
|
||||
assert_eq!(
|
||||
view.read(app).selection_ranges(app.as_ref()),
|
||||
vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_split_selection_into_lines() {
|
||||
App::test((), |app| {
|
||||
let settings = settings::channel(&app.font_cache()).unwrap().1;
|
||||
let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(9, 5), ctx));
|
||||
let (_, view) = app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
|
||||
view.update(app, |view, ctx| {
|
||||
view.fold_ranges(
|
||||
vec![
|
||||
Point::new(0, 2)..Point::new(1, 2),
|
||||
Point::new(2, 3)..Point::new(4, 1),
|
||||
Point::new(7, 0)..Point::new(8, 4),
|
||||
],
|
||||
ctx,
|
||||
);
|
||||
view.select_display_ranges(
|
||||
&[
|
||||
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
|
||||
DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
|
||||
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
|
||||
DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
|
||||
],
|
||||
ctx,
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
assert_eq!(
|
||||
view.read(app).text(app.as_ref()),
|
||||
"aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
|
||||
);
|
||||
|
||||
view.update(app, |view, ctx| view.split_selection_into_lines(&(), ctx));
|
||||
assert_eq!(
|
||||
view.read(app).text(app.as_ref()),
|
||||
"aa…bbb\nccc…eeee\nfffff\nggggg\n…i"
|
||||
);
|
||||
assert_eq!(
|
||||
view.read(app).selection_ranges(app.as_ref()),
|
||||
[
|
||||
DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
|
||||
DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
|
||||
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
|
||||
DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2)
|
||||
]
|
||||
);
|
||||
|
||||
view.update(app, |view, ctx| {
|
||||
view.select_display_ranges(
|
||||
&[DisplayPoint::new(4, 0)..DisplayPoint::new(0, 1)],
|
||||
ctx,
|
||||
)
|
||||
.unwrap();
|
||||
view.split_selection_into_lines(&(), ctx);
|
||||
});
|
||||
assert_eq!(
|
||||
view.read(app).text(app.as_ref()),
|
||||
"aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\n…i"
|
||||
);
|
||||
assert_eq!(
|
||||
view.read(app).selection_ranges(app.as_ref()),
|
||||
[
|
||||
DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
|
||||
DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
|
||||
DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
|
||||
DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
|
||||
DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
|
||||
DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
|
||||
DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
|
||||
DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
impl BufferView {
|
||||
fn selection_ranges(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
|
||||
self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::{
|
||||
buffer, Anchor, AnchorRangeExt, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
|
||||
buffer::{self, AnchorRangeExt},
|
||||
Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
|
||||
};
|
||||
use crate::{
|
||||
sum_tree::{self, Cursor, SumTree},
|
||||
sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
|
||||
time,
|
||||
util::find_insertion_index,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{AppContext, ModelHandle};
|
||||
@@ -14,12 +14,11 @@ use std::{
|
||||
iter::Take,
|
||||
ops::Range,
|
||||
};
|
||||
use sum_tree::{Dimension, SeekBias};
|
||||
|
||||
pub struct FoldMap {
|
||||
buffer: ModelHandle<Buffer>,
|
||||
transforms: Mutex<SumTree<Transform>>,
|
||||
folds: Vec<Range<Anchor>>,
|
||||
folds: SumTree<Fold>,
|
||||
last_sync: Mutex<time::Global>,
|
||||
}
|
||||
|
||||
@@ -29,14 +28,17 @@ impl FoldMap {
|
||||
let text_summary = buffer.text_summary();
|
||||
Self {
|
||||
buffer: buffer_handle,
|
||||
folds: Vec::new(),
|
||||
transforms: Mutex::new(SumTree::from_item(Transform {
|
||||
summary: TransformSummary {
|
||||
buffer: text_summary.clone(),
|
||||
display: text_summary,
|
||||
folds: Default::default(),
|
||||
transforms: Mutex::new(SumTree::from_item(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
buffer: text_summary.clone(),
|
||||
display: text_summary,
|
||||
},
|
||||
display_text: None,
|
||||
},
|
||||
display_text: None,
|
||||
})),
|
||||
&(),
|
||||
)),
|
||||
last_sync: Mutex::new(buffer.version()),
|
||||
}
|
||||
}
|
||||
@@ -76,17 +78,12 @@ impl FoldMap {
|
||||
pub fn folds_in_range<'a, T>(
|
||||
&'a self,
|
||||
range: Range<T>,
|
||||
app: &'a AppContext,
|
||||
ctx: &'a AppContext,
|
||||
) -> Result<impl Iterator<Item = &'a Range<Anchor>>>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
let buffer = self.buffer.read(app);
|
||||
let range = buffer.anchor_before(range.start)?..buffer.anchor_before(range.end)?;
|
||||
Ok(self.folds.iter().filter(move |fold| {
|
||||
range.start.cmp(&fold.end, buffer).unwrap() == Ordering::Less
|
||||
&& range.end.cmp(&fold.start, buffer).unwrap() == Ordering::Greater
|
||||
}))
|
||||
Ok(self.intersecting_folds(range, ctx)?.map(|f| &f.0))
|
||||
}
|
||||
|
||||
pub fn fold<T: ToOffset>(
|
||||
@@ -97,19 +94,22 @@ impl FoldMap {
|
||||
let _ = self.sync(ctx);
|
||||
|
||||
let mut edits = Vec::new();
|
||||
let mut folds = Vec::new();
|
||||
let buffer = self.buffer.read(ctx);
|
||||
for range in ranges.into_iter() {
|
||||
let start = range.start.to_offset(buffer)?;
|
||||
let end = range.end.to_offset(buffer)?;
|
||||
edits.push(Edit {
|
||||
old_range: start..end,
|
||||
new_range: start..end,
|
||||
});
|
||||
|
||||
let fold = buffer.anchor_after(start)?..buffer.anchor_before(end)?;
|
||||
let ix = find_insertion_index(&self.folds, |probe| probe.cmp(&fold, buffer))?;
|
||||
self.folds.insert(ix, fold);
|
||||
let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?;
|
||||
if range.start != range.end {
|
||||
let fold =
|
||||
Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?);
|
||||
folds.push(fold);
|
||||
edits.push(Edit {
|
||||
old_range: range.clone(),
|
||||
new_range: range.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, buffer));
|
||||
edits.sort_unstable_by(|a, b| {
|
||||
a.old_range
|
||||
.start
|
||||
@@ -117,6 +117,16 @@ impl FoldMap {
|
||||
.then_with(|| b.old_range.end.cmp(&a.old_range.end))
|
||||
});
|
||||
|
||||
self.folds = {
|
||||
let mut new_tree = SumTree::new();
|
||||
let mut cursor = self.folds.cursor::<_, ()>();
|
||||
for fold in folds {
|
||||
new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
|
||||
new_tree.push(fold, buffer);
|
||||
}
|
||||
new_tree.push_tree(cursor.suffix(buffer), buffer);
|
||||
new_tree
|
||||
};
|
||||
self.apply_edits(edits, ctx);
|
||||
Ok(())
|
||||
}
|
||||
@@ -131,36 +141,66 @@ impl FoldMap {
|
||||
let buffer = self.buffer.read(ctx);
|
||||
|
||||
let mut edits = Vec::new();
|
||||
let mut fold_ixs_to_delete = Vec::new();
|
||||
for range in ranges.into_iter() {
|
||||
let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
|
||||
let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
|
||||
|
||||
// Remove intersecting folds and add their ranges to edits that are passed to apply_edits
|
||||
self.folds.retain(|fold| {
|
||||
if fold.start.cmp(&end, buffer).unwrap() > Ordering::Equal
|
||||
|| fold.end.cmp(&start, buffer).unwrap() < Ordering::Equal
|
||||
{
|
||||
true
|
||||
} else {
|
||||
let offset_range =
|
||||
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap();
|
||||
edits.push(Edit {
|
||||
old_range: offset_range.clone(),
|
||||
new_range: offset_range,
|
||||
});
|
||||
false
|
||||
}
|
||||
});
|
||||
// Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
|
||||
let mut folds_cursor = self.intersecting_folds(range, ctx)?;
|
||||
while let Some(fold) = folds_cursor.item() {
|
||||
let offset_range =
|
||||
fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap();
|
||||
edits.push(Edit {
|
||||
old_range: offset_range.clone(),
|
||||
new_range: offset_range,
|
||||
});
|
||||
fold_ixs_to_delete.push(*folds_cursor.start());
|
||||
folds_cursor.next();
|
||||
}
|
||||
}
|
||||
|
||||
fold_ixs_to_delete.sort_unstable();
|
||||
fold_ixs_to_delete.dedup();
|
||||
edits.sort_unstable_by(|a, b| {
|
||||
a.old_range
|
||||
.start
|
||||
.cmp(&b.old_range.start)
|
||||
.then_with(|| b.old_range.end.cmp(&a.old_range.end))
|
||||
});
|
||||
|
||||
self.folds = {
|
||||
let mut cursor = self.folds.cursor::<_, ()>();
|
||||
let mut folds = SumTree::new();
|
||||
for fold_ix in fold_ixs_to_delete {
|
||||
folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer);
|
||||
cursor.next();
|
||||
}
|
||||
folds.push_tree(cursor.suffix(buffer), buffer);
|
||||
folds
|
||||
};
|
||||
self.apply_edits(edits, ctx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn intersecting_folds<'a, T>(
|
||||
&self,
|
||||
range: Range<T>,
|
||||
ctx: &'a AppContext,
|
||||
) -> Result<FilterCursor<impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>>
|
||||
where
|
||||
T: ToOffset,
|
||||
{
|
||||
let buffer = self.buffer.read(ctx);
|
||||
let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
|
||||
let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
|
||||
Ok(self.folds.filter::<_, usize>(move |summary| {
|
||||
start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
|
||||
&& end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
|
||||
let transforms = self.sync(ctx);
|
||||
let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
|
||||
cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right);
|
||||
cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
|
||||
while let Some(transform) = cursor.item() {
|
||||
if transform.display_text.is_some() {
|
||||
return true;
|
||||
@@ -177,7 +217,7 @@ impl FoldMap {
|
||||
pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<usize> {
|
||||
let transforms = self.sync(ctx);
|
||||
let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right);
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
(cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
|
||||
}
|
||||
@@ -193,7 +233,7 @@ impl FoldMap {
|
||||
pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
|
||||
let transforms = self.sync(ctx);
|
||||
let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&display_point, SeekBias::Right);
|
||||
cursor.seek(&display_point, SeekBias::Right, &());
|
||||
let overshoot = display_point.0 - cursor.start().display.lines;
|
||||
cursor.start().buffer.lines + overshoot
|
||||
}
|
||||
@@ -201,7 +241,7 @@ impl FoldMap {
|
||||
pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
|
||||
let transforms = self.sync(ctx);
|
||||
let mut cursor = transforms.cursor::<Point, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right);
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point - cursor.start().buffer.lines;
|
||||
DisplayPoint(cmp::min(
|
||||
cursor.start().display.lines + overshoot,
|
||||
@@ -226,14 +266,17 @@ impl FoldMap {
|
||||
let mut new_transforms = SumTree::new();
|
||||
let mut transforms = self.transforms.lock();
|
||||
let mut cursor = transforms.cursor::<usize, usize>();
|
||||
cursor.seek(&0, SeekBias::Right);
|
||||
cursor.seek(&0, SeekBias::Right, &());
|
||||
|
||||
while let Some(mut edit) = edits.next() {
|
||||
new_transforms.push_tree(cursor.slice(&edit.old_range.start, SeekBias::Left));
|
||||
new_transforms.push_tree(
|
||||
cursor.slice(&edit.old_range.start, SeekBias::Left, &()),
|
||||
&(),
|
||||
);
|
||||
edit.new_range.start -= edit.old_range.start - cursor.start();
|
||||
edit.old_range.start = *cursor.start();
|
||||
|
||||
cursor.seek(&edit.old_range.end, SeekBias::Right);
|
||||
cursor.seek(&edit.old_range.end, SeekBias::Right, &());
|
||||
cursor.next();
|
||||
|
||||
let mut delta = edit.delta();
|
||||
@@ -250,7 +293,7 @@ impl FoldMap {
|
||||
|
||||
if next_edit.old_range.end >= edit.old_range.end {
|
||||
edit.old_range.end = next_edit.old_range.end;
|
||||
cursor.seek(&edit.old_range.end, SeekBias::Right);
|
||||
cursor.seek(&edit.old_range.end, SeekBias::Right, &());
|
||||
cursor.next();
|
||||
}
|
||||
} else {
|
||||
@@ -262,14 +305,10 @@ impl FoldMap {
|
||||
((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
|
||||
|
||||
let anchor = buffer.anchor_before(edit.new_range.start).unwrap();
|
||||
let folds_start =
|
||||
find_insertion_index(&self.folds, |probe| probe.start.cmp(&anchor, buffer))
|
||||
.unwrap();
|
||||
let mut folds = self.folds[folds_start..]
|
||||
.iter()
|
||||
.map(|fold| {
|
||||
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
|
||||
})
|
||||
let mut folds_cursor = self.folds.cursor::<_, ()>();
|
||||
folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer);
|
||||
let mut folds = folds_cursor
|
||||
.map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap())
|
||||
.peekable();
|
||||
|
||||
while folds
|
||||
@@ -293,29 +332,35 @@ impl FoldMap {
|
||||
|
||||
if fold.start > sum.buffer.chars {
|
||||
let text_summary = buffer.text_summary_for_range(sum.buffer.chars..fold.start);
|
||||
new_transforms.push(Transform {
|
||||
summary: TransformSummary {
|
||||
display: text_summary.clone(),
|
||||
buffer: text_summary,
|
||||
new_transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
display: text_summary.clone(),
|
||||
buffer: text_summary,
|
||||
},
|
||||
display_text: None,
|
||||
},
|
||||
display_text: None,
|
||||
});
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
if fold.end > fold.start {
|
||||
new_transforms.push(Transform {
|
||||
summary: TransformSummary {
|
||||
display: TextSummary {
|
||||
chars: 1,
|
||||
bytes: '…'.len_utf8(),
|
||||
lines: Point::new(0, 1),
|
||||
first_line_len: 1,
|
||||
rightmost_point: Point::new(0, 1),
|
||||
new_transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
display: TextSummary {
|
||||
chars: 1,
|
||||
bytes: '…'.len_utf8(),
|
||||
lines: Point::new(0, 1),
|
||||
first_line_len: 1,
|
||||
rightmost_point: Point::new(0, 1),
|
||||
},
|
||||
buffer: buffer.text_summary_for_range(fold.start..fold.end),
|
||||
},
|
||||
buffer: buffer.text_summary_for_range(fold.start..fold.end),
|
||||
display_text: Some('…'),
|
||||
},
|
||||
display_text: Some('…'),
|
||||
});
|
||||
&(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,26 +368,32 @@ impl FoldMap {
|
||||
if sum.buffer.chars < edit.new_range.end {
|
||||
let text_summary =
|
||||
buffer.text_summary_for_range(sum.buffer.chars..edit.new_range.end);
|
||||
new_transforms.push(Transform {
|
||||
new_transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
display: text_summary.clone(),
|
||||
buffer: text_summary,
|
||||
},
|
||||
display_text: None,
|
||||
},
|
||||
&(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
new_transforms.push_tree(cursor.suffix(&()), &());
|
||||
if new_transforms.is_empty() {
|
||||
let text_summary = buffer.text_summary();
|
||||
new_transforms.push(
|
||||
Transform {
|
||||
summary: TransformSummary {
|
||||
display: text_summary.clone(),
|
||||
buffer: text_summary,
|
||||
},
|
||||
display_text: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new_transforms.push_tree(cursor.suffix());
|
||||
if new_transforms.is_empty() {
|
||||
let text_summary = buffer.text_summary();
|
||||
new_transforms.push(Transform {
|
||||
summary: TransformSummary {
|
||||
display: text_summary.clone(),
|
||||
buffer: text_summary,
|
||||
},
|
||||
display_text: None,
|
||||
});
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
drop(cursor);
|
||||
@@ -363,7 +414,7 @@ impl FoldMapSnapshot {
|
||||
|
||||
let display_point = Point::new(start_row, 0);
|
||||
let mut cursor = self.transforms.cursor();
|
||||
cursor.seek(&DisplayPoint(display_point), SeekBias::Left);
|
||||
cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &());
|
||||
|
||||
Ok(BufferRows {
|
||||
display_point,
|
||||
@@ -374,7 +425,7 @@ impl FoldMapSnapshot {
|
||||
pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result<Chars<'a>> {
|
||||
let offset = self.to_display_offset(point, ctx)?;
|
||||
let mut cursor = self.transforms.cursor();
|
||||
cursor.seek(&offset, SeekBias::Right);
|
||||
cursor.seek(&offset, SeekBias::Right, &());
|
||||
Ok(Chars {
|
||||
cursor,
|
||||
offset: offset.0,
|
||||
@@ -385,7 +436,7 @@ impl FoldMapSnapshot {
|
||||
|
||||
fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<DisplayOffset> {
|
||||
let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
|
||||
cursor.seek(&point, SeekBias::Right);
|
||||
cursor.seek(&point, SeekBias::Right, &());
|
||||
let overshoot = point.0 - cursor.start().display.lines;
|
||||
let mut offset = cursor.start().display.chars;
|
||||
if !overshoot.is_zero() {
|
||||
@@ -421,16 +472,106 @@ impl sum_tree::Item for Transform {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::AddAssign<&'a Self> for TransformSummary {
|
||||
fn add_assign(&mut self, other: &'a Self) {
|
||||
impl sum_tree::Summary for TransformSummary {
|
||||
type Context = ();
|
||||
|
||||
fn add_summary(&mut self, other: &Self, _: &()) {
|
||||
self.buffer += &other.buffer;
|
||||
self.display += &other.display;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dimension<'a, TransformSummary> for TransformSummary {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary) {
|
||||
*self += summary;
|
||||
sum_tree::Summary::add_summary(self, summary, &());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Fold(Range<Anchor>);
|
||||
|
||||
impl Default for Fold {
|
||||
fn default() -> Self {
|
||||
Self(Anchor::Start..Anchor::End)
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Fold {
|
||||
type Summary = FoldSummary;
|
||||
|
||||
fn summary(&self) -> Self::Summary {
|
||||
FoldSummary {
|
||||
start: self.0.start.clone(),
|
||||
end: self.0.end.clone(),
|
||||
min_start: self.0.start.clone(),
|
||||
max_end: self.0.end.clone(),
|
||||
count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct FoldSummary {
|
||||
start: Anchor,
|
||||
end: Anchor,
|
||||
min_start: Anchor,
|
||||
max_end: Anchor,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl Default for FoldSummary {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start: Anchor::Start,
|
||||
end: Anchor::End,
|
||||
min_start: Anchor::End,
|
||||
max_end: Anchor::Start,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Summary for FoldSummary {
|
||||
type Context = Buffer;
|
||||
|
||||
fn add_summary(&mut self, other: &Self, buffer: &Buffer) {
|
||||
if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
|
||||
self.min_start = other.min_start.clone();
|
||||
}
|
||||
if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
|
||||
self.max_end = other.max_end.clone();
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
|
||||
assert!(start_comparison <= Ordering::Equal);
|
||||
if start_comparison == Ordering::Equal {
|
||||
assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
|
||||
}
|
||||
}
|
||||
self.start = other.start.clone();
|
||||
self.end = other.end.clone();
|
||||
self.count += other.count;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
|
||||
fn add_summary(&mut self, summary: &'a FoldSummary) {
|
||||
self.0.start = summary.start.clone();
|
||||
self.0.end = summary.end.clone();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
|
||||
fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering {
|
||||
self.0.cmp(&other.0, buffer).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
|
||||
fn add_summary(&mut self, summary: &'a FoldSummary) {
|
||||
*self += summary.count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,7 +639,7 @@ impl<'a> Iterator for Chars<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary) {
|
||||
self.0 += &summary.display.lines;
|
||||
}
|
||||
@@ -507,19 +648,19 @@ impl<'a> Dimension<'a, TransformSummary> for DisplayPoint {
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
|
||||
pub struct DisplayOffset(usize);
|
||||
|
||||
impl<'a> Dimension<'a, TransformSummary> for DisplayOffset {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary) {
|
||||
self.0 += &summary.display.chars;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dimension<'a, TransformSummary> for Point {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary) {
|
||||
*self += &summary.buffer.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Dimension<'a, TransformSummary> for usize {
|
||||
impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
|
||||
fn add_summary(&mut self, summary: &'a TransformSummary) {
|
||||
*self += &summary.buffer.chars;
|
||||
}
|
||||
@@ -571,7 +712,7 @@ mod tests {
|
||||
});
|
||||
assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
|
||||
|
||||
map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref())
|
||||
map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref())
|
||||
.unwrap();
|
||||
assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
|
||||
});
|
||||
@@ -719,7 +860,7 @@ mod tests {
|
||||
};
|
||||
|
||||
for seed in seed_range {
|
||||
println!("{:?}", seed);
|
||||
dbg!(seed);
|
||||
let mut rng = StdRng::seed_from_u64(seed);
|
||||
|
||||
App::test((), |app| {
|
||||
@@ -732,26 +873,38 @@ mod tests {
|
||||
|
||||
for _ in 0..operations {
|
||||
log::info!("text: {:?}", buffer.read(app).text());
|
||||
if rng.gen() {
|
||||
let buffer = buffer.read(app);
|
||||
|
||||
let fold_count = rng.gen_range(1..=5);
|
||||
let mut fold_ranges: Vec<Range<usize>> = Vec::new();
|
||||
for _ in 0..fold_count {
|
||||
let end = rng.gen_range(0..buffer.len() + 1);
|
||||
let start = rng.gen_range(0..end + 1);
|
||||
fold_ranges.push(start..end);
|
||||
match rng.gen_range(0..=100) {
|
||||
0..=34 => {
|
||||
let buffer = buffer.read(app);
|
||||
let mut to_fold = Vec::new();
|
||||
for _ in 0..rng.gen_range(1..=5) {
|
||||
let end = rng.gen_range(0..=buffer.len());
|
||||
let start = rng.gen_range(0..=end);
|
||||
to_fold.push(start..end);
|
||||
}
|
||||
log::info!("folding {:?}", to_fold);
|
||||
map.fold(to_fold, app.as_ref()).unwrap();
|
||||
}
|
||||
35..=59 if !map.folds.is_empty() => {
|
||||
let buffer = buffer.read(app);
|
||||
let mut to_unfold = Vec::new();
|
||||
for _ in 0..rng.gen_range(1..=3) {
|
||||
let end = rng.gen_range(0..=buffer.len());
|
||||
let start = rng.gen_range(0..=end);
|
||||
to_unfold.push(start..end);
|
||||
}
|
||||
log::info!("unfolding {:?}", to_unfold);
|
||||
map.unfold(to_unfold, app.as_ref()).unwrap();
|
||||
}
|
||||
_ => {
|
||||
let edits = buffer.update(app, |buffer, ctx| {
|
||||
let start_version = buffer.version.clone();
|
||||
let edit_count = rng.gen_range(1..=5);
|
||||
buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
|
||||
buffer.edits_since(start_version).collect::<Vec<_>>()
|
||||
});
|
||||
log::info!("editing {:?}", edits);
|
||||
}
|
||||
log::info!("folding {:?}", fold_ranges);
|
||||
map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
|
||||
} else {
|
||||
let edits = buffer.update(app, |buffer, ctx| {
|
||||
let start_version = buffer.version.clone();
|
||||
let edit_count = rng.gen_range(1..=5);
|
||||
buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
|
||||
buffer.edits_since(start_version).collect::<Vec<_>>()
|
||||
});
|
||||
log::info!("editing {:?}", edits);
|
||||
}
|
||||
map.check_invariants(app.as_ref());
|
||||
|
||||
@@ -844,6 +997,31 @@ mod tests {
|
||||
);
|
||||
assert!(map.is_line_folded(display_point.row(), app.as_ref()));
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
let end = rng.gen_range(0..=buffer.len());
|
||||
let start = rng.gen_range(0..=end);
|
||||
let expected_folds = map
|
||||
.folds
|
||||
.items()
|
||||
.into_iter()
|
||||
.filter(|fold| {
|
||||
let start = buffer.anchor_before(start).unwrap();
|
||||
let end = buffer.anchor_after(end).unwrap();
|
||||
start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less
|
||||
&& end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater
|
||||
})
|
||||
.map(|fold| fold.0)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
map.folds_in_range(start..end, app.as_ref())
|
||||
.unwrap()
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
expected_folds
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -894,13 +1072,13 @@ mod tests {
|
||||
|
||||
fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
|
||||
let buffer = self.buffer.read(app);
|
||||
let mut folds = self.folds.clone();
|
||||
let mut folds = self.folds.items();
|
||||
// Ensure sorting doesn't change how folds get merged and displayed.
|
||||
folds.sort_by(|a, b| a.cmp(b, buffer).unwrap());
|
||||
folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
|
||||
let mut fold_ranges = folds
|
||||
.iter()
|
||||
.map(|fold| {
|
||||
fold.start.to_offset(buffer).unwrap()..fold.end.to_offset(buffer).unwrap()
|
||||
fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap()
|
||||
})
|
||||
.peekable();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod fold_map;
|
||||
|
||||
use super::{buffer, Anchor, AnchorRangeExt, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
|
||||
use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
|
||||
use anyhow::Result;
|
||||
pub use fold_map::BufferRows;
|
||||
use fold_map::{FoldMap, FoldMapSnapshot};
|
||||
|
||||
Reference in New Issue
Block a user