Skip to content
This repository has been archived by the owner on Jul 23, 2019. It is now read-only.

Optimize cursor movement and selection manipulation #45

Merged
merged 5 commits into from Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions xray_core/Cargo.toml
Expand Up @@ -6,6 +6,7 @@ license = "MIT"

[dependencies]
futures = "0.1"
lazy_static = "1.0"

[dev-dependencies]
rand = "0.3"
Expand Down
176 changes: 104 additions & 72 deletions xray_core/src/buffer.rs
@@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::iter;
Expand Down Expand Up @@ -25,22 +26,24 @@ pub struct Buffer {
lamport_clock: LamportTimestamp,
fragments: Tree<Fragment>,
insertions: HashMap<ChangeId, Tree<FragmentMapping>>,
anchor_cache: RefCell<HashMap<Anchor, (usize, Point)>>,
offset_cache: RefCell<HashMap<Point, usize>>,
pub version: NotifyCell<Version>
}

#[derive(Clone, Copy, Debug)]
pub struct Version(LocalTimestamp);

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct Point {
pub row: u32,
pub column: u32
}

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub struct Anchor(AnchorInner);

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
enum AnchorInner {
Start,
End,
Expand All @@ -51,7 +54,7 @@ enum AnchorInner {
}
}

#[derive(Clone, Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
enum AnchorBias {
Left,
Right
Expand Down Expand Up @@ -85,7 +88,7 @@ struct ChangeId {
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
struct FragmentId(Vec<u16>);
struct FragmentId(Arc<Vec<u16>>);

#[derive(Eq, PartialEq, Clone, Debug)]
struct Fragment {
Expand Down Expand Up @@ -144,6 +147,8 @@ impl Buffer {
lamport_clock: 0,
fragments,
insertions: HashMap::new(),
anchor_cache: RefCell::new(HashMap::new()),
offset_cache: RefCell::new(HashMap::new()),
version: NotifyCell::new(Version(0))
}
}
Expand Down Expand Up @@ -192,6 +197,8 @@ impl Buffer {
local_timestamp: self.local_clock
};
self.splice_fragments(change_id, old_range, new_text);
self.anchor_cache.borrow_mut().clear();
self.offset_cache.borrow_mut().clear();
self.version.set(Version(self.local_clock));
}
}
Expand Down Expand Up @@ -386,12 +393,16 @@ impl Buffer {
let mut cursor = self.fragments.cursor();
cursor.seek(&CharacterCount(offset), seek_bias);
let fragment = cursor.item().unwrap();

Ok(Anchor(AnchorInner::Middle {
let offset_in_fragment = offset - cursor.start::<CharacterCount>().0;
let offset_in_insertion = fragment.start_offset + offset_in_fragment;
let point = cursor.start::<Point>() + &fragment.point_for_offset(offset_in_fragment)?;
let anchor = Anchor(AnchorInner::Middle {
insertion_id: fragment.insertion.id,
offset: offset - cursor.start::<CharacterCount>().0,
offset: offset_in_insertion,
bias
}))
});
self.cache_position(Some(anchor.clone()), offset, point);
Ok(anchor)
}

