mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 05:15:00 +00:00
WIP: Replace ropey with our own Rope
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
2f5754db63
commit
2cdf315d95
6 changed files with 220 additions and 243 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -2188,15 +2188,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
|
||||
[[package]]
|
||||
name = "ropey"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f3ef16589fdbb3e8fbce3dca944c08e61f39c7f16064b21a257d68ea911a83"
|
||||
dependencies = [
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.14.1"
|
||||
|
@ -2987,7 +2978,6 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"postage",
|
||||
"rand 0.8.3",
|
||||
"ropey",
|
||||
"rust-embed",
|
||||
"seahash",
|
||||
"serde 1.0.125",
|
||||
|
|
|
@ -31,7 +31,6 @@ num_cpus = "1.13.0"
|
|||
parking_lot = "0.11.1"
|
||||
postage = {version = "0.4.1", features = ["futures-traits"]}
|
||||
rand = "0.8.3"
|
||||
ropey = "1.2"
|
||||
rust-embed = "5.9.0"
|
||||
seahash = "4.1"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
mod anchor;
|
||||
mod point;
|
||||
mod rope;
|
||||
pub mod rope;
|
||||
mod selection;
|
||||
|
||||
pub use anchor::*;
|
||||
pub use point::*;
|
||||
use ropey::{Rope, RopeSlice};
|
||||
pub use rope::{Rope, TextSummary};
|
||||
use seahash::SeaHasher;
|
||||
pub use selection::*;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
|
@ -57,9 +57,9 @@ type HashMap<K, V> = std::collections::HashMap<K, V>;
|
|||
type HashSet<T> = std::collections::HashSet<T>;
|
||||
|
||||
pub struct Buffer {
|
||||
fragments: SumTree<Fragment>,
|
||||
visible_text: Rope,
|
||||
deleted_text: Rope,
|
||||
fragments: SumTree<Fragment>,
|
||||
insertion_splits: HashMap<time::Local, SumTree<InsertionSplit>>,
|
||||
pub version: time::Global,
|
||||
saved_version: time::Global,
|
||||
|
@ -305,74 +305,6 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct TextSummary {
|
||||
pub chars: usize,
|
||||
pub bytes: usize,
|
||||
pub lines: Point,
|
||||
pub first_line_len: u32,
|
||||
pub rightmost_point: 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();
|
||||
|
||||
let mut point = Point::default();
|
||||
let mut rightmost_point = point;
|
||||
let mut first_line_len = None;
|
||||
for (i, c) in slice.chars().enumerate() {
|
||||
if c == '\n' {
|
||||
if first_line_len.is_none() {
|
||||
first_line_len = Some(i as u32);
|
||||
}
|
||||
point.row += 1;
|
||||
point.column = 0;
|
||||
} else {
|
||||
point.column += 1;
|
||||
if point.column > rightmost_point.column {
|
||||
rightmost_point = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
chars: slice.len_chars(),
|
||||
bytes: slice.len_bytes(),
|
||||
lines: Point::new(last_row as u32, last_column as u32),
|
||||
first_line_len: first_line_len.unwrap_or(slice.len_chars() as u32),
|
||||
rightmost_point,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
struct InsertionSplit {
|
||||
extent: usize,
|
||||
|
@ -510,10 +442,10 @@ impl Buffer {
|
|||
&(),
|
||||
);
|
||||
|
||||
if base_text.len_chars() > 0 {
|
||||
if base_text.len() > 0 {
|
||||
let base_fragment_id =
|
||||
FragmentId::between(&FragmentId::min_value(), &FragmentId::max_value());
|
||||
let range_in_insertion = 0..base_text.len_chars();
|
||||
let range_in_insertion = 0..base_text.len();
|
||||
|
||||
visible_text = base_text.clone();
|
||||
insertion_splits.get_mut(&base_insertion.id).unwrap().push(
|
||||
|
@ -663,11 +595,12 @@ impl Buffer {
|
|||
}
|
||||
|
||||
pub fn text_summary(&self) -> TextSummary {
|
||||
TextSummary::from(self.visible_text.slice(..))
|
||||
self.visible_text.summary()
|
||||
}
|
||||
|
||||
pub fn text_summary_for_range(&self, range: Range<usize>) -> TextSummary {
|
||||
TextSummary::from(self.visible_text.slice(range))
|
||||
// TODO: Use a dedicated ::summarize method in Rope.
|
||||
self.visible_text.slice(range).summary()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
@ -686,7 +619,7 @@ impl Buffer {
|
|||
}
|
||||
|
||||
pub fn rightmost_point(&self) -> Point {
|
||||
self.rightmost_point_in_range(0..self.len())
|
||||
self.visible_text.summary().rightmost_point
|
||||
}
|
||||
|
||||
pub fn rightmost_point_in_range(&self, range: Range<usize>) -> Point {
|
||||
|
@ -694,7 +627,7 @@ impl Buffer {
|
|||
}
|
||||
|
||||
pub fn max_point(&self) -> Point {
|
||||
self.text_summary().lines
|
||||
self.visible_text.max_point()
|
||||
}
|
||||
|
||||
pub fn line(&self, row: u32) -> Result<String> {
|
||||
|
@ -717,11 +650,11 @@ impl Buffer {
|
|||
Ok(self.chars_at(start)?.take(end - start))
|
||||
}
|
||||
|
||||
pub fn chars(&self) -> ropey::iter::Chars {
|
||||
pub fn chars(&self) -> rope::Chars {
|
||||
self.chars_at(0).unwrap()
|
||||
}
|
||||
|
||||
pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<ropey::iter::Chars> {
|
||||
pub fn chars_at<T: ToOffset>(&self, position: T) -> Result<rope::Chars> {
|
||||
let offset = position.to_offset(self)?;
|
||||
Ok(self.visible_text.chars_at(offset))
|
||||
}
|
||||
|
@ -1084,22 +1017,25 @@ impl Buffer {
|
|||
let end_fragment_id = self.resolve_fragment_id(end_id, end_offset)?;
|
||||
|
||||
let old_fragments = self.fragments.clone();
|
||||
let last_id = old_fragments.extent::<FragmentIdRef>().0.unwrap();
|
||||
let last_id_ref = FragmentIdRef::new(&last_id);
|
||||
let old_visible_text = self.visible_text.clone();
|
||||
let old_deleted_text = self.deleted_text.clone();
|
||||
|
||||
let mut fragments_cursor = old_fragments.cursor::<FragmentIdRef, FragmentTextSummary>();
|
||||
let mut visible_text_cursor = old_visible_text.cursor(0);
|
||||
let mut deleted_text_cursor = old_deleted_text.cursor(0);
|
||||
|
||||
let mut cursor = old_fragments.cursor::<FragmentIdRef, FragmentTextSummary>();
|
||||
let mut new_fragments =
|
||||
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
|
||||
let mut new_visible_text = self.visible_text.clone();
|
||||
let mut new_deleted_text = self.deleted_text.clone();
|
||||
fragments_cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
|
||||
let mut new_visible_text = visible_text_cursor.slice(fragments_cursor.start().visible);
|
||||
let mut new_deleted_text = deleted_text_cursor.slice(fragments_cursor.start().deleted);
|
||||
|
||||
let start_fragment = cursor.item().unwrap();
|
||||
let start_fragment = fragments_cursor.item().unwrap();
|
||||
if start_offset == start_fragment.range_in_insertion.end {
|
||||
new_fragments.push(cursor.item().unwrap().clone(), &());
|
||||
cursor.next();
|
||||
new_fragments.push(fragments_cursor.item().unwrap().clone(), &());
|
||||
fragments_cursor.next();
|
||||
}
|
||||
|
||||
while let Some(fragment) = cursor.item() {
|
||||
while let Some(fragment) = fragments_cursor.item() {
|
||||
if new_text.is_none() && fragment.id > end_fragment_id {
|
||||
break;
|
||||
}
|
||||
|
@ -1118,13 +1054,14 @@ impl Buffer {
|
|||
fragment.range_in_insertion.end
|
||||
};
|
||||
let (before_range, within_range, after_range) = self.split_fragment(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
fragments_cursor.prev_item().as_ref().unwrap(),
|
||||
&fragment,
|
||||
split_start..split_end,
|
||||
);
|
||||
let insertion = if let Some(new_text) = new_text {
|
||||
let prev_fragment = fragments_cursor.prev_item();
|
||||
Some(self.build_fragment_to_insert(
|
||||
before_range.as_ref().or(cursor.prev_item()).unwrap(),
|
||||
before_range.as_ref().or(prev_fragment).unwrap(),
|
||||
within_range.as_ref().or(after_range.as_ref()),
|
||||
new_text,
|
||||
local_timestamp,
|
||||
|
@ -1137,25 +1074,26 @@ impl Buffer {
|
|||
new_fragments.push(fragment, &());
|
||||
}
|
||||
if let Some(fragment) = insertion {
|
||||
new_visible_text.insert(
|
||||
new_fragments.summary().text.visible,
|
||||
new_text.take().unwrap(),
|
||||
);
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_fragments.push(fragment, &());
|
||||
new_visible_text.push(new_text.take().unwrap());
|
||||
}
|
||||
if let Some(mut fragment) = within_range {
|
||||
if fragment.was_visible(&version_in_range, &self.undo_map) {
|
||||
fragment.deletions.insert(local_timestamp);
|
||||
if fragment.visible {
|
||||
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.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + fragment.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1168,14 +1106,16 @@ impl Buffer {
|
|||
if new_text.is_some() && lamport_timestamp > fragment.insertion.lamport_timestamp {
|
||||
let new_text = new_text.take().unwrap();
|
||||
let fragment = self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
fragments_cursor.prev_item().as_ref().unwrap(),
|
||||
Some(&fragment),
|
||||
new_text,
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, new_text);
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_fragments.push(fragment, &());
|
||||
new_visible_text.push(new_text);
|
||||
}
|
||||
|
||||
if fragment.id < end_fragment_id
|
||||
|
@ -1184,39 +1124,46 @@ impl Buffer {
|
|||
fragment.deletions.insert(local_timestamp);
|
||||
if fragment.visible {
|
||||
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.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + fragment.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
new_fragments.push(fragment, &());
|
||||
}
|
||||
|
||||
cursor.next();
|
||||
fragments_cursor.next();
|
||||
}
|
||||
|
||||
if let Some(new_text) = new_text {
|
||||
let fragment = self.build_fragment_to_insert(
|
||||
cursor.prev_item().as_ref().unwrap(),
|
||||
fragments_cursor.prev_item().as_ref().unwrap(),
|
||||
None,
|
||||
new_text,
|
||||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, new_text);
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_fragments.push(fragment, &());
|
||||
new_visible_text.push(new_text);
|
||||
}
|
||||
|
||||
new_fragments.push_tree(cursor.slice(&last_id_ref, SeekBias::Right, &()), &());
|
||||
new_fragments.push_tree(fragments_cursor.suffix(&()), &());
|
||||
new_visible_text.append(visible_text_cursor.suffix());
|
||||
new_deleted_text.append(deleted_text_cursor.suffix());
|
||||
|
||||
self.fragments = new_fragments;
|
||||
self.visible_text = new_visible_text;
|
||||
self.deleted_text = new_deleted_text;
|
||||
self.fragments = new_fragments;
|
||||
self.local_clock.observe(local_timestamp);
|
||||
self.lamport_clock.observe(lamport_timestamp);
|
||||
Ok(())
|
||||
|
@ -1291,52 +1238,60 @@ impl Buffer {
|
|||
|
||||
fn apply_undo(&mut self, undo: UndoOperation) -> Result<()> {
|
||||
let mut new_fragments;
|
||||
let mut new_visible_text = self.visible_text.clone();
|
||||
let mut new_deleted_text = self.deleted_text.clone();
|
||||
let mut new_visible_text = Rope::new();
|
||||
let mut new_deleted_text = Rope::new();
|
||||
|
||||
self.undo_map.insert(undo);
|
||||
let edit = &self.history.ops[&undo.edit_id];
|
||||
let start_fragment_id = self.resolve_fragment_id(edit.start_id, edit.start_offset)?;
|
||||
let end_fragment_id = self.resolve_fragment_id(edit.end_id, edit.end_offset)?;
|
||||
let mut cursor = self.fragments.cursor::<FragmentIdRef, ()>();
|
||||
|
||||
let mut fragments_cursor = self.fragments.cursor::<FragmentIdRef, ()>();
|
||||
let mut visible_text_cursor = self.visible_text.cursor(0);
|
||||
let mut deleted_text_cursor = self.deleted_text.cursor(0);
|
||||
|
||||
if edit.start_id == edit.end_id && edit.start_offset == edit.end_offset {
|
||||
let splits = &self.insertion_splits[&undo.edit_id];
|
||||
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 =
|
||||
fragments_cursor.slice(&FragmentIdRef::new(first_split_id), SeekBias::Left, &());
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_deleted_text
|
||||
.append(deleted_text_cursor.slice(new_fragments.summary().text.deleted));
|
||||
|
||||
loop {
|
||||
let mut fragment = cursor.item().unwrap().clone();
|
||||
let mut fragment = fragments_cursor.item().unwrap().clone();
|
||||
let was_visible = fragment.visible;
|
||||
fragment.visible = fragment.is_visible(&self.undo_map);
|
||||
fragment.max_undos.observe(undo.id);
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
if fragment.visible != was_visible {
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_deleted_text
|
||||
.append(deleted_text_cursor.slice(new_fragments.summary().text.deleted));
|
||||
}
|
||||
|
||||
if fragment.visible && !was_visible {
|
||||
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,
|
||||
&new_deleted_text.slice(visible_range.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
deleted_text_cursor
|
||||
.slice(new_fragments.summary().text.deleted + fragment.len()),
|
||||
);
|
||||
new_deleted_text.remove(visible_range);
|
||||
} else if !fragment.visible && was_visible {
|
||||
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.clone()).to_string(),
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + fragment.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
fragments_cursor.next();
|
||||
if let Some(split_id) = insertion_splits.next() {
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()),
|
||||
fragments_cursor.slice(&FragmentIdRef::new(split_id), SeekBias::Left, &()),
|
||||
&(),
|
||||
);
|
||||
} else {
|
||||
|
@ -1344,9 +1299,12 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
new_fragments =
|
||||
cursor.slice(&FragmentIdRef::new(&start_fragment_id), SeekBias::Left, &());
|
||||
while let Some(fragment) = cursor.item() {
|
||||
new_fragments = fragments_cursor.slice(
|
||||
&FragmentIdRef::new(&start_fragment_id),
|
||||
SeekBias::Left,
|
||||
&(),
|
||||
);
|
||||
while let Some(fragment) = fragments_cursor.item() {
|
||||
if fragment.id > end_fragment_id {
|
||||
break;
|
||||
} else {
|
||||
|
@ -1358,37 +1316,42 @@ impl Buffer {
|
|||
fragment.visible = fragment.is_visible(&self.undo_map);
|
||||
fragment.max_undos.observe(undo.id);
|
||||
|
||||
// TODO: avoid calling to_string on rope slice.
|
||||
if fragment.visible != was_visible {
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
}
|
||||
|
||||
if fragment.visible && !was_visible {
|
||||
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,
|
||||
&new_deleted_text.slice(visible_range.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
deleted_text_cursor
|
||||
.slice(new_fragments.summary().text.deleted + fragment.len()),
|
||||
);
|
||||
new_deleted_text.remove(visible_range);
|
||||
} else if !fragment.visible && was_visible {
|
||||
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.clone()).to_string(),
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + fragment.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
new_fragments.push(fragment, &());
|
||||
cursor.next();
|
||||
fragments_cursor.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_fragments.push_tree(cursor.suffix(&()), &());
|
||||
drop(cursor);
|
||||
new_fragments.push_tree(fragments_cursor.suffix(&()), &());
|
||||
new_visible_text.append(visible_text_cursor.suffix());
|
||||
new_deleted_text.append(deleted_text_cursor.suffix());
|
||||
drop(fragments_cursor);
|
||||
|
||||
self.fragments = new_fragments;
|
||||
self.visible_text = new_visible_text;
|
||||
self.deleted_text = new_deleted_text;
|
||||
self.fragments = new_fragments;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1470,15 +1433,23 @@ impl Buffer {
|
|||
let mut ops = Vec::with_capacity(old_ranges.size_hint().0);
|
||||
|
||||
let old_fragments = self.fragments.clone();
|
||||
let mut cursor = old_fragments.cursor::<usize, usize>();
|
||||
let old_visible_text = self.visible_text.clone();
|
||||
let old_deleted_text = self.deleted_text.clone();
|
||||
|
||||
let mut fragments_cursor = old_fragments.cursor::<usize, usize>();
|
||||
let mut visible_text_cursor = old_visible_text.cursor(0);
|
||||
let mut deleted_text_cursor = old_deleted_text.cursor(0);
|
||||
|
||||
let mut new_fragments = SumTree::new();
|
||||
let mut new_visible_text = self.visible_text.clone();
|
||||
let mut new_deleted_text = self.deleted_text.clone();
|
||||
let mut new_visible_text = Rope::new();
|
||||
let mut new_deleted_text = Rope::new();
|
||||
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
|
||||
fragments_cursor.slice(&cur_range.as_ref().unwrap().start, SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
new_visible_text.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_deleted_text.append(deleted_text_cursor.slice(new_fragments.summary().text.deleted));
|
||||
|
||||
let mut start_id = None;
|
||||
let mut start_offset = None;
|
||||
|
@ -1489,10 +1460,10 @@ impl Buffer {
|
|||
let mut local_timestamp = self.local_clock.tick();
|
||||
let mut lamport_timestamp = self.lamport_clock.tick();
|
||||
|
||||
while cur_range.is_some() && cursor.item().is_some() {
|
||||
let mut fragment = cursor.item().unwrap().clone();
|
||||
let fragment_summary = cursor.item_summary().unwrap();
|
||||
let mut fragment_start = *cursor.start();
|
||||
while cur_range.is_some() && fragments_cursor.item().is_some() {
|
||||
let mut fragment = fragments_cursor.item().unwrap().clone();
|
||||
let fragment_summary = fragments_cursor.item_summary().unwrap();
|
||||
let mut fragment_start = *fragments_cursor.start();
|
||||
let mut fragment_end = fragment_start + fragment.visible_len();
|
||||
|
||||
let old_split_tree = self
|
||||
|
@ -1546,8 +1517,12 @@ impl Buffer {
|
|||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
|
||||
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_fragments.push(new_fragment, &());
|
||||
new_visible_text.push(&new_text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1559,18 +1534,20 @@ impl Buffer {
|
|||
prefix.id =
|
||||
FragmentId::between(&new_fragments.last().unwrap().id, &fragment.id);
|
||||
version_in_range.observe_all(&fragment_summary.max_version);
|
||||
if fragment.visible {
|
||||
if prefix.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.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + prefix.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
fragment.range_in_insertion.start = prefix.range_in_insertion.end;
|
||||
new_fragments.push(prefix.clone(), &());
|
||||
|
@ -1592,14 +1569,16 @@ impl Buffer {
|
|||
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.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor
|
||||
.slice(new_fragments.summary().text.visible + fragment.len()),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1651,11 +1630,11 @@ impl Buffer {
|
|||
new_fragments.push(fragment, &());
|
||||
|
||||
// Scan forward until we find a fragment that is not fully contained by the current splice.
|
||||
cursor.next();
|
||||
fragments_cursor.next();
|
||||
if let Some(range) = cur_range.clone() {
|
||||
while let Some(fragment) = cursor.item() {
|
||||
let fragment_summary = cursor.item_summary().unwrap();
|
||||
fragment_start = *cursor.start();
|
||||
while let Some(fragment) = fragments_cursor.item() {
|
||||
let fragment_summary = fragments_cursor.item_summary().unwrap();
|
||||
fragment_start = *fragments_cursor.start();
|
||||
fragment_end = fragment_start + fragment.visible_len();
|
||||
if range.start < fragment_start && range.end >= fragment_end {
|
||||
let mut new_fragment = fragment.clone();
|
||||
|
@ -1664,17 +1643,20 @@ impl Buffer {
|
|||
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.clone()).to_string(),
|
||||
new_visible_text.append(
|
||||
visible_text_cursor.slice(new_fragments.summary().text.visible),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
deleted_text_cursor.slice(new_fragments.summary().text.deleted),
|
||||
);
|
||||
new_deleted_text.append(
|
||||
visible_text_cursor.slice(
|
||||
new_fragments.summary().text.visible + new_fragment.len(),
|
||||
),
|
||||
);
|
||||
new_visible_text.remove(deleted_range);
|
||||
}
|
||||
new_fragments.push(new_fragment, &());
|
||||
cursor.next();
|
||||
fragments_cursor.next();
|
||||
|
||||
if range.end == fragment_end {
|
||||
end_id = Some(fragment.insertion.id);
|
||||
|
@ -1715,7 +1697,11 @@ 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, &()),
|
||||
fragments_cursor.slice(
|
||||
&cur_range.as_ref().unwrap().start,
|
||||
SeekBias::Right,
|
||||
&(),
|
||||
),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
@ -1749,19 +1735,21 @@ impl Buffer {
|
|||
local_timestamp,
|
||||
lamport_timestamp,
|
||||
);
|
||||
new_visible_text.insert(new_fragments.summary().text.visible, &new_text);
|
||||
|
||||
new_visible_text
|
||||
.append(visible_text_cursor.slice(new_fragments.summary().text.visible));
|
||||
new_fragments.push(new_fragment, &());
|
||||
new_visible_text.push(&new_text);
|
||||
}
|
||||
} else {
|
||||
new_fragments.push_tree(
|
||||
cursor.slice(&old_fragments.extent::<usize>(), SeekBias::Right, &()),
|
||||
&(),
|
||||
);
|
||||
}
|
||||
|
||||
new_fragments.push_tree(fragments_cursor.suffix(&()), &());
|
||||
new_visible_text.append(visible_text_cursor.suffix());
|
||||
new_deleted_text.append(deleted_text_cursor.suffix());
|
||||
|
||||
self.fragments = new_fragments;
|
||||
self.visible_text = new_visible_text;
|
||||
self.deleted_text = new_deleted_text;
|
||||
self.fragments = new_fragments;
|
||||
ops
|
||||
}
|
||||
|
||||
|
@ -2031,9 +2019,9 @@ impl Buffer {
|
|||
impl Clone for Buffer {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
fragments: self.fragments.clone(),
|
||||
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(),
|
||||
saved_version: self.saved_version.clone(),
|
||||
|
@ -2409,12 +2397,7 @@ pub trait ToOffset {
|
|||
|
||||
impl ToOffset for Point {
|
||||
fn to_offset(&self, buffer: &Buffer) -> Result<usize> {
|
||||
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"))
|
||||
}
|
||||
buffer.visible_text.to_offset(*self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2448,13 +2431,7 @@ impl ToPoint for Anchor {
|
|||
|
||||
impl ToPoint for usize {
|
||||
fn to_point(&self, buffer: &Buffer) -> Result<Point> {
|
||||
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"))
|
||||
}
|
||||
buffer.visible_text.to_point(*self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,14 @@ impl Rope {
|
|||
self.chunks.summary()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.chunks.extent()
|
||||
}
|
||||
|
||||
pub fn max_point(&self) -> Point {
|
||||
self.chunks.extent()
|
||||
}
|
||||
|
||||
pub fn cursor(&self, offset: usize) -> Cursor {
|
||||
Cursor::new(self, offset)
|
||||
}
|
||||
|
@ -116,15 +124,7 @@ impl Rope {
|
|||
self.chunks.cursor::<(), ()>().map(|c| c.0.as_str())
|
||||
}
|
||||
|
||||
fn text(&self) -> String {
|
||||
let mut text = String::new();
|
||||
for chunk in self.chunks.cursor::<(), ()>() {
|
||||
text.push_str(&chunk.0);
|
||||
}
|
||||
text
|
||||
}
|
||||
|
||||
fn to_point(&self, offset: usize) -> Result<Point> {
|
||||
pub fn to_point(&self, offset: usize) -> Result<Point> {
|
||||
if offset <= self.summary().chars {
|
||||
let mut cursor = self.chunks.cursor::<usize, TextSummary>();
|
||||
cursor.seek(&offset, SeekBias::Left, &());
|
||||
|
@ -135,7 +135,8 @@ impl Rope {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_offset(&self, point: Point) -> Result<usize> {
|
||||
pub fn to_offset(&self, point: Point) -> Result<usize> {
|
||||
// TODO: Verify the point actually exists.
|
||||
if point <= self.summary().lines {
|
||||
let mut cursor = self.chunks.cursor::<Point, TextSummary>();
|
||||
cursor.seek(&point, SeekBias::Left, &());
|
||||
|
@ -162,7 +163,7 @@ pub struct Cursor<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
fn new(rope: &'a Rope, offset: usize) -> Self {
|
||||
pub fn new(rope: &'a Rope, offset: usize) -> Self {
|
||||
let mut chunks = rope.chunks.cursor();
|
||||
chunks.seek(&offset, SeekBias::Right, &());
|
||||
Self {
|
||||
|
@ -172,14 +173,14 @@ impl<'a> Cursor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn seek_forward(&mut self, end_offset: usize) {
|
||||
pub fn seek_forward(&mut self, end_offset: usize) {
|
||||
debug_assert!(end_offset >= self.offset);
|
||||
|
||||
self.chunks.seek_forward(&end_offset, SeekBias::Right, &());
|
||||
self.offset = end_offset;
|
||||
}
|
||||
|
||||
fn slice(&mut self, end_offset: usize) -> Rope {
|
||||
pub fn slice(&mut self, end_offset: usize) -> Rope {
|
||||
debug_assert!(end_offset >= self.offset);
|
||||
|
||||
let mut slice = Rope::new();
|
||||
|
@ -203,7 +204,7 @@ impl<'a> Cursor<'a> {
|
|||
slice
|
||||
}
|
||||
|
||||
fn suffix(mut self) -> Rope {
|
||||
pub fn suffix(mut self) -> Rope {
|
||||
self.slice(self.rope.chunks.extent())
|
||||
}
|
||||
}
|
||||
|
@ -456,4 +457,14 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rope {
|
||||
fn text(&self) -> String {
|
||||
let mut text = String::new();
|
||||
for chunk in self.chunks.cursor::<(), ()>() {
|
||||
text.push_str(&chunk.0);
|
||||
}
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ use super::{
|
|||
Anchor, Buffer, DisplayPoint, Edit, Point, ToOffset,
|
||||
};
|
||||
use crate::{
|
||||
editor::rope,
|
||||
sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
|
||||
time,
|
||||
};
|
||||
|
@ -607,7 +608,7 @@ pub struct Chars<'a> {
|
|||
cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
|
||||
offset: usize,
|
||||
buffer: &'a Buffer,
|
||||
buffer_chars: Option<Take<ropey::iter::Chars<'a>>>,
|
||||
buffer_chars: Option<Take<rope::Chars<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Chars<'a> {
|
||||
|
|
|
@ -3,7 +3,7 @@ mod fuzzy;
|
|||
mod ignore;
|
||||
|
||||
use crate::{
|
||||
editor::History,
|
||||
editor::{History, Rope},
|
||||
sum_tree::{self, Cursor, Edit, SeekBias, SumTree},
|
||||
};
|
||||
use ::ignore::gitignore::Gitignore;
|
||||
|
@ -16,7 +16,6 @@ use postage::{
|
|||
prelude::{Sink, Stream},
|
||||
watch,
|
||||
};
|
||||
use ropey::Rope;
|
||||
use smol::channel::Sender;
|
||||
use std::{
|
||||
cmp,
|
||||
|
@ -204,7 +203,7 @@ impl Worktree {
|
|||
let path = path.to_path_buf();
|
||||
let abs_path = self.absolutize(&path);
|
||||
ctx.background_executor().spawn(async move {
|
||||
let buffer_size = content.len_bytes().min(10 * 1024);
|
||||
let buffer_size = content.summary().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.chunks() {
|
||||
|
|
Loading…
Reference in a new issue