Introduce FullOffset type

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
Co-Authored-By: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Max Brunsfeld 2021-10-27 11:02:12 -07:00 committed by Nathan Sobo
parent bc076c1cc1
commit 0c10d6c82d
2 changed files with 154 additions and 98 deletions

View file

@ -1,3 +1,5 @@
use crate::FullOffset;
use super::{Buffer, Content, FromAnchor, Point, ToOffset};
use anyhow::Result;
use std::{cmp::Ordering, ops::Range};
@ -5,7 +7,7 @@ use sum_tree::{Bias, SumTree};
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub struct Anchor {
pub full_offset: usize,
pub full_offset: FullOffset,
pub bias: Bias,
pub version: clock::Global,
}
@ -13,7 +15,7 @@ pub struct Anchor {
#[derive(Clone)]
pub struct AnchorMap<T> {
pub(crate) version: clock::Global,
pub(crate) entries: Vec<((usize, Bias), T)>,
pub(crate) entries: Vec<((FullOffset, Bias), T)>,
}
#[derive(Clone)]
@ -22,7 +24,7 @@ pub struct AnchorSet(pub(crate) AnchorMap<()>);
#[derive(Clone)]
pub struct AnchorRangeMap<T> {
pub(crate) version: clock::Global,
pub(crate) entries: Vec<(Range<(usize, Bias)>, T)>,
pub(crate) entries: Vec<(Range<(FullOffset, Bias)>, T)>,
}
#[derive(Clone)]
@ -44,23 +46,23 @@ pub(crate) struct AnchorRangeMultimapEntry<T> {
#[derive(Clone, Debug)]
pub(crate) struct FullOffsetRange {
pub(crate) start: usize,
pub(crate) end: usize,
pub(crate) start: FullOffset,
pub(crate) end: FullOffset,
}
#[derive(Clone, Debug)]
pub(crate) struct AnchorRangeMultimapSummary {
start: usize,
end: usize,
min_start: usize,
max_end: usize,
start: FullOffset,
end: FullOffset,
min_start: FullOffset,
max_end: FullOffset,
count: usize,
}
impl Anchor {
pub fn min() -> Self {
Self {
full_offset: 0,
full_offset: FullOffset(0),
bias: Bias::Left,
version: Default::default(),
}
@ -68,7 +70,7 @@ impl Anchor {
pub fn max() -> Self {
Self {
full_offset: usize::MAX,
full_offset: FullOffset::MAX,
bias: Bias::Right,
version: Default::default(),
}
@ -192,7 +194,7 @@ impl<T: Clone> AnchorRangeMultimap<T> {
{
let content = content.clone();
let mut endpoint = Anchor {
full_offset: 0,
full_offset: FullOffset(0),
bias: Bias::Right,
version: self.version.clone(),
};
@ -219,7 +221,7 @@ impl<T: Clone> AnchorRangeMultimap<T> {
std::iter::from_fn({
let mut endpoint = Anchor {
full_offset: 0,
full_offset: FullOffset(0),
bias: Bias::Left,
version: self.version.clone(),
};
@ -260,10 +262,10 @@ impl<T: Clone> sum_tree::Item for AnchorRangeMultimapEntry<T> {
impl Default for AnchorRangeMultimapSummary {
fn default() -> Self {
Self {
start: 0,
end: usize::MAX,
min_start: usize::MAX,
max_end: 0,
start: FullOffset(0),
end: FullOffset::MAX,
min_start: FullOffset::MAX,
max_end: FullOffset(0),
count: 0,
}
}
@ -294,8 +296,8 @@ impl sum_tree::Summary for AnchorRangeMultimapSummary {
impl Default for FullOffsetRange {
fn default() -> Self {
Self {
start: 0,
end: usize::MAX,
start: FullOffset(0),
end: FullOffset::MAX,
}
}
}

View file

@ -22,7 +22,7 @@ use std::{
cmp::{self, Reverse},
convert::{TryFrom, TryInto},
iter::Iterator,
ops::Range,
ops::{self, Range},
str,
sync::Arc,
time::{Duration, Instant},
@ -84,7 +84,7 @@ pub struct Transaction {
start: clock::Global,
end: clock::Global,
edits: Vec<clock::Local>,
ranges: Vec<Range<usize>>,
ranges: Vec<Range<FullOffset>>,
selections_before: HashMap<SelectionSetId, Arc<[Selection]>>,
selections_after: HashMap<SelectionSetId, Arc<[Selection]>>,
first_edit_at: Instant,
@ -101,7 +101,7 @@ impl Transaction {
self.end.observe(edit.timestamp.local());
let mut other_ranges = edit.ranges.iter().peekable();
let mut new_ranges: Vec<Range<usize>> = Vec::new();
let mut new_ranges = Vec::new();
let insertion_len = edit.new_text.as_ref().map_or(0, |t| t.len());
let mut delta = 0;
@ -429,7 +429,7 @@ pub enum Operation {
pub struct EditOperation {
timestamp: InsertionTimestamp,
version: clock::Global,
ranges: Vec<Range<usize>>,
ranges: Vec<Range<FullOffset>>,
new_text: Option<String>,
}
@ -437,7 +437,7 @@ pub struct EditOperation {
pub struct UndoOperation {
id: clock::Local,
counts: HashMap<clock::Local, u32>,
ranges: Vec<Range<usize>>,
ranges: Vec<Range<FullOffset>>,
version: clock::Global,
}
@ -735,7 +735,7 @@ impl Buffer {
fragment_start = old_fragments.start().visible;
}
let full_range_start = range.start + old_fragments.start().deleted;
let full_range_start = FullOffset(range.start + old_fragments.start().deleted);
// Preserve any portion of the current fragment that precedes this range.
if fragment_start < range.start {
@ -783,7 +783,7 @@ impl Buffer {
}
}
let full_range_end = range.end + old_fragments.start().deleted;
let full_range_end = FullOffset(range.end + old_fragments.start().deleted);
edit.ranges.push(full_range_start..full_range_end);
}
@ -898,7 +898,7 @@ impl Buffer {
fn apply_remote_edit(
&mut self,
version: &clock::Global,
ranges: &[Range<usize>],
ranges: &[Range<FullOffset>],
new_text: Option<&str>,
timestamp: InsertionTimestamp,
) {
@ -909,24 +909,27 @@ impl Buffer {
let cx = Some(version.clone());
let mut new_ropes =
RopeBuilder::new(self.visible_text.cursor(0), self.deleted_text.cursor(0));
let mut old_fragments = self.fragments.cursor::<VersionedOffset>();
let mut new_fragments =
old_fragments.slice(&VersionedOffset::Offset(ranges[0].start), Bias::Left, &cx);
let mut old_fragments = self.fragments.cursor::<VersionedFullOffset>();
let mut new_fragments = old_fragments.slice(
&VersionedFullOffset::Offset(ranges[0].start),
Bias::Left,
&cx,
);
new_ropes.push_tree(new_fragments.summary().text);
let mut fragment_start = old_fragments.start().offset();
let mut fragment_start = old_fragments.start().full_offset();
for range in ranges {
let fragment_end = old_fragments.end(&cx).offset();
let fragment_end = old_fragments.end(&cx).full_offset();
// If the current fragment ends before this range, then jump ahead to the first fragment
// that extends past the start of this range, reusing any intervening fragments.
if fragment_end < range.start {
// If the current fragment has been partially consumed, then consume the rest of it
// and advance to the next fragment before slicing.
if fragment_start > old_fragments.start().offset() {
if fragment_start > old_fragments.start().full_offset() {
if fragment_end > fragment_start {
let mut suffix = old_fragments.item().unwrap().clone();
suffix.len = fragment_end - fragment_start;
suffix.len = fragment_end.0 - fragment_start.0;
new_ropes.push_fragment(&suffix, suffix.visible);
new_fragments.push(suffix, &None);
}
@ -934,21 +937,21 @@ impl Buffer {
}
let slice =
old_fragments.slice(&VersionedOffset::Offset(range.start), Bias::Left, &cx);
old_fragments.slice(&VersionedFullOffset::Offset(range.start), Bias::Left, &cx);
new_ropes.push_tree(slice.summary().text);
new_fragments.push_tree(slice, &None);
fragment_start = old_fragments.start().offset();
fragment_start = old_fragments.start().full_offset();
}
// If we are at the end of a non-concurrent fragment, advance to the next one.
let fragment_end = old_fragments.end(&cx).offset();
let fragment_end = old_fragments.end(&cx).full_offset();
if fragment_end == range.start && fragment_end > fragment_start {
let mut fragment = old_fragments.item().unwrap().clone();
fragment.len = fragment_end - fragment_start;
fragment.len = fragment_end.0 - fragment_start.0;
new_ropes.push_fragment(&fragment, fragment.visible);
new_fragments.push(fragment, &None);
old_fragments.next(&cx);
fragment_start = old_fragments.start().offset();
fragment_start = old_fragments.start().full_offset();
}
// Skip over insertions that are concurrent to this edit, but have a lower lamport
@ -970,7 +973,7 @@ impl Buffer {
// Preserve any portion of the current fragment that precedes this range.
if fragment_start < range.start {
let mut prefix = old_fragments.item().unwrap().clone();
prefix.len = range.start - fragment_start;
prefix.len = range.start.0 - fragment_start.0;
fragment_start = range.start;
new_ropes.push_fragment(&prefix, prefix.visible);
new_fragments.push(prefix, &None);
@ -995,11 +998,11 @@ impl Buffer {
// portions as deleted.
while fragment_start < range.end {
let fragment = old_fragments.item().unwrap();
let fragment_end = old_fragments.end(&cx).offset();
let fragment_end = old_fragments.end(&cx).full_offset();
let mut intersection = fragment.clone();
let intersection_end = cmp::min(range.end, fragment_end);
if fragment.was_visible(version, &self.undo_map) {
intersection.len = intersection_end - fragment_start;
intersection.len = intersection_end.0 - fragment_start.0;
intersection.deletions.insert(timestamp.local());
intersection.visible = false;
}
@ -1016,11 +1019,11 @@ impl Buffer {
// If the current fragment has been partially consumed, then consume the rest of it
// and advance to the next fragment before slicing.
if fragment_start > old_fragments.start().offset() {
let fragment_end = old_fragments.end(&cx).offset();
if fragment_start > old_fragments.start().full_offset() {
let fragment_end = old_fragments.end(&cx).full_offset();
if fragment_end > fragment_start {
let mut suffix = old_fragments.item().unwrap().clone();
suffix.len = fragment_end - fragment_start;
suffix.len = fragment_end.0 - fragment_start.0;
new_ropes.push_fragment(&suffix, suffix.visible);
new_fragments.push(suffix, &None);
}
@ -1049,9 +1052,9 @@ impl Buffer {
}
let cx = Some(cx);
let mut old_fragments = self.fragments.cursor::<VersionedOffset>();
let mut old_fragments = self.fragments.cursor::<VersionedFullOffset>();
let mut new_fragments = old_fragments.slice(
&VersionedOffset::Offset(undo.ranges[0].start),
&VersionedFullOffset::Offset(undo.ranges[0].start),
Bias::Right,
&cx,
);
@ -1060,11 +1063,14 @@ impl Buffer {
new_ropes.push_tree(new_fragments.summary().text);
for range in &undo.ranges {
let mut end_offset = old_fragments.end(&cx).offset();
let mut end_offset = old_fragments.end(&cx).full_offset();
if end_offset < range.start {
let preceding_fragments =
old_fragments.slice(&VersionedOffset::Offset(range.start), Bias::Right, &cx);
let preceding_fragments = old_fragments.slice(
&VersionedFullOffset::Offset(range.start),
Bias::Right,
&cx,
);
new_ropes.push_tree(preceding_fragments.summary().text);
new_fragments.push_tree(preceding_fragments, &None);
}
@ -1084,16 +1090,16 @@ impl Buffer {
new_fragments.push(fragment, &None);
old_fragments.next(&cx);
if end_offset == old_fragments.end(&cx).offset() {
if end_offset == old_fragments.end(&cx).full_offset() {
let unseen_fragments = old_fragments.slice(
&VersionedOffset::Offset(end_offset),
&VersionedFullOffset::Offset(end_offset),
Bias::Right,
&cx,
);
new_ropes.push_tree(unseen_fragments.summary().text);
new_fragments.push_tree(unseen_fragments, &None);
}
end_offset = old_fragments.end(&cx).offset();
end_offset = old_fragments.end(&cx).full_offset();
} else {
break;
}
@ -1698,14 +1704,14 @@ impl<'a> Content<'a> {
fn summary_for_anchor(&self, anchor: &Anchor) -> TextSummary {
let cx = Some(anchor.version.clone());
let mut cursor = self.fragments.cursor::<(VersionedOffset, usize)>();
let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
cursor.seek(
&VersionedOffset::Offset(anchor.full_offset),
&VersionedFullOffset::Offset(anchor.full_offset),
anchor.bias,
&cx,
);
let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
anchor.full_offset - cursor.start().0.offset()
anchor.full_offset - cursor.start().0.full_offset()
} else {
0
};
@ -1723,11 +1729,11 @@ impl<'a> Content<'a> {
let cx = Some(map.version.clone());
let mut summary = TextSummary::default();
let mut rope_cursor = self.visible_text.cursor(0);
let mut cursor = self.fragments.cursor::<(VersionedOffset, usize)>();
let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
map.entries.iter().map(move |((offset, bias), value)| {
cursor.seek_forward(&VersionedOffset::Offset(*offset), *bias, &cx);
cursor.seek_forward(&VersionedFullOffset::Offset(*offset), *bias, &cx);
let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
offset - cursor.start().0.offset()
*offset - cursor.start().0.full_offset()
} else {
0
};
@ -1743,25 +1749,29 @@ impl<'a> Content<'a> {
let cx = Some(map.version.clone());
let mut summary = TextSummary::default();
let mut rope_cursor = self.visible_text.cursor(0);
let mut cursor = self.fragments.cursor::<(VersionedOffset, usize)>();
let mut cursor = self.fragments.cursor::<(VersionedFullOffset, usize)>();
map.entries.iter().map(move |(range, value)| {
let Range {
start: (start_offset, start_bias),
end: (end_offset, end_bias),
} = range;
cursor.seek_forward(&VersionedOffset::Offset(*start_offset), *start_bias, &cx);
cursor.seek_forward(
&VersionedFullOffset::Offset(*start_offset),
*start_bias,
&cx,
);
let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
start_offset - cursor.start().0.offset()
*start_offset - cursor.start().0.full_offset()
} else {
0
};
summary += rope_cursor.summary(cursor.start().1 + overshoot);
let start_summary = summary.clone();
cursor.seek_forward(&VersionedOffset::Offset(*end_offset), *end_bias, &cx);
cursor.seek_forward(&VersionedFullOffset::Offset(*end_offset), *end_bias, &cx);
let overshoot = if cursor.item().map_or(false, |fragment| fragment.visible) {
end_offset - cursor.start().0.offset()
*end_offset - cursor.start().0.full_offset()
} else {
0
};
@ -1790,7 +1800,7 @@ impl<'a> Content<'a> {
.into_iter()
.map(|((offset, bias), value)| {
cursor.seek_forward(&offset, bias, &None);
let full_offset = cursor.start().deleted + offset;
let full_offset = FullOffset(cursor.start().deleted + offset);
((full_offset, bias), value)
})
.collect();
@ -1812,9 +1822,9 @@ impl<'a> Content<'a> {
end: (end_offset, end_bias),
} = range;
cursor.seek_forward(&start_offset, start_bias, &None);
let full_start_offset = cursor.start().deleted + start_offset;
let full_start_offset = FullOffset(cursor.start().deleted + start_offset);
cursor.seek_forward(&end_offset, end_bias, &None);
let full_end_offset = cursor.start().deleted + end_offset;
let full_end_offset = FullOffset(cursor.start().deleted + end_offset);
(
(full_start_offset, start_bias)..(full_end_offset, end_bias),
value,
@ -1869,23 +1879,23 @@ impl<'a> Content<'a> {
}
}
fn full_offset_for_anchor(&self, anchor: &Anchor) -> usize {
fn full_offset_for_anchor(&self, anchor: &Anchor) -> FullOffset {
let cx = Some(anchor.version.clone());
let mut cursor = self
.fragments
.cursor::<(VersionedOffset, FragmentTextSummary)>();
.cursor::<(VersionedFullOffset, FragmentTextSummary)>();
cursor.seek(
&VersionedOffset::Offset(anchor.full_offset),
&VersionedFullOffset::Offset(anchor.full_offset),
anchor.bias,
&cx,
);
let overshoot = if cursor.item().is_some() {
anchor.full_offset - cursor.start().0.offset()
anchor.full_offset - cursor.start().0.full_offset()
} else {
0
};
let summary = cursor.start().1;
summary.visible + summary.deleted + overshoot
FullOffset(summary.visible + summary.deleted + overshoot)
}
fn point_for_offset(&self, offset: usize) -> Result<Point> {
@ -2118,12 +2128,56 @@ impl Default for FragmentSummary {
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FullOffset(usize);
impl FullOffset {
const MAX: Self = FullOffset(usize::MAX);
fn to_proto(self) -> u64 {
self.0 as u64
}
fn from_proto(value: u64) -> Self {
Self(value as usize)
}
}
impl ops::AddAssign<usize> for FullOffset {
fn add_assign(&mut self, rhs: usize) {
self.0 += rhs;
}
}
impl ops::Add<usize> for FullOffset {
type Output = Self;
fn add(mut self, rhs: usize) -> Self::Output {
self += rhs;
self
}
}
impl ops::Sub for FullOffset {
type Output = usize;
fn sub(self, rhs: Self) -> Self::Output {
self.0 - rhs.0
}
}
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for usize {
fn add_summary(&mut self, summary: &FragmentSummary, _: &Option<clock::Global>) {
*self += summary.text.visible;
}
}
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FullOffset {
fn add_summary(&mut self, summary: &FragmentSummary, _: &Option<clock::Global>) {
self.0 += summary.text.visible + summary.text.deleted;
}
}
impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, FragmentTextSummary> for usize {
fn cmp(
&self,
@ -2135,28 +2189,28 @@ impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, FragmentTextSummary> for usiz
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum VersionedOffset {
Offset(usize),
InvalidVersion,
enum VersionedFullOffset {
Offset(FullOffset),
Invalid,
}
impl VersionedOffset {
fn offset(&self) -> usize {
if let Self::Offset(offset) = self {
*offset
impl VersionedFullOffset {
fn full_offset(&self) -> FullOffset {
if let Self::Offset(position) = self {
*position
} else {
panic!("invalid version")
}
}
}
impl Default for VersionedOffset {
impl Default for VersionedFullOffset {
fn default() -> Self {
Self::Offset(0)
Self::Offset(Default::default())
}
}
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedOffset {
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedFullOffset {
fn add_summary(&mut self, summary: &'a FragmentSummary, cx: &Option<clock::Global>) {
if let Self::Offset(offset) = self {
let version = cx.as_ref().unwrap();
@ -2167,18 +2221,18 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedOffset {
.iter()
.all(|t| !version.observed(*t))
{
*self = Self::InvalidVersion;
*self = Self::Invalid;
}
}
}
}
impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, Self> for VersionedOffset {
fn cmp(&self, other: &Self, _: &Option<clock::Global>) -> cmp::Ordering {
match (self, other) {
impl<'a> sum_tree::SeekTarget<'a, FragmentSummary, Self> for VersionedFullOffset {
fn cmp(&self, cursor_position: &Self, _: &Option<clock::Global>) -> cmp::Ordering {
match (self, cursor_position) {
(Self::Offset(a), Self::Offset(b)) => Ord::cmp(a, b),
(Self::Offset(_), Self::InvalidVersion) => cmp::Ordering::Less,
(Self::InvalidVersion, _) => unreachable!(),
(Self::Offset(_), Self::Invalid) => cmp::Ordering::Less,
(Self::Invalid, _) => unreachable!(),
}
}
}
@ -2229,8 +2283,8 @@ impl<'a> Into<proto::Operation> for &'a Operation {
.ranges
.iter()
.map(|r| proto::Range {
start: r.start as u64,
end: r.end as u64,
start: r.start.to_proto(),
end: r.end.to_proto(),
})
.collect(),
counts: undo
@ -2281,8 +2335,8 @@ impl<'a> Into<proto::operation::Edit> for &'a EditOperation {
.ranges
.iter()
.map(|range| proto::Range {
start: range.start as u64,
end: range.end as u64,
start: range.start.to_proto(),
end: range.end.to_proto(),
})
.collect();
proto::operation::Edit {
@ -2300,7 +2354,7 @@ impl<'a> Into<proto::Anchor> for &'a Anchor {
fn into(self) -> proto::Anchor {
proto::Anchor {
version: (&self.version).into(),
offset: self.full_offset as u64,
offset: self.full_offset.to_proto(),
bias: match self.bias {
Bias::Left => proto::anchor::Bias::Left as i32,
Bias::Right => proto::anchor::Bias::Right as i32,
@ -2356,7 +2410,7 @@ impl TryFrom<proto::Operation> for Operation {
ranges: undo
.ranges
.into_iter()
.map(|r| r.start as usize..r.end as usize)
.map(|r| FullOffset::from_proto(r.start)..FullOffset::from_proto(r.end))
.collect(),
version: undo.version.into(),
},
@ -2406,7 +2460,7 @@ impl From<proto::operation::Edit> for EditOperation {
let ranges = edit
.ranges
.into_iter()
.map(|range| range.start as usize..range.end as usize)
.map(|range| FullOffset::from_proto(range.start)..FullOffset::from_proto(range.end))
.collect();
EditOperation {
timestamp: InsertionTimestamp {
@ -2434,7 +2488,7 @@ impl TryFrom<proto::Anchor> for Anchor {
}
Ok(Self {
full_offset: message.offset as usize,
full_offset: FullOffset::from_proto(message.offset),
bias: if message.bias == proto::anchor::Bias::Left as i32 {
Bias::Left
} else if message.bias == proto::anchor::Bias::Right as i32 {
@ -2470,12 +2524,12 @@ impl TryFrom<proto::Selection> for Selection {
pub trait ToOffset {
fn to_offset<'a>(&self, content: impl Into<Content<'a>>) -> usize;
fn to_full_offset<'a>(&self, content: impl Into<Content<'a>>, bias: Bias) -> usize {
fn to_full_offset<'a>(&self, content: impl Into<Content<'a>>, bias: Bias) -> FullOffset {
let content = content.into();
let offset = self.to_offset(&content);
let mut cursor = content.fragments.cursor::<FragmentTextSummary>();
cursor.seek(&offset, bias, &None);
offset + cursor.start().deleted
FullOffset(offset + cursor.start().deleted)
}
}