led/src/buffer.rs
Nathan Vegdahl b78501f983 Super simple text editing interface!
No cursor yet, just always appends to the end of the current buffer.
But still!
2014-12-14 21:06:48 -08:00

467 lines
13 KiB
Rust

#![allow(dead_code)]
use std::cmp::min;
use std::mem;
use std::fmt;
use std;
fn newline_count(text: &str) -> uint {
let mut count = 0;
for c in text.chars() {
if c == '\n' {
count += 1;
}
}
return count;
}
fn char_count(text: &str) -> uint {
let mut count = 0;
for _ in text.chars() {
count += 1;
}
return count;
}
fn char_and_newline_count(text: &str) -> (uint, uint) {
let mut char_count = 0;
let mut newline_count = 0;
for c in text.chars() {
char_count += 1;
if c == '\n' {
newline_count += 1;
}
}
return (char_count, newline_count);
}
fn char_pos_to_byte_pos(text: &str, pos: uint) -> uint {
let mut i: uint = 0;
for (offset, _) in text.char_indices() {
if i == pos {
return offset;
}
i += 1;
}
if i == pos {
return text.len();
}
panic!("char_pos_to_byte_pos(): char position off the end of the string.");
}
/// A block of text, contiguous in memory
pub struct TextBlock {
pub data: Vec<u8>,
}
impl TextBlock {
/// Create a new empty text block.
pub fn new() -> TextBlock {
TextBlock {
data: Vec::<u8>::new()
}
}
/// 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()
}
/// Insert 'text' at char position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
// Find insertion position in bytes
let byte_pos = char_pos_to_byte_pos(self.as_str(), pos);
// Grow data size
self.data.grow(text.len(), 0);
// Move old bytes forward
let mut from = self.data.len() - text.len();
let mut to = self.data.len();
while from > byte_pos {
from -= 1;
to -= 1;
self.data[to] = self.data[from];
}
// Copy new bytes in
let mut i = byte_pos;
for b in text.bytes() {
self.data[i] = b;
i += 1
}
}
/// Remove the text between char positions 'pos_a' and 'pos_b'.
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
// Bounds checks
if pos_a > pos_b {
panic!("TextBlock::remove_text(): pos_a must be less than or equal to pos_b.");
}
// Find removal positions in bytes
let byte_pos_a = char_pos_to_byte_pos(self.as_str(), pos_a);
let byte_pos_b = char_pos_to_byte_pos(self.as_str(), pos_b);
// Move bytes to fill in the gap left by the removed bytes
let mut from = byte_pos_b;
let mut to = byte_pos_a;
while from < self.data.len() {
self.data[to] = self.data[from];
from += 1;
to += 1;
}
// Remove data from the end
let final_size = self.data.len() + byte_pos_a - byte_pos_b;
self.data.truncate(final_size);
}
/// Returns an immutable string slice into the text block's memory
pub fn as_str<'a>(&'a self) -> &'a str {
unsafe {
mem::transmute(self.data.as_slice())
}
}
}
impl fmt::Show for TextBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
/// 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 char_count: uint,
pub newline_count: uint,
}
pub enum TextNodeData {
Leaf(TextBlock),
Branch(Box<TextNode>, Box<TextNode>)
}
const MIN_LEAF_SIZE: uint = 64;
const MAX_LEAF_SIZE: uint = MIN_LEAF_SIZE * 2;
impl TextNode {
pub fn new() -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new()),
char_count: 0,
newline_count: 0,
}
}
pub fn new_from_str(text: &str) -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new_from_str(text)),
char_count: char_count(text),
newline_count: newline_count(text),
}
}
/// Recursively splits a leaf node into roughly equal-sized children,
/// being no larger than 'max_size'.
pub fn split(&mut self, max_size: uint) {
if let TextNodeData::Branch(_, _) = self.data {
panic!("TextNode::split(): attempt to split a non-leaf node.");
}
if self.char_count > max_size {
// 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()));
}
tn1.split(max_size);
tn2.split(max_size);
// 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.char_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);
let (cc, nlc) = char_and_newline_count(text);
self.char_count += cc;
self.newline_count += nlc;
}
if self.char_count > MAX_LEAF_SIZE {
self.split(MAX_LEAF_SIZE);
}
},
TextNodeData::Branch(ref mut left, ref mut right) => {
if pos <= left.char_count {
left.insert_text(text, pos);
}
else {
right.insert_text(text, pos - left.char_count);
}
self.char_count = left.char_count + right.char_count;
self.newline_count = left.newline_count + right.newline_count;
}
}
}
/// Remove the text between byte positions 'pos_a' and 'pos_b'.
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
// Bounds checks
if pos_a > pos_b {
panic!("TextNode::remove_text(): pos_a must be less than or equal to pos_b.");
}
if pos_b > self.char_count {
panic!("TextNode::remove_text(): attempt to remove text after end of node text.");
}
match self.data {
TextNodeData::Leaf(ref mut tb) => {
tb.remove_text(pos_a, pos_b);
let (cc, nlc) = char_and_newline_count(tb.as_str());
self.char_count = cc;
self.newline_count = nlc;
},
TextNodeData::Branch(ref mut left, ref mut right) => {
let lbc = left.char_count;
if pos_a < lbc {
left.remove_text(pos_a, min(pos_b, lbc));
}
if pos_b > lbc {
right.remove_text(pos_a - min(pos_a, lbc), pos_b - lbc);
}
self.char_count = left.char_count + right.char_count;
self.newline_count = left.newline_count + right.newline_count;
}
}
if self.char_count < MIN_LEAF_SIZE {
self.merge();
}
}
}
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.char_count
}
/// Insert 'text' at char position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
self.root.insert_text(text, pos);
}
/// Remove the text between char positions 'pos_a' and 'pos_b'.
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
self.root.remove_text(pos_a, pos_b);
}
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new();
let mut cur_node = &self.root;
loop {
match cur_node.data {
TextNodeData::Leaf(_) => {
break;
},
TextNodeData::Branch(ref left, ref right) => {
node_stack.push(&(**right));
cur_node = &(**left);
}
}
}
TextBufferIter {
node_stack: node_stack,
cur_block: match cur_node.data {
TextNodeData::Leaf(ref tb) => tb.as_str().chars(),
_ => panic!("This should never happen.")
}
}
}
}
impl fmt::Show for TextBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.root.fmt(f)
}
}
/// An iterator over a text buffer
pub struct TextBufferIter<'a> {
node_stack: Vec<&'a TextNode>,
cur_block: std::str::Chars<'a>,
}
impl<'a> TextBufferIter<'a> {
// Puts the iterator on the next line
pub fn next_line(&mut self) -> Option<char> {
// TODO: more efficient implementation, taking advantage of rope
// structure.
for c in *self {
if c == '\n' {
return Option::Some(c);
}
}
return Option::None;
}
// Skips the iterator n characters ahead
pub fn skip_chars(&mut self, n: uint) {
// TODO: more efficient implementation, taking advantage of rope
// structure.
for _ in range(0, n) {
if let Option::None = self.next() {
break;
}
}
}
}
impl<'a> Iterator<char> for TextBufferIter<'a> {
fn next(&mut self) -> Option<char> {
if let Option::Some(c) = self.cur_block.next() {
return Option::Some(c);
}
loop {
if let Option::Some(node) = self.node_stack.pop() {
match node.data {
TextNodeData::Leaf(ref tb) => {
self.cur_block = tb.as_str().chars();
if let Option::Some(c) = self.cur_block.next() {
return Option::Some(c);
}
else {
continue;
}
},
TextNodeData::Branch(ref left, ref right) => {
self.node_stack.push(&(**right));
self.node_stack.push(&(**left));
continue;
}
}
}
else {
return Option::None;
}
}
}
}