WIP: Get the type checker passing...
...but not the borrow checker! Co-Authored-By: Max Brunsfeld <max@zed.dev>
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
mod anchor;
|
||||
mod point;
|
||||
mod selection;
|
||||
mod text;
|
||||
|
||||
pub use anchor::*;
|
||||
pub use point::*;
|
||||
use ropey::Rope;
|
||||
use ropey::{Rope, RopeSlice};
|
||||
use seahash::SeaHasher;
|
||||
pub use selection::*;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
pub use text::*;
|
||||
|
||||
use crate::{
|
||||
operation_queue::{self, OperationQueue},
|
||||
@@ -79,10 +77,6 @@ pub struct Buffer {
|
||||
lamport_clock: time::Lamport,
|
||||
}
|
||||
|
||||
pub struct Snapshot {
|
||||
fragments: SumTree<Fragment>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Transaction {
|
||||
start: time::Global,
|
||||
@@ -243,18 +237,6 @@ impl UndoMap {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CharIter<'a> {
|
||||
fragments_cursor: Cursor<'a, Fragment, usize, usize>,
|
||||
fragment_chars: str::Chars<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FragmentIter<'a> {
|
||||
cursor: Cursor<'a, Fragment, usize, usize>,
|
||||
started: bool,
|
||||
}
|
||||
|
||||
struct Edits<'a, F: Fn(&FragmentSummary) -> bool> {
|
||||
cursor: FilterCursor<'a, F, Fragment, usize>,
|
||||
undos: &'a UndoMap,
|
||||
@@ -312,21 +294,60 @@ pub struct FragmentSummary {
|
||||
|
||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
|
||||
struct FragmentTextSummary {
|
||||
visible: TextSummary,
|
||||
deleted: TextSummary,
|
||||
visible: usize,
|
||||
deleted: usize,
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
|
||||
fn add_summary(&mut self, summary: &'a FragmentSummary) {
|
||||
self.visible += &summary.text.visible;
|
||||
self.deleted += &summary.text.deleted;
|
||||
self.visible += summary.text.visible;
|
||||
self.deleted += summary.text.deleted;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, Ord, PartialOrd)]
|
||||
struct FragmentExtent {
|
||||
chars: usize,
|
||||
lines: Point,
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct TextSummary {
|
||||
pub chars: usize,
|
||||
pub bytes: usize,
|
||||
pub lines: Point,
|
||||
}
|
||||
|
||||
impl<'a> From<RopeSlice<'a>> for TextSummary {
|
||||
fn from(slice: RopeSlice<'a>) -> Self {
|
||||
let last_row = slice.len_lines() - 1;
|
||||
let last_column = slice.line(last_row).len_chars();
|
||||
Self {
|
||||
chars: slice.len_chars(),
|
||||
bytes: slice.len_bytes(),
|
||||
lines: Point::new(last_row as u32, last_column as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// if joined_line_len > self.rightmost_point.column {
|
||||
// self.rightmost_point = Point::new(self.lines.row, joined_line_len);
|
||||
// }
|
||||
// if other.rightmost_point.column > self.rightmost_point.column {
|
||||
// self.rightmost_point = self.lines + &other.rightmost_point;
|
||||
// }
|
||||
|
||||
// if self.lines.row == 0 {
|
||||
// self.first_line_len += other.first_line_len;
|
||||
// }
|
||||
|
||||
self.chars += other.chars;
|
||||
self.bytes += other.bytes;
|
||||
self.lines += &other.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<Self> for TextSummary {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self += &other;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug)]
|
||||
@@ -455,7 +476,6 @@ impl Buffer {
|
||||
extent: 0,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
),
|
||||
);
|
||||
fragments.push(
|
||||
@@ -464,7 +484,6 @@ impl Buffer {
|
||||
base_insertion.clone(),
|
||||
0..0,
|
||||
),
|
||||
&FragmentContext::default(),
|
||||
&(),
|
||||
);
|
||||
|
||||
@@ -480,11 +499,9 @@ impl Buffer {
|
||||
extent: range_in_insertion.end,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
fragments.push(
|
||||
Fragment::new(base_fragment_id, base_insertion, range_in_insertion.clone()),
|
||||
&FragmentContext::new(base_text, Rope::new(), Default::default()),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
@@ -511,10 +528,8 @@ impl Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> Snapshot {
|
||||
Snapshot {
|
||||
fragments: self.fragments.clone(),
|
||||
}
|
||||
pub fn snapshot(&self) -> Rope {
|
||||
self.visible_text.clone()
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<&FileHandle> {
|
||||
@@ -625,7 +640,7 @@ impl Buffer {
|
||||
}
|
||||
|
||||
pub fn text_summary(&self) -> TextSummary {
|
||||
self.fragments.extent::<TextSummary>()
|
||||
TextSummary::from(self.visible_text.slice(..))
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, range: Range<usize>) -> TextSummary {
|
||||
@@ -680,7 +695,7 @@ impl Buffer {
|
||||
}
|
||||
|
||||
pub fn max_point(&self) -> Point {
|
||||
TextSummary::from(&self.visible_text).lines
|
||||
self.text_summary().lines
|
||||
}
|
||||
|
||||
pub fn line(&self, row: u32) -> Result<String> {
|
||||
@@ -703,13 +718,13 @@ impl Buffer {
|
||||
Ok(self.chars_at(start)?.take(end - start))
|
||||
}
|
||||
|
||||
pub fn chars(&self) -> CharIter {
|
||||
pub fn chars(&self) -> ropey::iter::Chars {
|
||||
self.chars_at(0).unwrap()
|
||||
}
|
||||
|
||||
pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<CharIter> {
|
||||
pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<ropey::iter::Chars> {
|
||||
let offset = position.to_offset(self)?;
|
||||
Ok(CharIter::new(&self.fragments, offset))
|
||||
Ok(self.visible_text.chars_at(offset))
|
||||
}
|
||||
|
||||
pub fn selections_changed_since(&self, since: SelectionsVersion) -> bool {
|
||||
@@ -1081,15 +1096,7 @@ impl Buffer {
|
||||
let start_fragment = cursor.item().unwrap();
|
||||
if start_offset == start_fragment.range_in_insertion.end {
|
||||
// TODO: maybe don't recompute this fragment and its summary.
|
||||
new_fragments.push(
|
||||
cursor.item().unwrap().clone(),
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
cursor.start().clone(),
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(cursor.item().unwrap().clone(), &());
|
||||
cursor.next();
|
||||
}
|
||||
|
||||
@@ -1128,30 +1135,12 @@ impl Buffer {
|
||||
None
|
||||
};
|
||||
if let Some(fragment) = before_range {
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(fragment) = insertion {
|
||||
new_visible_text.insert(
|
||||
new_fragments.summary().text.visible.chars,
|
||||
new_text.unwrap(),
|
||||
);
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_visible_text
|
||||
.insert(new_fragments.summary().text.visible, new_text.unwrap());
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(mut fragment) = within_range {
|
||||
if fragment.was_visible(&version_in_range, &self.undo_map) {
|
||||
@@ -1159,35 +1148,19 @@ impl Buffer {
|
||||
fragment.visible = false;
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
let deleted_start = new_fragments.summary().text.visible.chars;
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted.chars,
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(fragment) = after_range {
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
} else {
|
||||
if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
|
||||
@@ -1199,16 +1172,8 @@ impl Buffer {
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, new_text);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
|
||||
if fragment.id < end_fragment_id
|
||||
@@ -1218,24 +1183,16 @@ impl Buffer {
|
||||
fragment.visible = false;
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
let deleted_start = new_fragments.summary().text.visible.chars;
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted.chars,
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
|
||||
cursor.next();
|
||||
@@ -1249,16 +1206,8 @@ impl Buffer {
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible.chars, new_text);
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, new_text);
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
|
||||
new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
|
||||
@@ -1363,32 +1312,24 @@ impl Buffer {
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
if fragment.visible && !was_visible {
|
||||
let visible_start = new_fragments.summary().text.deleted.chars;
|
||||
let visible_start = new_fragments.summary().text.deleted;
|
||||
let visible_range = visible_start..visible_start + fragment.len();
|
||||
new_visible_text.insert(
|
||||
new_fragments.summary().text.visible.chars,
|
||||
new_fragments.summary().text.visible,
|
||||
&new_deleted_text.slice(visible_range).to_string(),
|
||||
);
|
||||
new_deleted_text.remove(visible_range);
|
||||
} else if !fragment.visible && was_visible {
|
||||
let deleted_start = new_fragments.summary().text.visible.chars;
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted.chars,
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
if let Some(split_id) = insertion_splits.next() {
|
||||
new_fragments.push_tree(
|
||||
@@ -1418,33 +1359,25 @@ impl Buffer {
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
if fragment.visible && !was_visible {
|
||||
let visible_start = new_fragments.summary().text.deleted.chars;
|
||||
let visible_start = new_fragments.summary().text.deleted;
|
||||
let visible_range = visible_start..visible_start + fragment.len();
|
||||
new_visible_text.insert(
|
||||
new_fragments.summary().text.visible.chars,
|
||||
new_fragments.summary().text.visible,
|
||||
&new_deleted_text.slice(visible_range).to_string(),
|
||||
);
|
||||
new_deleted_text.remove(visible_range);
|
||||
} else if !fragment.visible && was_visible {
|
||||
let deleted_start = new_fragments.summary().text.visible.chars;
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted.chars,
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
new_fragments.push(
|
||||
fragment,
|
||||
&FragmentContext::new(
|
||||
new_visible_text.clone(),
|
||||
new_deleted_text.clone(),
|
||||
new_fragments.summary().text,
|
||||
),
|
||||
&(),
|
||||
);
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
}
|
||||
}
|
||||
@@ -1579,17 +1512,13 @@ impl Buffer {
|
||||
prefix.id =
|
||||
FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
|
||||
fragment.range_in_insertion.start = prefix.range_in_insertion.end;
|
||||
new_fragments.push(
|
||||
prefix.clone(),
|
||||
&FragmentContext::new(new_visible_text.clone()) & (),
|
||||
);
|
||||
new_fragments.push(prefix.clone(), &());
|
||||
new_split_tree.push(
|
||||
InsertionSplit {
|
||||
extent: prefix.range_in_insertion.end - prefix.range_in_insertion.start,
|
||||
fragment_id: prefix.id,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
fragment_start = range.start;
|
||||
}
|
||||
@@ -1614,6 +1543,7 @@ impl Buffer {
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
|
||||
new_fragments.push(new_fragment, &());
|
||||
}
|
||||
}
|
||||
@@ -1629,6 +1559,15 @@ impl Buffer {
|
||||
if fragment.visible {
|
||||
prefix.deletions.insert(local_timestamp);
|
||||
prefix.visible = false;
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + prefix.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
fragment.range_in_insertion.start = prefix.range_in_insertion.end;
|
||||
new_fragments.push(prefix.clone(), &());
|
||||
@@ -1649,6 +1588,15 @@ impl Buffer {
|
||||
if fragment.visible {
|
||||
fragment.deletions.insert(local_timestamp);
|
||||
fragment.visible = false;
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1712,6 +1660,15 @@ impl Buffer {
|
||||
if new_fragment.visible {
|
||||
new_fragment.deletions.insert(local_timestamp);
|
||||
new_fragment.visible = false;
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
let deleted_start = new_fragments.summary().text.visible;
|
||||
let deleted_range = deleted_start..deleted_start + new_fragment.len();
|
||||
new_deleted_text.insert(
|
||||
new_fragments.summary().text.deleted,
|
||||
&new_visible_text.slice(deleted_range).to_string(),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
new_fragments.push(new_fragment, &());
|
||||
cursor.next();
|
||||
@@ -1775,6 +1732,7 @@ impl Buffer {
|
||||
end_id: last_fragment.insertion.id,
|
||||
end_offset: last_fragment.range_in_insertion.end,
|
||||
version_in_range: time::Global::new(),
|
||||
// TODO: avoid cloning the String.
|
||||
new_text: new_text.clone(),
|
||||
},
|
||||
lamport_timestamp,
|
||||
@@ -1784,10 +1742,11 @@ impl Buffer {
|
||||
let new_fragment = self.build_fragment_to_insert(
|
||||
&last_fragment,
|
||||
None,
|
||||
new_text,
|
||||
&new_text,
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
|
||||
new_fragments.push(new_fragment, &());
|
||||
}
|
||||
} else {
|
||||
@@ -1797,6 +1756,8 @@ impl Buffer {
|
||||
);
|
||||
}
|
||||
|
||||
self.visible_text = new_visible_text;
|
||||
self.deleted_text = new_deleted_text;
|
||||
self.fragments = new_fragments;
|
||||
ops
|
||||
}
|
||||
@@ -1864,7 +1825,6 @@ impl Buffer {
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1875,7 +1835,6 @@ impl Buffer {
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1886,7 +1845,6 @@ impl Buffer {
|
||||
fragment_id: fragment.id.clone(),
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1927,7 +1885,6 @@ impl Buffer {
|
||||
fragment_id: new_fragment_id.clone(),
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
self.insertion_splits.insert(insertion_id, split_tree);
|
||||
|
||||
@@ -2021,7 +1978,7 @@ impl Buffer {
|
||||
fn summary_for_anchor(&self, anchor: &Anchor) -> Result<TextSummary> {
|
||||
match anchor {
|
||||
Anchor::Start => Ok(TextSummary::default()),
|
||||
Anchor::End => Ok(self.fragments.summary().text_summary),
|
||||
Anchor::End => Ok(self.text_summary()),
|
||||
Anchor::Middle {
|
||||
insertion_id,
|
||||
offset,
|
||||
@@ -2042,37 +1999,29 @@ impl Buffer {
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("split offset is out of range"))?;
|
||||
|
||||
let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, TextSummary>();
|
||||
let mut fragments_cursor = self
|
||||
.fragments
|
||||
.cursor::<FragmentIdRef, FragmentTextSummary>();
|
||||
fragments_cursor.seek(&FragmentIdRef::new(&split.fragment_id), SeekBias::Left, &());
|
||||
let fragment = fragments_cursor
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("fragment id does not exist"))?;
|
||||
|
||||
let mut summary = fragments_cursor.start().clone();
|
||||
let mut ix = fragments_cursor.start().clone().visible;
|
||||
if fragment.visible {
|
||||
summary += fragment
|
||||
.text
|
||||
.slice(..offset - fragment.range_in_insertion.start)
|
||||
.summary();
|
||||
ix += offset - fragment.range_in_insertion.start;
|
||||
}
|
||||
Ok(summary)
|
||||
Ok(self.text_summary_for_range(0..ix))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("offset is out of range"))
|
||||
.map(|fragment| {
|
||||
let overshoot = fragment
|
||||
.point_for_offset(offset - &fragments_cursor.start().chars)
|
||||
.unwrap();
|
||||
fragments_cursor.start().lines + &overshoot
|
||||
})
|
||||
if offset <= self.len() {
|
||||
Ok(self.text_summary_for_range(0..offset).lines)
|
||||
} else {
|
||||
Err(anyhow!("offset out of bounds"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2080,6 +2029,7 @@ impl Clone for Buffer {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
visible_text: self.visible_text.clone(),
|
||||
deleted_text: self.deleted_text.clone(),
|
||||
fragments: self.fragments.clone(),
|
||||
insertion_splits: self.insertion_splits.clone(),
|
||||
version: self.version.clone(),
|
||||
@@ -2100,16 +2050,6 @@ impl Clone for Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub fn fragments<'a>(&'a self) -> FragmentIter<'a> {
|
||||
FragmentIter::new(&self.fragments)
|
||||
}
|
||||
|
||||
pub fn text_summary(&self) -> TextSummary {
|
||||
self.fragments.summary().text_summary
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Event {
|
||||
Edited,
|
||||
@@ -2123,81 +2063,6 @@ impl Entity for Buffer {
|
||||
type Event = Event;
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for Point {
|
||||
fn add_summary(&mut self, summary: &FragmentSummary) {
|
||||
*self += &summary.text_summary.lines;
|
||||
}
|
||||
}
|
||||
|
||||
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, &());
|
||||
let fragment_chars = fragments_cursor.item().map_or("".chars(), |fragment| {
|
||||
let offset_in_fragment = offset - fragments_cursor.start();
|
||||
fragment.text[offset_in_fragment..].chars()
|
||||
});
|
||||
Self {
|
||||
fragments_cursor,
|
||||
fragment_chars,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CharIter<'a> {
|
||||
type Item = char;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(char) = self.fragment_chars.next() {
|
||||
Some(char)
|
||||
} else {
|
||||
loop {
|
||||
self.fragments_cursor.next();
|
||||
if let Some(fragment) = self.fragments_cursor.item() {
|
||||
if fragment.visible {
|
||||
self.fragment_chars = fragment.text.as_str().chars();
|
||||
return self.fragment_chars.next();
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FragmentIter<'a> {
|
||||
fn new(fragments: &'a SumTree<Fragment>) -> Self {
|
||||
let mut cursor = fragments.cursor::<usize, usize>();
|
||||
cursor.seek(&0, SeekBias::Right, &());
|
||||
Self {
|
||||
cursor,
|
||||
started: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for FragmentIter<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.started {
|
||||
self.cursor.next();
|
||||
} else {
|
||||
self.started = true;
|
||||
}
|
||||
if let Some(fragment) = self.cursor.item() {
|
||||
if fragment.visible {
|
||||
return Some(fragment.text.as_str());
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, F: Fn(&FragmentSummary) -> bool> Iterator for Edits<'a, F> {
|
||||
type Item = Edit;
|
||||
|
||||
@@ -2411,28 +2276,10 @@ impl Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FragmentContext {
|
||||
visible_text: Rope,
|
||||
deleted_text: Rope,
|
||||
start: FragmentTextSummary,
|
||||
}
|
||||
|
||||
impl FragmentContext {
|
||||
fn new(visible_text: Rope, deleted_text: Rope, start: FragmentTextSummary) -> Self {
|
||||
Self {
|
||||
visible_text,
|
||||
deleted_text,
|
||||
start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Fragment {
|
||||
type Context = FragmentContext;
|
||||
type Summary = FragmentSummary;
|
||||
|
||||
fn summary(&self, ctx: &FragmentContext) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
let mut max_version = time::Global::new();
|
||||
max_version.observe(self.insertion.id);
|
||||
for deletion in &self.deletions {
|
||||
@@ -2441,21 +2288,19 @@ impl sum_tree::Item for Fragment {
|
||||
max_version.observe_all(&self.max_undos);
|
||||
|
||||
if self.visible {
|
||||
let start = ctx.start.visible.chars;
|
||||
FragmentSummary {
|
||||
text: FragmentTextSummary {
|
||||
visible: TextSummary::from(ctx.visible_text.slice(start..start + self.len())),
|
||||
deleted: TextSummary::default(),
|
||||
visible: self.len(),
|
||||
deleted: 0,
|
||||
},
|
||||
max_fragment_id: self.id.clone(),
|
||||
max_version,
|
||||
}
|
||||
} else {
|
||||
let start = ctx.start.deleted.chars;
|
||||
FragmentSummary {
|
||||
text: FragmentTextSummary {
|
||||
visible: TextSummary::default(),
|
||||
deleted: TextSummary::from(ctx.deleted_text.slice(start..start + self.len())),
|
||||
visible: 0,
|
||||
deleted: self.len(),
|
||||
},
|
||||
max_fragment_id: self.id.clone(),
|
||||
max_version,
|
||||
@@ -2486,46 +2331,16 @@ impl Default for FragmentSummary {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for TextSummary {
|
||||
fn add_summary(&mut self, summary: &FragmentSummary) {
|
||||
*self += &summary.text_summary;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AddAssign<&'a FragmentExtent> for FragmentExtent {
|
||||
fn add_assign(&mut self, other: &Self) {
|
||||
self.chars += other.chars;
|
||||
self.lines += &other.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FragmentExtent {
|
||||
fn default() -> Self {
|
||||
FragmentExtent {
|
||||
lines: Point::zero(),
|
||||
chars: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentExtent {
|
||||
fn add_summary(&mut self, summary: &FragmentSummary) {
|
||||
self.chars += summary.text_summary.chars;
|
||||
self.lines += &summary.text_summary.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for usize {
|
||||
fn add_summary(&mut self, summary: &FragmentSummary) {
|
||||
*self += summary.text_summary.chars;
|
||||
*self += summary.text.visible;
|
||||
}
|
||||
}
|
||||
|
||||
impl sum_tree::Item for InsertionSplit {
|
||||
type Context = ();
|
||||
type Summary = InsertionSplitSummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
InsertionSplitSummary {
|
||||
extent: self.extent,
|
||||
}
|
||||
@@ -2591,17 +2406,12 @@ 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
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("point is out of range"))
|
||||
.map(|fragment| {
|
||||
let overshoot = fragment
|
||||
.offset_for_point(*self - fragments_cursor.start().lines)
|
||||
.unwrap();
|
||||
fragments_cursor.start().chars + overshoot
|
||||
})
|
||||
if *self <= buffer.max_point() {
|
||||
// TODO: return an error if line is shorter than column.
|
||||
Ok(buffer.visible_text.line_to_char(self.row as usize) + self.column as usize)
|
||||
} else {
|
||||
Err(anyhow!("point is out of bounds"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2635,17 +2445,13 @@ 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
|
||||
.item()
|
||||
.ok_or_else(|| anyhow!("offset is out of range"))
|
||||
.map(|fragment| {
|
||||
let overshoot = fragment
|
||||
.point_for_offset(*self - &fragments_cursor.start().chars)
|
||||
.unwrap();
|
||||
fragments_cursor.start().lines + overshoot
|
||||
})
|
||||
if *self <= buffer.len() {
|
||||
let row = buffer.visible_text.char_to_line(*self);
|
||||
let column = *self - buffer.visible_text.line_to_char(row);
|
||||
Ok(Point::new(row as u32, column as u32))
|
||||
} else {
|
||||
Err(anyhow!("offset is out of bounds"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2798,8 +2604,9 @@ mod tests {
|
||||
let (longest_column, longest_rows) =
|
||||
line_lengths.iter().next_back().unwrap();
|
||||
let range_sum = buffer.text_summary_for_range(start..end);
|
||||
assert_eq!(range_sum.rightmost_point.column, *longest_column);
|
||||
assert!(longest_rows.contains(&range_sum.rightmost_point.row));
|
||||
// TODO: re-enable when we have rightmost point again.
|
||||
// assert_eq!(range_sum.rightmost_point.column, *longest_column);
|
||||
// assert!(longest_rows.contains(&range_sum.rightmost_point.row));
|
||||
let range_text = &buffer.text()[start..end];
|
||||
assert_eq!(range_sum.chars, range_text.chars().count());
|
||||
assert_eq!(range_sum.bytes, range_text.len());
|
||||
@@ -2878,26 +2685,45 @@ mod tests {
|
||||
fn test_text_summary_for_range(ctx: &mut gpui::MutableAppContext) {
|
||||
ctx.add_model(|ctx| {
|
||||
let buffer = Buffer::new(0, "ab\nefg\nhklm\nnopqrs\ntuvwxyz", ctx);
|
||||
let text = Text::from(buffer.text());
|
||||
assert_eq!(
|
||||
buffer.text_summary_for_range(1..3),
|
||||
text.slice(1..3).summary()
|
||||
TextSummary {
|
||||
chars: 2,
|
||||
bytes: 2,
|
||||
lines: Point::new(1, 0)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text_summary_for_range(1..12),
|
||||
text.slice(1..12).summary()
|
||||
TextSummary {
|
||||
chars: 2,
|
||||
bytes: 2,
|
||||
lines: Point::new(1, 0)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text_summary_for_range(0..20),
|
||||
text.slice(0..20).summary()
|
||||
TextSummary {
|
||||
chars: 2,
|
||||
bytes: 2,
|
||||
lines: Point::new(1, 0)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text_summary_for_range(0..22),
|
||||
text.slice(0..22).summary()
|
||||
TextSummary {
|
||||
chars: 2,
|
||||
bytes: 2,
|
||||
lines: Point::new(1, 0)
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
buffer.text_summary_for_range(7..22),
|
||||
text.slice(7..22).summary()
|
||||
TextSummary {
|
||||
chars: 2,
|
||||
bytes: 2,
|
||||
lines: Point::new(1, 0)
|
||||
}
|
||||
);
|
||||
buffer
|
||||
});
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
use ropey::{Rope, RopeSlice};
|
||||
|
||||
use super::Point;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct TextSummary {
|
||||
pub chars: usize,
|
||||
pub bytes: usize,
|
||||
pub lines: Point,
|
||||
}
|
||||
|
||||
impl<'a> From<RopeSlice<'a>> for TextSummary {
|
||||
fn from(slice: RopeSlice<'a>) -> Self {
|
||||
let last_row = slice.len_lines() - 1;
|
||||
let last_column = slice.line(last_row).len_chars();
|
||||
Self {
|
||||
chars: slice.len_chars(),
|
||||
bytes: slice.len_bytes(),
|
||||
lines: Point::new(last_row as u32, last_column as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Rope> for TextSummary {
|
||||
fn from(text: &'a Rope) -> Self {
|
||||
Self::from(text.slice(..))
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
// if joined_line_len > self.rightmost_point.column {
|
||||
// self.rightmost_point = Point::new(self.lines.row, joined_line_len);
|
||||
// }
|
||||
// if other.rightmost_point.column > self.rightmost_point.column {
|
||||
// self.rightmost_point = self.lines + &other.rightmost_point;
|
||||
// }
|
||||
|
||||
// if self.lines.row == 0 {
|
||||
// self.first_line_len += other.first_line_len;
|
||||
// }
|
||||
|
||||
self.chars += other.chars;
|
||||
self.bytes += other.bytes;
|
||||
self.lines += &other.lines;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign<Self> for TextSummary {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self += &other;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
buffer::{self, AnchorRangeExt},
|
||||
Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
|
||||
buffer::{self, AnchorRangeExt, TextSummary},
|
||||
Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset,
|
||||
};
|
||||
use crate::{
|
||||
sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
|
||||
@@ -38,7 +38,6 @@ impl FoldMap {
|
||||
display_text: None,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
)),
|
||||
last_sync: Mutex::new(buffer.version()),
|
||||
}
|
||||
@@ -124,7 +123,7 @@ impl FoldMap {
|
||||
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(fold, buffer);
|
||||
}
|
||||
new_tree.push_tree(cursor.suffix(buffer), buffer);
|
||||
new_tree
|
||||
@@ -343,7 +342,6 @@ impl FoldMap {
|
||||
display_text: None,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -363,7 +361,6 @@ impl FoldMap {
|
||||
display_text: Some('…'),
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -381,7 +378,6 @@ impl FoldMap {
|
||||
display_text: None,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -398,7 +394,6 @@ impl FoldMap {
|
||||
display_text: None,
|
||||
},
|
||||
&(),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -471,10 +466,9 @@ struct TransformSummary {
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Transform {
|
||||
type Context = ();
|
||||
type Summary = TransformSummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
self.summary.clone()
|
||||
}
|
||||
}
|
||||
@@ -504,10 +498,9 @@ impl Default for Fold {
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Fold {
|
||||
type Context = ();
|
||||
type Summary = FoldSummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
FoldSummary {
|
||||
start: self.0.start.clone(),
|
||||
end: self.0.end.clone(),
|
||||
@@ -615,7 +608,7 @@ pub struct Chars<'a> {
|
||||
cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
|
||||
offset: usize,
|
||||
buffer: &'a Buffer,
|
||||
buffer_chars: Option<Take<buffer::CharIter<'a>>>,
|
||||
buffer_chars: Option<Take<ropey::iter::Chars<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chars<'a> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
mod fold_map;
|
||||
|
||||
use super::{buffer, Anchor, Buffer, Edit, Point, TextSummary, ToOffset, ToPoint};
|
||||
use super::{buffer, Anchor, Buffer, Edit, Point, ToOffset, ToPoint};
|
||||
use anyhow::Result;
|
||||
pub use fold_map::BufferRows;
|
||||
use fold_map::{FoldMap, FoldMapSnapshot};
|
||||
|
||||
@@ -48,10 +48,9 @@ impl<T: Operation> OperationQueue<T> {
|
||||
}
|
||||
|
||||
impl<T: Operation> Item for T {
|
||||
type Context = ();
|
||||
type Summary = OperationSummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
OperationSummary {
|
||||
key: OperationKey(self.timestamp()),
|
||||
len: 1,
|
||||
|
||||
@@ -11,13 +11,12 @@ const TREE_BASE: usize = 2;
|
||||
const TREE_BASE: usize = 6;
|
||||
|
||||
pub trait Item: Clone + fmt::Debug {
|
||||
type Context;
|
||||
type Summary: Summary;
|
||||
|
||||
fn summary(&self, ctx: &Self::Context) -> Self::Summary;
|
||||
fn summary(&self) -> Self::Summary;
|
||||
}
|
||||
|
||||
pub trait KeyedItem: Item<Context = ()> {
|
||||
pub trait KeyedItem: Item {
|
||||
type Key: for<'a> Dimension<'a, Self::Summary> + Ord;
|
||||
|
||||
fn key(&self) -> Self::Key;
|
||||
@@ -65,13 +64,9 @@ impl<T: Item> SumTree<T> {
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn from_item(
|
||||
item: T,
|
||||
item_ctx: &T::Context,
|
||||
summary_ctx: &<T::Summary as Summary>::Context,
|
||||
) -> Self {
|
||||
pub fn from_item(item: T, ctx: &<T::Summary as Summary>::Context) -> Self {
|
||||
let mut tree = Self::new();
|
||||
tree.push(item, item_ctx, summary_ctx);
|
||||
tree.push(item, ctx);
|
||||
tree
|
||||
}
|
||||
|
||||
@@ -130,13 +125,47 @@ impl<T: Item> SumTree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(
|
||||
&mut self,
|
||||
item: T,
|
||||
item_ctx: &T::Context,
|
||||
summary_ctx: &<T::Summary as Summary>::Context,
|
||||
) {
|
||||
let summary = item.summary(item_ctx);
|
||||
pub fn extend<I>(&mut self, iter: I, ctx: &<T::Summary as Summary>::Context)
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
let mut leaf: Option<Node<T>> = None;
|
||||
|
||||
for item in iter {
|
||||
if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
|
||||
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
|
||||
}
|
||||
|
||||
if leaf.is_none() {
|
||||
leaf = Some(Node::Leaf::<T> {
|
||||
summary: T::Summary::default(),
|
||||
items: ArrayVec::new(),
|
||||
item_summaries: ArrayVec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(Node::Leaf {
|
||||
summary,
|
||||
items,
|
||||
item_summaries,
|
||||
}) = leaf.as_mut()
|
||||
{
|
||||
let item_summary = item.summary();
|
||||
summary.add_summary(&item_summary, ctx);
|
||||
items.push(item);
|
||||
item_summaries.push(item_summary);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
if leaf.is_some() {
|
||||
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
|
||||
let summary = item.summary();
|
||||
self.push_tree(
|
||||
SumTree::from_child_trees(
|
||||
vec![SumTree(Arc::new(Node::Leaf {
|
||||
@@ -144,9 +173,9 @@ impl<T: Item> SumTree<T> {
|
||||
items: ArrayVec::from_iter(Some(item)),
|
||||
item_summaries: ArrayVec::from_iter(Some(summary)),
|
||||
}))],
|
||||
summary_ctx,
|
||||
ctx,
|
||||
),
|
||||
summary_ctx,
|
||||
ctx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -320,54 +349,13 @@ impl<T: Item> SumTree<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Item<Context = ()>> SumTree<T> {
|
||||
pub fn extend<I>(&mut self, iter: I, ctx: &<T::Summary as Summary>::Context)
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
let mut leaf: Option<Node<T>> = None;
|
||||
|
||||
for item in iter {
|
||||
if leaf.is_some() && leaf.as_ref().unwrap().items().len() == 2 * TREE_BASE {
|
||||
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
|
||||
}
|
||||
|
||||
if leaf.is_none() {
|
||||
leaf = Some(Node::Leaf::<T> {
|
||||
summary: T::Summary::default(),
|
||||
items: ArrayVec::new(),
|
||||
item_summaries: ArrayVec::new(),
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(Node::Leaf {
|
||||
summary,
|
||||
items,
|
||||
item_summaries,
|
||||
}) = leaf.as_mut()
|
||||
{
|
||||
let item_summary = item.summary(&());
|
||||
summary.add_summary(&item_summary, ctx);
|
||||
items.push(item);
|
||||
item_summaries.push(item_summary);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
if leaf.is_some() {
|
||||
self.push_tree(SumTree(Arc::new(leaf.take().unwrap())), ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: KeyedItem> SumTree<T> {
|
||||
#[allow(unused)]
|
||||
pub fn insert(&mut self, item: T, ctx: &<T::Summary as Summary>::Context) {
|
||||
*self = {
|
||||
let mut cursor = self.cursor::<T::Key, ()>();
|
||||
let mut new_tree = cursor.slice(&item.key(), SeekBias::Left, ctx);
|
||||
new_tree.push(item, &(), ctx);
|
||||
new_tree.push(item, ctx);
|
||||
new_tree.push_tree(cursor.suffix(ctx), ctx);
|
||||
new_tree
|
||||
};
|
||||
@@ -875,10 +863,9 @@ mod tests {
|
||||
struct Sum(usize);
|
||||
|
||||
impl Item for u8 {
|
||||
type Context = ();
|
||||
type Summary = IntegersSummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
IntegersSummary {
|
||||
count: Count(1),
|
||||
sum: Sum(*self as usize),
|
||||
|
||||
@@ -3,7 +3,7 @@ mod fuzzy;
|
||||
mod ignore;
|
||||
|
||||
use crate::{
|
||||
editor::{History, Snapshot as BufferSnapshot},
|
||||
editor::History,
|
||||
sum_tree::{self, Cursor, Edit, SeekBias, SumTree},
|
||||
};
|
||||
use ::ignore::gitignore::Gitignore;
|
||||
@@ -16,6 +16,7 @@ use postage::{
|
||||
prelude::{Sink, Stream},
|
||||
watch,
|
||||
};
|
||||
use ropey::Rope;
|
||||
use smol::channel::Sender;
|
||||
use std::{
|
||||
cmp,
|
||||
@@ -198,20 +199,15 @@ impl Worktree {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn save<'a>(
|
||||
&self,
|
||||
path: &Path,
|
||||
content: BufferSnapshot,
|
||||
ctx: &AppContext,
|
||||
) -> Task<Result<()>> {
|
||||
pub fn save<'a>(&self, path: &Path, content: Rope, ctx: &AppContext) -> Task<Result<()>> {
|
||||
let handles = self.handles.clone();
|
||||
let path = path.to_path_buf();
|
||||
let abs_path = self.absolutize(&path);
|
||||
ctx.background_executor().spawn(async move {
|
||||
let buffer_size = content.text_summary().bytes.min(10 * 1024);
|
||||
let buffer_size = content.len_bytes().min(10 * 1024);
|
||||
let file = fs::File::create(&abs_path)?;
|
||||
let mut writer = io::BufWriter::with_capacity(buffer_size, &file);
|
||||
for chunk in content.fragments() {
|
||||
for chunk in content.chunks() {
|
||||
writer.write(chunk.as_bytes())?;
|
||||
}
|
||||
writer.flush()?;
|
||||
@@ -459,7 +455,7 @@ impl FileHandle {
|
||||
self.worktree.read(ctx).load_history(&self.path(), ctx)
|
||||
}
|
||||
|
||||
pub fn save<'a>(&self, content: BufferSnapshot, ctx: &AppContext) -> Task<Result<()>> {
|
||||
pub fn save<'a>(&self, content: Rope, ctx: &AppContext) -> Task<Result<()>> {
|
||||
let worktree = self.worktree.read(ctx);
|
||||
worktree.save(&self.path(), content, ctx)
|
||||
}
|
||||
@@ -538,10 +534,9 @@ impl Entry {
|
||||
}
|
||||
|
||||
impl sum_tree::Item for Entry {
|
||||
type Context = ();
|
||||
type Summary = EntrySummary;
|
||||
|
||||
fn summary(&self, _: &()) -> Self::Summary {
|
||||
fn summary(&self) -> Self::Summary {
|
||||
let file_count;
|
||||
let visible_file_count;
|
||||
if self.is_file() {
|
||||
|
||||
Reference in New Issue
Block a user