Beginnings of a rope text buffer implementation.
This commit is contained in:
parent
b1dd62f8db
commit
65c900a237
219
src/buffer.rs
219
src/buffer.rs
|
@ -1,4 +1,17 @@
|
||||||
use std::mem;
|
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
|
/// A block of text, contiguous in memory
|
||||||
pub struct TextBlock {
|
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.
|
/// Return the length of the text block in bytes.
|
||||||
pub fn len(&self) -> uint {
|
pub fn len(&self) -> uint {
|
||||||
self.data.len()
|
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),
|
/// A text rope node, using TextBlocks for its underlying text
|
||||||
// Node(Box<TextBuffer>, Box<TextBuffer>)
|
/// storage.
|
||||||
// }
|
// TODO: record number of graphines as well, to support utf8 properly
|
||||||
//
|
pub struct TextNode {
|
||||||
// impl TextBuffer {
|
pub data: TextNodeData,
|
||||||
// pub fn new() -> TextBuffer {
|
pub newline_count: uint,
|
||||||
// TextBuffer::Block(TextBlock::new())
|
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)
|
||||||
|
}
|
||||||
|
}
|
16
src/main.rs
16
src/main.rs
|
@ -3,7 +3,7 @@ extern crate docopt;
|
||||||
extern crate serialize;
|
extern crate serialize;
|
||||||
|
|
||||||
use docopt::Docopt;
|
use docopt::Docopt;
|
||||||
use buffer::TextBlock;
|
use buffer::TextBuffer;
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
|
||||||
|
@ -30,16 +30,14 @@ fn main() {
|
||||||
// Get command-line arguments
|
// Get command-line arguments
|
||||||
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
|
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) {
|
for _ in range(0i, 100) {
|
||||||
tb.insert_text("Hello", 0);
|
|
||||||
tb.insert_text("Goodbye", 0);
|
|
||||||
tb.insert_text(args.arg_file.as_slice(), 0);
|
tb.insert_text(args.arg_file.as_slice(), 0);
|
||||||
if tb.len() > 1024 {
|
//if tb.len() > 1024 {
|
||||||
tb.remove_text(0, 12 + args.arg_file.len());
|
// tb.remove_text(0, args.arg_file.len());
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", tb.as_str());
|
println!("{}", tb);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user