~raph/xi2

e0c6afc8486e743306b150acf6dc0492919b67d8 — Raph Levien 1 year, 8 months ago d126257
Start implementing height rope

This checkpoints the start of a generic rope-based vector without any
height measurement. We might do height measurement as a trait, or just
specialize this to TextLayout.
2 files changed, 156 insertions(+), 0 deletions(-)

A xi-druid/src/height_rope.rs
M xi-druid/src/main.rs
A xi-druid/src/height_rope.rs => xi-druid/src/height_rope.rs +155 -0
@@ 0,0 1,155 @@
//! A rope-based vector where each element has a height. The intended
//! use is for the elements to be text layout objects.

use xi_rope::tree::{Cursor, Leaf, Node, NodeInfo, TreeBuilder};
use xi_rope::interval::{Interval, IntervalBounds};
use std::marker::PhantomData;

#[derive(Clone)]
pub struct Vector<T: Clone>(Node<VectorInfo<T>>);

// This technically doesn't have to be newtyped, we could impl leaf on
// Vec directly.
#[derive(Clone)]
pub struct VectorLeaf<T> {
    data: Vec<T>,
}

// Have to implement by hand because rust issue #26925
impl<T> Default for VectorLeaf<T> {
    fn default() -> Self {
        VectorLeaf { data: Vec::new() }
    }
}

#[derive(Clone)]
pub struct VectorInfo<T> {
    phantom: PhantomData<T>,
}

impl<T: Clone> NodeInfo for VectorInfo<T> {
    type L = VectorLeaf<T>;

    fn accumulate(&mut self, _other: &Self) {}

    fn compute_info(_: &Self::L) -> Self {
        VectorInfo { phantom: Default::default() }
    }
}

const MIN_LEAF: usize = 16;
const MAX_LEAF: usize = 32;

impl<T: Clone> Leaf for VectorLeaf<T> {
    fn len(&self) -> usize {
        self.data.len()
    }

    fn is_ok_child(&self) -> bool {
        self.data.len() >= MIN_LEAF
    }

    fn push_maybe_split(&mut self, other: &Self, iv: Interval) -> Option<Self> {
        let (start, end) = iv.start_end();
        self.data.extend_from_slice(&other.data[start..end]);
        if self.len() <= MAX_LEAF {
            None
        } else {
            let splitpoint = self.len() / 2;
            let right_vec = self.data.split_off(splitpoint);
            Some(VectorLeaf { data: right_vec })
        }
    }
}

impl<T: Clone> From<Vec<T>> for Vector<T> {
    fn from(v: Vec<T>) -> Self {
        Vector(Node::from_leaf(VectorLeaf { data: v }))
    }
}

impl<T: Clone> Vector<T> {
    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn singleton(item: T) -> Vector<T> {
        vec![item].into()
    }

    pub fn get(&self, index: usize) -> Option<&T> {
        let cursor = Cursor::new(&self.0, index);
        cursor.get_leaf().and_then(|(leaf, offset)| leaf.data.get(offset))
    }

    // Note: we can do get_mut too, but that requires mutable leaf access.

    pub fn push(&mut self, item: T) {
        // This could be optimized more.
        self.0 = Node::concat(self.0.clone(), Self::singleton(item).0)
    }

    pub fn remove(&mut self, index: usize) {
        let mut b = TreeBuilder::new();
        self.push_subseq(&mut b, Interval::new(0, index));
        self.push_subseq(&mut b, Interval::new(index + 1, self.len()));
        self.0 = b.build();
    }

    pub fn set(&mut self, index: usize, value: T) {
        let mut b = TreeBuilder::new();
        self.push_subseq(&mut b, Interval::new(0, index));
        b.push_leaf(VectorLeaf { data: vec![value]});
        self.push_subseq(&mut b, Interval::new(index + 1, self.len()));
        self.0 = b.build();
    }

    pub fn insert(&mut self, index: usize, value: T) {
        let mut b = TreeBuilder::new();
        self.push_subseq(&mut b, Interval::new(0, index));
        b.push_leaf(VectorLeaf { data: vec![value]});
        self.push_subseq(&mut b, Interval::new(index, self.len()));
        self.0 = b.build();
    }

    pub fn iter_chunks(&self, range: impl IntervalBounds) -> ChunkIter<T> {
        let Interval { start, end } = range.into_interval(self.len());

        ChunkIter { cursor: Cursor::new(&self.0, start), end }
    }

    fn push_subseq(&self, b: &mut TreeBuilder<VectorInfo<T>>, iv: Interval) {
        // TODO: if we make the push_subseq method in xi-rope public, we can save some
        // allocations.
        b.push(self.0.subseq(iv));
    }
}

impl<'a, T: Clone> IntoIterator for &'a Vector<T> {
    type Item = &'a T;

    type IntoIter = std::iter::Flatten<ChunkIter<'a, T>>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter_chunks(..).flatten()
    }
}

pub struct ChunkIter<'a, T: Clone> {
    cursor: Cursor<'a, VectorInfo<T>>,
    end: usize,
}

impl<'a, T: Clone> Iterator for ChunkIter<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<&'a [T]> {
        if self.cursor.pos() >= self.end {
            return None;
        }
        let (leaf, start_pos) = self.cursor.get_leaf().unwrap();
        let len = (self.end - self.cursor.pos()).min(leaf.len() - start_pos);
        self.cursor.next_leaf();
        Some(&leaf.data[start_pos..start_pos + len])
    }
}

M xi-druid/src/main.rs => xi-druid/src/main.rs +1 -0
@@ 1,4 1,5 @@
mod edit_widget;
mod height_rope;
mod key_bindings;
mod util;