Beginnings of a rope text buffer implementation.

This commit is contained in:
Nathan Vegdahl 2014-12-13 15:27:47 -08:00
parent b1dd62f8db
commit 65c900a237
2 changed files with 214 additions and 21 deletions

View File

@ -1,4 +1,17 @@
use std::mem;
use std::fmt;
fn newline_count(text: &str) -> uint {
let mut count = 0;
for c in text.chars() {
if c == '\n' {
count += 1;
}
}
return count;
}
/// A block of text, contiguous in memory
pub struct TextBlock {
@ -13,6 +26,19 @@ impl TextBlock {
}
}
/// Create a new text block with the contents of 'text'.
pub fn new_from_str(text: &str) -> TextBlock {
let mut tb = TextBlock {
data: Vec::<u8>::with_capacity(text.len())
};
for b in text.bytes() {
tb.data.push(b);
}
return tb;
}
/// Return the length of the text block in bytes.
pub fn len(&self) -> uint {
self.data.len()
@ -85,16 +111,185 @@ impl TextBlock {
}
}
impl fmt::Show for TextBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
// /// A rope text storage buffer, using TextBlocks for its underlying text
// /// storage.
// pub enum TextBuffer {
// Block(TextBlock),
// Node(Box<TextBuffer>, Box<TextBuffer>)
// }
//
// impl TextBuffer {
// pub fn new() -> TextBuffer {
// TextBuffer::Block(TextBlock::new())
// }
// }
/// A text rope node, using TextBlocks for its underlying text
/// storage.
// TODO: record number of graphines as well, to support utf8 properly
pub struct TextNode {
pub data: TextNodeData,
pub newline_count: uint,
pub byte_count: uint,
}
pub enum TextNodeData {
Leaf(TextBlock),
Branch(Box<TextNode>, Box<TextNode>)
}
const MIN_LEAF_SIZE: uint = 512;
const MAX_LEAF_SIZE: uint = MIN_LEAF_SIZE * 2;
impl TextNode {
pub fn new() -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new()),
newline_count: 0,
byte_count: 0
}
}
pub fn new_from_str(text: &str) -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new_from_str(text)),
newline_count: newline_count(text),
byte_count: text.len()
}
}
/// Splits a leaf node into equal-sized pieces until all children are
/// less than 'max_size'.
pub fn split(&mut self) {
if let TextNodeData::Branch(_, _) = self.data {
panic!("TextNode::split(): attempt to split a non-leaf node.");
}
if self.byte_count > 1 {
// Split data into two new text blocks
let mut tn1 = box TextNode::new();
let mut tn2 = box TextNode::new();
if let TextNodeData::Leaf(ref mut tb) = self.data {
let pos = tb.len() / 2;
tn1 = box TextNode::new_from_str(tb.as_str().slice(0, pos));
tn2 = box TextNode::new_from_str(tb.as_str().slice(pos, tb.len()));
}
// Swap the old and new data
let mut new_data = TextNodeData::Branch(tn1, tn2);
mem::swap(&mut self.data, &mut new_data);
}
}
/// Merges the data of a non-leaf node to make it a leaf node
pub fn merge(&mut self) {
if let TextNodeData::Branch(_, _) = self.data {
let mut s: String = String::from_str("");
if let TextNodeData::Branch(ref mut left, ref mut right) = self.data {
// Merge left and right children first, to make sure we're dealing
// with leafs
if let TextNodeData::Branch(_, _) = left.data { left.merge(); }
if let TextNodeData::Branch(_, _) = right.data { right.merge(); }
// Push data into a string
if let TextNodeData::Leaf(ref tb) = left.data {
s.push_str(tb.as_str());
}
if let TextNodeData::Leaf(ref tb) = right.data {
s.push_str(tb.as_str());
}
}
self.data = TextNodeData::Leaf(TextBlock::new_from_str(s.as_slice()));
}
}
/// Insert 'text' at position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
if pos > self.byte_count {
panic!("TextNode::insert_text(): attempt to insert text after end of node text.");
}
match self.data {
TextNodeData::Leaf(_) => {
if let TextNodeData::Leaf(ref mut tb) = self.data {
tb.insert_text(text, pos);
}
self.newline_count += newline_count(text);
self.byte_count += text.len();
if self.byte_count > MAX_LEAF_SIZE {
self.split();
}
},
TextNodeData::Branch(ref mut left, ref mut right) => {
if pos <= left.byte_count {
left.insert_text(text, pos);
}
else {
right.insert_text(text, pos - left.byte_count);
}
self.newline_count = left.newline_count + right.newline_count;
self.byte_count = left.byte_count + right.byte_count;
}
}
}
}
impl fmt::Show for TextNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.data {
TextNodeData::Leaf(ref tb) => {
tb.fmt(f)
},
TextNodeData::Branch(ref left, ref right) => {
try!(left.fmt(f));
right.fmt(f)
}
}
}
}
/// A text buffer
pub struct TextBuffer {
root: TextNode
}
impl TextBuffer {
pub fn new() -> TextBuffer {
TextBuffer {
root: TextNode::new()
}
}
pub fn len(&self) -> uint {
self.root.byte_count
}
/// Insert 'text' at byte position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
self.root.insert_text(text, pos);
}
/// Remove the text between byte positions 'pos_a' and 'pos_b'.
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
match self.root.data {
TextNodeData::Leaf(ref mut tb) => {
tb.remove_text(pos_a, pos_b);
self.root.byte_count = tb.len();
},
TextNodeData::Branch(_, _) => {}
}
}
}
impl fmt::Show for TextBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.root.fmt(f)
}
}

View File

@ -3,7 +3,7 @@ extern crate docopt;
extern crate serialize;
use docopt::Docopt;
use buffer::TextBlock;
use buffer::TextBuffer;
mod buffer;
@ -30,16 +30,14 @@ fn main() {
// Get command-line arguments
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
let mut tb = TextBlock::new();
let mut tb = TextBuffer::new();
for _ in range(0i, 1000) {
tb.insert_text("Hello", 0);
tb.insert_text("Goodbye", 0);
for _ in range(0i, 100) {
tb.insert_text(args.arg_file.as_slice(), 0);
if tb.len() > 1024 {
tb.remove_text(0, 12 + args.arg_file.len());
}
//if tb.len() > 1024 {
// tb.remove_text(0, args.arg_file.len());
//}
}
println!("{}", tb.as_str());
println!("{}", tb);
}