pub fn anchor_before_point(&self, point: Point) -> Result<Anchor> {
Expand Down Expand Up @@ -429,89 +440,104 @@ impl Buffer {
let mut cursor = self.fragments.cursor();
cursor.seek(&point, seek_bias);
let fragment = cursor.item().unwrap();

Ok(Anchor(AnchorInner::Middle {
let offset_in_fragment = fragment.offset_for_point(point - &cursor.start::<Point>())?;
let offset_in_insertion = fragment.start_offset + offset_in_fragment;
let anchor = Anchor(AnchorInner::Middle {
insertion_id: fragment.insertion.id,
offset: fragment.offset_for_point(point - &cursor.start::<Point>())?,
offset: offset_in_insertion,
bias
}))
});
let offset = cursor.start::<CharacterCount>().0 + offset_in_fragment;
self.cache_position(Some(anchor.clone()), offset, point);
Ok(anchor)
}

pub fn offset_for_anchor(&self, anchor: &Anchor) -> Result<usize> {
match &anchor.0 {
&AnchorInner::Start => Ok(0),
&AnchorInner::End => Ok(self.len()),
&AnchorInner::Middle { ref insertion_id, offset, ref bias } => {
let seek_bias = match bias {
&AnchorBias::Left => SeekBias::Left,
&AnchorBias::Right => SeekBias::Right,
};

let splits = self.insertions.get(&insertion_id).ok_or(Error::InvalidAnchor)?;
let mut splits_cursor = splits.cursor();
splits_cursor.seek(&InsertionOffset(offset), seek_bias);

splits_cursor.item().and_then(|split| {
let mut fragments_cursor = self.fragments.cursor();
fragments_cursor.seek(&split.fragment_id, SeekBias::Left);

fragments_cursor.item().map(|fragment| {
let overshoot = if fragment.is_visible() {
offset - fragment.start_offset
} else {
0
};
fragments_cursor.start::<CharacterCount>().0 + overshoot
})
}).ok_or(Error::InvalidAnchor)
}
}
Ok(self.position_for_anchor(anchor)?.0)
}

pub fn point_for_anchor(&self, anchor: &Anchor) -> Result<Point> {
Ok(self.position_for_anchor(anchor)?.1)
}

fn position_for_anchor(&self, anchor: &Anchor) -> Result<(usize, Point)> {
match &anchor.0 {
&AnchorInner::Start => Ok(Point {row: 0, column: 0}),
&AnchorInner::End => Ok(self.fragments.len::<Point>()),
&AnchorInner::Start => Ok((0, Point {row: 0, column: 0})),
&AnchorInner::End => Ok((self.len(), self.fragments.len::<Point>())),
&AnchorInner::Middle { ref insertion_id, offset, ref bias } => {
let seek_bias = match bias {
&AnchorBias::Left => SeekBias::Left,
&AnchorBias::Right => SeekBias::Right,
let cached_position = {
let anchor_cache = self.anchor_cache.try_borrow().ok();
anchor_cache.as_ref().and_then(|cache| cache.get(anchor).cloned())
};

let splits = self.insertions.get(&insertion_id).ok_or(Error::InvalidAnchor)?;
let mut splits_cursor = splits.cursor();
splits_cursor.seek(&InsertionOffset(offset), seek_bias);
splits_cursor.item().ok_or(Error::InvalidAnchor).and_then(|split| {
let mut fragments_cursor = self.fragments.cursor();
fragments_cursor.seek(&split.fragment_id, SeekBias::Left);
fragments_cursor.item().ok_or(Error::InvalidAnchor).and_then(|fragment| {
let overshoot = if fragment.is_visible() {
fragment.point_for_offset(offset)?
} else {
Point {row: 0, column: 0}
};

Ok(fragments_cursor.start::<Point>() + &overshoot)
if let Some(cached_position) = cached_position {
Ok(cached_position)
} else {
let seek_bias = match bias {
&AnchorBias::Left => SeekBias::Left,
&AnchorBias::Right => SeekBias::Right,
};

let splits = self.insertions.get(&insertion_id).ok_or(Error::InvalidAnchor)?;
let mut splits_cursor = splits.cursor();
splits_cursor.seek(&InsertionOffset(offset), seek_bias);
splits_cursor.item().ok_or(Error::InvalidAnchor).and_then(|split| {
let mut fragments_cursor = self.fragments.cursor();
fragments_cursor.seek(&split.fragment_id, SeekBias::Left);
fragments_cursor.item().ok_or(Error::InvalidAnchor).and_then(|fragment| {
let overshoot = if fragment.is_visible() {
offset - fragment.start_offset
} else {
0
};
let offset = fragments_cursor.start::<CharacterCount>().0 + overshoot;
let point = fragments_cursor.start::<Point>() + &fragment.point_for_offset(overshoot)?;
self.cache_position(Some(anchor.clone()), offset, point);
Ok((offset, point))
})
})
})
}
}
}
}

fn offset_for_point(&self, point: Point) -> Result<usize> {
let mut fragments_cursor = self.fragments.cursor();
fragments_cursor.seek(&point, SeekBias::Left);
fragments_cursor.item().ok_or(Error::OffsetOutOfRange).map(|fragment| {
let overshoot = fragment.offset_for_point(point - &fragments_cursor.start::<Point>()).unwrap();
&fragments_cursor.start::<CharacterCount>().0 + &overshoot
})
let cached_offset = {
let offset_cache = self.offset_cache.try_borrow().ok();
offset_cache.as_ref().and_then(|cache| cache.get(&point).cloned())
};

if let Some(cached_offset) = cached_offset {
Ok(cached_offset)
} else {
let mut fragments_cursor = self.fragments.cursor();
fragments_cursor.seek(&point, SeekBias::Left);
fragments_cursor.item().ok_or(Error::OffsetOutOfRange).map(|fragment| {
let overshoot = fragment.offset_for_point(point - &fragments_cursor.start::<Point>()).unwrap();
let offset = &fragments_cursor.start::<CharacterCount>().0 + &overshoot;
self.cache_position(None, offset, point);
offset
})
}
}

pub fn cmp_anchors(&self, a: &Anchor, b: &Anchor) -> Result<cmp::Ordering> {
let a_offset = self.offset_for_anchor(a)?;
let b_offset = self.offset_for_anchor(b)?;
Ok(a_offset.cmp(&b_offset))
}

fn cache_position(&self, anchor: Option<Anchor>, offset: usize, point: Point) {
anchor.map(|anchor| {
if let Ok(mut anchor_cache) = self.anchor_cache.try_borrow_mut() {
anchor_cache.insert(anchor, (offset, point));
}
});

if let Ok(mut offset_cache) = self.offset_cache.try_borrow_mut() {
offset_cache.insert(point, offset);
}
}
}

impl Point {
Expand Down Expand Up @@ -708,13 +734,18 @@ impl<'a> From<Vec<u16>> for Text {
}
}

lazy_static! {
static ref FRAGMENT_ID_MIN_VALUE: FragmentId = FragmentId(Arc::new(vec![0 as u16]));
static ref FRAGMENT_ID_MAX_VALUE: FragmentId = FragmentId(Arc::new(vec![u16::max_value()]));
}

impl FragmentId {
fn min_value() -> Self {
FragmentId(vec![0 as u16])
FRAGMENT_ID_MIN_VALUE.clone()
}

fn max_value() -> Self {
FragmentId(vec![u16::max_value()])
FRAGMENT_ID_MAX_VALUE.clone()
}

fn between(left: &Self, right: &Self) -> Self {
Expand All @@ -736,7 +767,7 @@ impl FragmentId {
}
}

FragmentId(new_entries)
FragmentId(Arc::new(new_entries))
}
}

Expand Down Expand Up @@ -798,7 +829,8 @@ impl Fragment {

fn point_for_offset(&self, offset: usize) -> Result<Point> {
let text = &self.insertion.text;
return Ok(text.point_for_offset(offset)? - &text.point_for_offset(self.start_offset)?)
let offset_in_insertion = self.start_offset + offset;
Ok(text.point_for_offset(offset_in_insertion)? - &text.point_for_offset(self.start_offset)?)
}

fn offset_for_point(&self, point: Point) -> Result<usize> {
Expand Down Expand Up @@ -1084,7 +1116,7 @@ mod tests {
use self::rand::{Rng, SeedableRng, StdRng};
let mut rng = StdRng::from_seed(&[seed]);

let mut ids = vec![FragmentId(vec![0]), FragmentId(vec![4])];
let mut ids = vec![FragmentId(Arc::new(vec![0])), FragmentId(Arc::new(vec![4]))];
for _i in 0..100 {
let index = rng.gen_range::<usize>(1, ids.len());

Expand Down
2 changes: 2 additions & 0 deletions xray_core/src/lib.rs
@@ -1,3 +1,5 @@
#[macro_use]
extern crate lazy_static;
extern crate futures;

mod notify_cell;
Expand Down