WIP refactor: change text buffer to use the Line struct for storing text.

Most of the UI and editor code is either commented out or not included
in the compile right now.  This is temporary while I get the basic
text code working again.
This commit is contained in:
Nathan Vegdahl 2014-12-29 16:17:53 -08:00
parent 370d8a315e
commit c88ba48b6d
8 changed files with 648 additions and 1038 deletions

View File

@ -2,30 +2,30 @@
use std::mem;
use std::str::Graphemes;
use string_utils::{grapheme_pos_to_byte_pos, is_line_ending};
use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending};
/// A single line of text
pub struct TextLine {
pub struct Line {
text: Vec<u8>, // The text data, stored as UTF8
ending: LineEnding, // The type of line ending, if any
pub ending: LineEnding, // The type of line ending, if any
}
impl TextLine {
/// Creates a new empty TextLine
pub fn new() -> TextLine {
TextLine {
impl Line {
/// Creates a new empty Line
pub fn new() -> Line {
Line {
text: Vec::new(),
ending: LineEnding::None,
}
}
/// Creates a new TextLine from a str.
pub fn new_from_str(text: &str) -> TextLine {
// Initialize TextLine
let mut tl = TextLine {
/// Creates a new Line from a str.
pub fn new_from_str(text: &str) -> Line {
// Initialize Line
let mut tl = Line {
text: Vec::with_capacity(text.len()),
ending: LineEnding::None,
};
@ -102,6 +102,24 @@ impl TextLine {
}
/// Returns the total number of unicode graphemes in the line
pub fn grapheme_count(&self) -> uint {
let mut count = grapheme_count(self.as_str());
match self.ending {
LineEnding::None => {},
_ => {count += 1;}
}
return count;
}
/// Returns the total number of unicode graphemes in the line,
/// not counting the line ending grapheme, if any.
pub fn grapheme_count_sans_line_ending(&self) -> uint {
grapheme_count(self.as_str())
}
/// Returns an immutable string slice into the text block's memory
pub fn as_str<'a>(&'a self) -> &'a str {
unsafe {
@ -133,7 +151,7 @@ impl TextLine {
let mut i = byte_pos;
for g in text.graphemes(true) {
if is_line_ending(g) {
panic!("TextLine::insert_text(): line ending in inserted text.");
panic!("Line::insert_text(): line ending in inserted text.");
}
for b in g.bytes() {
@ -144,9 +162,36 @@ impl TextLine {
}
/// Remove the text between grapheme 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!("Line::remove_text(): pos_a must be less than or equal to pos_b.");
}
// Find removal positions in bytes
let byte_pos_a = grapheme_pos_to_byte_pos(self.as_str(), pos_a);
let byte_pos_b = grapheme_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.text.len() {
self.text[to] = self.text[from];
from += 1;
to += 1;
}
// Remove data from the end
let final_text_size = self.text.len() + byte_pos_a - byte_pos_b;
self.text.truncate(final_text_size);
}
/// Returns an iterator over the graphemes of the line
pub fn grapheme_iter<'a>(&'a self) -> TextLineIter<'a> {
TextLineIter {
pub fn grapheme_iter<'a>(&'a self) -> LineGraphemeIter<'a> {
LineGraphemeIter {
graphemes: self.as_str().graphemes(true),
ending: self.ending,
done: false,
@ -155,10 +200,10 @@ impl TextLine {
/// Returns an iterator over the graphemes of the line
pub fn grapheme_iter_at_index<'a>(&'a self, index: uint) -> TextLineIter<'a> {
pub fn grapheme_iter_at_index<'a>(&'a self, index: uint) -> LineGraphemeIter<'a> {
let temp: &str = unsafe{mem::transmute(self.text.as_slice())};
let mut iter = TextLineIter {
let mut iter = LineGraphemeIter {
graphemes: temp.graphemes(true),
ending: self.ending,
done: false,
@ -202,14 +247,14 @@ pub const LINE_ENDINGS: [&'static str, ..9] = ["",
];
/// An iterator over the graphemes of a TextLine
pub struct TextLineIter<'a> {
/// An iterator over the graphemes of a Line
pub struct LineGraphemeIter<'a> {
graphemes: Graphemes<'a>,
ending: LineEnding,
done: bool,
}
impl<'a> Iterator<&'a str> for TextLineIter<'a> {
impl<'a> Iterator<&'a str> for LineGraphemeIter<'a> {
fn next(&mut self) -> Option<&'a str> {
if self.done {
return None;
@ -237,12 +282,12 @@ impl<'a> Iterator<&'a str> for TextLineIter<'a> {
//=========================================================================
// TextLine tests
// Line tests
//=========================================================================
#[test]
fn new_text_line() {
let tl = TextLine::new();
let tl = Line::new();
assert!(tl.text.len() == 0);
assert!(tl.ending == LineEnding::None);
@ -250,7 +295,7 @@ fn new_text_line() {
#[test]
fn new_text_line_from_str() {
let tl = TextLine::new_from_str("Hello!");
let tl = Line::new_from_str("Hello!");
assert!(tl.text.len() == 6);
assert!(tl.text[0] == ('H' as u8));
@ -264,7 +309,7 @@ fn new_text_line_from_str() {
#[test]
fn new_text_line_from_empty_str() {
let tl = TextLine::new_from_str("");
let tl = Line::new_from_str("");
assert!(tl.text.len() == 0);
assert!(tl.ending == LineEnding::None);
@ -272,7 +317,7 @@ fn new_text_line_from_empty_str() {
#[test]
fn new_text_line_from_str_with_lf() {
let tl = TextLine::new_from_str("Hello!\n");
let tl = Line::new_from_str("Hello!\n");
assert!(tl.text.len() == 6);
assert!(tl.text[0] == ('H' as u8));
@ -286,7 +331,7 @@ fn new_text_line_from_str_with_lf() {
#[test]
fn new_text_line_from_str_with_crlf() {
let tl = TextLine::new_from_str("Hello!\r\n");
let tl = Line::new_from_str("Hello!\r\n");
assert!(tl.text.len() == 6);
assert!(tl.text[0] == ('H' as u8));
@ -300,7 +345,7 @@ fn new_text_line_from_str_with_crlf() {
#[test]
fn new_text_line_from_str_with_crlf_and_too_long() {
let tl = TextLine::new_from_str("Hello!\r\nLa la la la");
let tl = Line::new_from_str("Hello!\r\nLa la la la");
assert!(tl.text.len() == 6);
assert!(tl.text[0] == ('H' as u8));
@ -314,7 +359,7 @@ fn new_text_line_from_str_with_crlf_and_too_long() {
#[test]
fn text_line_insert_text() {
let mut tl = TextLine::new_from_str("Hello!\r\n");
let mut tl = Line::new_from_str("Hello!\r\n");
tl.insert_text(" world", 5);
@ -334,14 +379,30 @@ fn text_line_insert_text() {
assert!(tl.ending == LineEnding::CRLF);
}
#[test]
fn text_line_remove_text() {
let mut tl = Line::new_from_str("Hello world!\r\n");
tl.remove_text(5, 11);
assert!(tl.text.len() == 6);
assert!(tl.text[0] == ('H' as u8));
assert!(tl.text[1] == ('e' as u8));
assert!(tl.text[2] == ('l' as u8));
assert!(tl.text[3] == ('l' as u8));
assert!(tl.text[4] == ('o' as u8));
assert!(tl.text[5] == ('!' as u8));
assert!(tl.ending == LineEnding::CRLF);
}
//=========================================================================
// TextLineIter tests
// LineGraphemeIter tests
//=========================================================================
#[test]
fn text_line_grapheme_iter() {
let tl = TextLine::new_from_str("Hello!");
let tl = Line::new_from_str("Hello!");
let mut iter = tl.grapheme_iter();
assert!(iter.next() == Some("H"));
@ -355,7 +416,7 @@ fn text_line_grapheme_iter() {
#[test]
fn text_line_grapheme_iter_with_lf() {
let tl = TextLine::new_from_str("Hello!\n");
let tl = Line::new_from_str("Hello!\n");
let mut iter = tl.grapheme_iter();
assert!(iter.next() == Some("H"));
@ -370,7 +431,7 @@ fn text_line_grapheme_iter_with_lf() {
#[test]
fn text_line_grapheme_iter_with_crlf() {
let tl = TextLine::new_from_str("Hello!\r\n");
let tl = Line::new_from_str("Hello!\r\n");
let mut iter = tl.grapheme_iter();
assert!(iter.next() == Some("H"));
@ -385,7 +446,7 @@ fn text_line_grapheme_iter_with_crlf() {
#[test]
fn text_line_grapheme_iter_at_index() {
let tl = TextLine::new_from_str("Hello!");
let tl = Line::new_from_str("Hello!");
let mut iter = tl.grapheme_iter_at_index(2);
assert!(iter.next() == Some("l"));
@ -397,7 +458,7 @@ fn text_line_grapheme_iter_at_index() {
#[test]
fn text_line_grapheme_iter_at_index_past_end() {
let tl = TextLine::new_from_str("Hello!");
let tl = Line::new_from_str("Hello!");
let mut iter = tl.grapheme_iter_at_index(10);
assert!(iter.next() == None);
@ -405,7 +466,7 @@ fn text_line_grapheme_iter_at_index_past_end() {
#[test]
fn text_line_grapheme_iter_at_index_at_lf() {
let tl = TextLine::new_from_str("Hello!\n");
let tl = Line::new_from_str("Hello!\n");
let mut iter = tl.grapheme_iter_at_index(6);
assert!(iter.next() == Some("\n"));

View File

@ -1,236 +1,278 @@
#![allow(dead_code)]
use std::fmt;
use std;
use self::text_node::{TextNode, TextNodeData};
use std::fmt;
use std::mem;
use std::cmp::{min, max};
use self::node::{BufferNode, BufferNodeData};
use self::line::{Line, LineGraphemeIter};
//mod text_block;
//mod text_node;
mod line;
mod node;
mod text_block;
mod text_node;
mod text_line;
/// A text buffer
pub struct TextBuffer {
pub root: TextNode,
}
pub struct Buffer {
root: BufferNode,
}//
impl TextBuffer {
pub fn new() -> TextBuffer {
TextBuffer {
root: TextNode::new()
impl Buffer {
pub fn new() -> Buffer {
Buffer {
root: BufferNode::new()
}
}
pub fn len(&self) -> uint {
self.root.char_count
self.root.grapheme_count
}
pub fn newline_count(&self) -> uint {
self.root.newline_count
pub fn line_count(&self) -> uint {
self.root.line_count
}
pub fn end_of_line(&self, pos: uint) -> uint {
self.root.end_of_line(pos)
pub fn get_line<'a>(&'a self, index: uint) -> &'a Line {
if index >= self.line_count() {
panic!("get_line(): index out of bounds.");
}
// NOTE: this can be done non-recursively, which would be more
// efficient. However, it seems likely to require unsafe code
// if done that way.
return self.root.get_line_recursive(index);
}
/// Removes the lines in line indices [line_a, line_b).
pub fn remove_lines(&mut self, line_a: uint, line_b: uint) {
// Nothing to do
if line_a == line_b {
return;
}
// Bounds error
else if line_a > line_b {
panic!("Buffer::remove_lines(): line_a must be less than or equal to line_b.");
}
// Bounds error
else if line_b > self.line_count() {
panic!("Buffer::remove_lines(): attempt to remove lines past the last line of text.");
}
// Complete removal of all lines
else if line_a == 0 && line_b == self.root.line_count {
let mut temp_node = BufferNode::new();
mem::swap(&mut (self.root), &mut temp_node);
}
// All other cases
else {
self.root.remove_lines_recursive(line_a, line_b);
}
}
pub fn pos_2d_to_closest_1d(&self, pos: (uint, uint)) -> uint {
match self.root.pos_2d_to_closest_1d(0, pos) {
text_node::IndexOrOffset::Index(i) => i,
_ => self.len()
}
return self.root.pos_2d_to_closest_1d_recursive(pos);
}
pub fn pos_1d_to_closest_2d(&self, pos: uint) -> (uint, uint) {
self.root.pos_1d_to_closest_2d((0,0), pos)
return self.root.pos_1d_to_closest_2d_recursive(pos);
}
/// Insert 'text' at char position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
self.root.insert_text(text, pos);
}
// /// 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);
}
//
// /// 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);
// }
/// Creates an iterator at the first character
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new();
let mut cur_node = &self.root;
// 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.")
// }
// }
// }
//
//
// /// Creates an iterator starting at the specified character index.
// /// If the index is past the end of the text, then the iterator will
// /// return None on next().
// pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> {
// let mut node_stack: Vec<&'a TextNode> = Vec::new();
// let mut cur_node = &self.root;
// let mut char_i = index;
//
// loop {
// match cur_node.data {
// TextNodeData::Leaf(_) => {
// let mut char_iter = match cur_node.data {
// TextNodeData::Leaf(ref tb) => tb.as_str().chars(),
// _ => panic!("This should never happen.")
// };
//
// while char_i > 0 {
// char_iter.next();
// char_i -= 1;
// }
//
// return TextBufferIter {
// node_stack: node_stack,
// cur_block: char_iter,
// };
// },
//
// TextNodeData::Branch(ref left, ref right) => {
// if left.char_count > char_i {
// node_stack.push(&(**right));
// cur_node = &(**left);
// }
// else {
// cur_node = &(**right);
// char_i -= left.char_count;
// }
// }
// }
// }
// }
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.")
}
}
}
/// Creates an iterator starting at the specified character index.
/// If the index is past the end of the text, then the iterator will
/// return None on next().
pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new();
let mut cur_node = &self.root;
let mut char_i = index;
loop {
match cur_node.data {
TextNodeData::Leaf(_) => {
let mut char_iter = match cur_node.data {
TextNodeData::Leaf(ref tb) => tb.as_str().chars(),
_ => panic!("This should never happen.")
};
while char_i > 0 {
char_iter.next();
char_i -= 1;
}
return TextBufferIter {
node_stack: node_stack,
cur_block: char_iter,
};
},
TextNodeData::Branch(ref left, ref right) => {
if left.char_count > char_i {
node_stack.push(&(**right));
cur_node = &(**left);
}
else {
cur_node = &(**right);
char_i -= left.char_count;
}
}
}
}
}
}
impl fmt::Show for TextBuffer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.root.fmt(f)
}
}
// impl fmt::Show for Buffer {
// 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>,
/// An iterator over a text buffer's graphemes
pub struct BufferGraphemeIter<'a> {
node_stack: Vec<&'a BufferNode>,
cur_line: &'a Line,
}
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;
}
}
}
// Skips the iterator n characters ahead, unless it hits a newline
// character. If it hits a newline character, returns true, otherwise,
// false.
pub fn skip_non_newline_chars(&mut self, n: uint) -> bool {
// TODO: more efficient implementation, taking advantage of rope
// structure.
for _ in range(0, n) {
match self.next() {
Option::Some(c) => {
if c == '\n' {
return true;
}
},
Option::None => {
break;
}
}
}
return false;
}
}
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;
}
}
}
}
// 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;
// }
// }
// }
//
//
// // Skips the iterator n characters ahead, unless it hits a newline
// // character. If it hits a newline character, returns true, otherwise,
// // false.
// pub fn skip_non_newline_chars(&mut self, n: uint) -> bool {
// // TODO: more efficient implementation, taking advantage of rope
// // structure.
// for _ in range(0, n) {
// match self.next() {
// Option::Some(c) => {
// if c == '\n' {
// return true;
// }
// },
//
// Option::None => {
// break;
// }
// }
// }
//
// return false;
// }
// }
//
//
// 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;
// }
// }
// }
// }

290
src/buffer/node.rs Normal file
View File

@ -0,0 +1,290 @@
use std;
use std::fmt;
use std::mem;
use std::cmp::{min, max};
use super::line::{Line, LineGraphemeIter};
pub enum BufferNodeData {
Leaf(Line),
Branch(Box<BufferNode>, Box<BufferNode>),
}
pub struct BufferNode {
pub data: BufferNodeData,
pub tree_height: uint,
pub grapheme_count: uint,
pub line_count: uint,
}
impl BufferNode {
pub fn new() -> BufferNode {
BufferNode {
data: BufferNodeData::Leaf(Line::new()),
tree_height: 1,
grapheme_count: 0,
line_count: 1,
}
}
fn update_height(&mut self) {
match self.data {
BufferNodeData::Leaf(_) => {
self.tree_height = 1;
},
BufferNodeData::Branch(ref left, ref right) => {
self.tree_height = max(left.tree_height, right.tree_height) + 1;
}
}
}
fn update_stats(&mut self) {
self.update_height();
match self.data {
BufferNodeData::Leaf(ref line) => {
self.grapheme_count = line.grapheme_count();
self.line_count = 1;
},
BufferNodeData::Branch(ref left, ref right) => {
self.grapheme_count = left.grapheme_count + right.grapheme_count;
self.line_count = left.line_count + right.line_count;
}
}
}
/// Rotates the tree under the node left
fn rotate_left(&mut self) {
let mut temp = BufferNode::new();
if let BufferNodeData::Branch(_, ref mut right) = self.data {
mem::swap(&mut temp, &mut (**right));
if let BufferNodeData::Branch(ref mut left, _) = temp.data {
mem::swap(&mut (**left), &mut (**right));
}
else {
panic!("rotate_left(): attempting to rotate node without branching right child.");
}
}
else {
panic!("rotate_left(): attempting to rotate leaf node.");
}
if let BufferNodeData::Branch(ref mut left, _) = temp.data {
mem::swap(&mut (**left), self);
left.update_stats();
}
mem::swap(&mut temp, self);
self.update_stats();
}
/// Rotates the tree under the node right
fn rotate_right(&mut self) {
let mut temp = BufferNode::new();
if let BufferNodeData::Branch(ref mut left, _) = self.data {
mem::swap(&mut temp, &mut (**left));
if let BufferNodeData::Branch(_, ref mut right) = temp.data {
mem::swap(&mut (**right), &mut (**left));
}
else {
panic!("rotate_right(): attempting to rotate node without branching left child.");
}
}
else {
panic!("rotate_right(): attempting to rotate leaf node.");
}
if let BufferNodeData::Branch(_, ref mut right) = temp.data {
mem::swap(&mut (**right), self);
right.update_stats();
}
mem::swap(&mut temp, self);
self.update_stats();
}
/// Rebalances the tree under the node
fn rebalance(&mut self) {
loop {
let mut rot: int;
if let BufferNodeData::Branch(ref mut left, ref mut right) = self.data {
let height_diff = (left.tree_height as int) - (right.tree_height as int);
// Left side higher than right side
if height_diff > 1 {
let mut child_rot = false;
if let BufferNodeData::Branch(ref lc, ref rc) = left.data {
if lc.tree_height < rc.tree_height {
child_rot = true;
}
}
if child_rot {
left.rotate_left();
}
rot = 1;
}
// Right side higher then left side
else if height_diff < -1 {
let mut child_rot = false;
if let BufferNodeData::Branch(ref lc, ref rc) = right.data {
if lc.tree_height > rc.tree_height {
child_rot = true;
}
}
if child_rot {
right.rotate_right();
}
rot = -1;
}
// Balanced, stop
else {
break;
}
}
else {
// Leaf node, stop
break;
}
if rot == 1 {
self.rotate_right();
}
else if rot == -1 {
self.rotate_left();
}
}
}
pub fn get_line_recursive<'a>(&'a self, index: uint) -> &'a Line {
match self.data {
BufferNodeData::Leaf(ref line) => {
if index != 0 {
panic!("get_line_recursive(): at leaf, but index is not zero. This should never happen!");
}
return line;
},
BufferNodeData::Branch(ref left, ref right) => {
if index < left.line_count {
return left.get_line_recursive(index);
}
else {
return right.get_line_recursive(index - left.line_count);
}
}
}
}
pub fn pos_2d_to_closest_1d_recursive(&self, pos: (uint, uint)) -> uint {
match self.data {
BufferNodeData::Leaf(_) => {
if pos.0 != 0 {
panic!("pos_2d_to_closest_1d_recursive(): at leaf, but index is not zero. This should never happen!");
}
return min(pos.1, self.grapheme_count);
},
BufferNodeData::Branch(ref left, ref right) => {
if pos.0 < left.line_count {
return left.pos_2d_to_closest_1d_recursive(pos);
}
else {
return left.grapheme_count + right.pos_2d_to_closest_1d_recursive((pos.0 - left.line_count, pos.1));
}
}
}
}
pub fn pos_1d_to_closest_2d_recursive(&self, pos: uint) -> (uint, uint) {
match self.data {
BufferNodeData::Leaf(_) => {
return (0, min(pos, self.grapheme_count));
},
BufferNodeData::Branch(ref left, ref right) => {
if pos < left.grapheme_count {
return left.pos_1d_to_closest_2d_recursive(pos);
}
else {
let (v, h) = right.pos_1d_to_closest_2d_recursive((pos - left.grapheme_count));
return (v + left.line_count, h);
}
}
}
}
pub fn remove_lines_recursive(&mut self, line_a: uint, line_b: uint) {
let mut remove_left = false;
let mut remove_right = false;
let mut temp_node = BufferNode::new();
if let BufferNodeData::Branch(ref mut left, ref mut right) = self.data {
// Left node completely removed
if line_a == 0 && line_b >= left.line_count {
remove_left = true;
}
// Left node partially removed
else if line_a < left.line_count {
let a = line_a;
let b = min(left.line_count, line_b);
left.remove_lines_recursive(a, b);
}
// Right node completely removed
if line_a <= left.line_count && line_b >= (left.line_count + right.line_count) {
remove_right = true;
}
// Right node partially removed
else if line_b > left.line_count {
let a = if line_a > left.line_count {line_a - left.line_count} else {0};
let b = line_b - left.line_count;
right.remove_lines_recursive(a, b);
}
// Set up for node removal
if remove_left && remove_right {
panic!("remove_lines_recursive(): attempting to completely remove both left and right nodes. This should never happen!");
}
else if remove_left {
mem::swap(&mut temp_node, &mut (**right));
}
else if remove_right {
mem::swap(&mut temp_node, &mut (**left));
}
}
else {
panic!("remove_lines_recursive(): processing a leaf node directly. This should never happen!");
}
// Swap out node for non-removed node
if remove_left || remove_right {
mem::swap(&mut temp_node, self);
}
self.update_stats();
self.rebalance();
}
}

View File

@ -1,189 +0,0 @@
#![allow(dead_code)]
use std::mem;
use std::fmt;
use string_utils::{char_pos_to_byte_pos, char_count};
/// A block of text, contiguous in memory
pub struct TextBlock {
// The actual text data, in utf8
pub data: Vec<u8>,
// The visual width of each printable character.
// Characters with variable width (e.g. tab characters)
// have width None.
pub widths: Vec<Option<u8>>,
}
impl TextBlock {
/// Create a new empty text block.
pub fn new() -> TextBlock {
TextBlock {
data: Vec::<u8>::new(),
widths: Vec::<Option<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()),
widths: Vec::<Option<u8>>::with_capacity(text.len()),
};
for b in text.bytes() {
tb.data.push(b);
}
// TODO: handle fonts
for c in text.chars() {
if c == '\t' {
tb.widths.push(None);
}
else {
tb.widths.push(Some(1));
}
}
return tb;
}
/// Return the length of the text block in bytes.
pub fn len(&self) -> uint {
self.data.len()
}
/// Returns the total width of text block sans-variable-width characters
pub fn total_non_variable_width(&self) -> uint {
let mut width: uint = 0;
for w in self.widths.iter() {
if let &Some(ww) = w {
width += ww as uint;
}
}
return width;
}
/// Returns the number of variable-width chars in the text block
pub fn variable_width_chars(&self) -> uint {
let mut count: uint = 0;
for w in self.widths.iter() {
if let &None = w {
count += 1;
}
}
return count;
}
/// Insert 'text' at char position 'pos'.
pub fn insert_text(&mut self, text: &str, pos: uint) {
//====== TEXT DATA ======
// 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
}
//====== WIDTHS ======
// Grow widths size
let cc = char_count(text);
self.widths.grow(cc, None);
// Move old widths forward
from = self.widths.len() - cc;
to = self.widths.len();
while from > pos {
from -= 1;
to -= 1;
self.widths[to] = self.widths[from];
}
// Copy new widths in
i = pos;
for c in text.chars() {
if c == '\t' {
self.widths[i] = None;
}
else {
self.widths[i] = Some(1);
}
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.");
}
//====== TEXT DATA ======
// 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_data_size = self.data.len() + byte_pos_a - byte_pos_b;
self.data.truncate(final_data_size);
//====== WIDTHS ======
from = pos_b;
to = pos_a;
while from < self.widths.len() {
self.widths[to] = self.widths[from];
from += 1;
to += 1;
}
// Remove data from end
let final_widths_size = self.widths.len() + pos_a - pos_b;
self.data.truncate(final_widths_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())
}
}

View File

@ -1,602 +0,0 @@
#![allow(dead_code)]
use std::fmt;
use std::mem;
use std::cmp::{min, max};
use string_utils::{newline_count, char_count, char_and_newline_count};
use super::text_block::TextBlock;
const MIN_LEAF_SIZE: uint = 64;
const MAX_LEAF_SIZE: uint = MIN_LEAF_SIZE * 2;
pub enum IndexOrOffset {
Index(uint),
Offset(uint)
}
/// 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 tree_height: uint,
pub char_count: uint,
pub newline_count: uint,
}
pub enum TextNodeData {
Leaf(TextBlock),
Branch(Box<TextNode>, Box<TextNode>)
}
impl TextNode {
pub fn new() -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new()),
tree_height: 1,
char_count: 0,
newline_count: 0,
}
}
pub fn new_from_str(text: &str) -> TextNode {
TextNode {
data: TextNodeData::Leaf(TextBlock::new_from_str(text)),
tree_height: 1,
char_count: char_count(text),
newline_count: newline_count(text),
}
}
pub fn update_height(&mut self) {
match self.data {
TextNodeData::Leaf(_) => {
self.tree_height = 1;
},
TextNodeData::Branch(ref left, ref right) => {
self.tree_height = max(left.tree_height, right.tree_height) + 1;
}
}
}
pub fn update_stats(&mut self) {
match self.data {
TextNodeData::Leaf(ref tb) => {
self.tree_height = 1;
let (cc, nlc) = char_and_newline_count(tb.as_str());
self.char_count = cc;
self.newline_count = nlc;
},
TextNodeData::Branch(ref left, ref right) => {
self.tree_height = max(left.tree_height, right.tree_height) + 1;
self.char_count = left.char_count + right.char_count;
self.newline_count = left.newline_count + right.newline_count;
}
}
}
pub fn rotate_left(&mut self) {
let mut temp = TextNode::new();
if let TextNodeData::Branch(_, ref mut right) = self.data {
mem::swap(&mut temp, &mut (**right));
if let TextNodeData::Branch(ref mut left, _) = temp.data {
mem::swap(&mut (**left), &mut (**right));
}
else {
panic!("rotate_left(): attempting to rotate node without branching right child.");
}
}
else {
panic!("rotate_left(): attempting to rotate leaf node.");
}
if let TextNodeData::Branch(ref mut left, _) = temp.data {
mem::swap(&mut (**left), self);
left.update_stats();
}
mem::swap(&mut temp, self);
self.update_stats();
}
pub fn rotate_right(&mut self) {
let mut temp = TextNode::new();
if let TextNodeData::Branch(ref mut left, _) = self.data {
mem::swap(&mut temp, &mut (**left));
if let TextNodeData::Branch(_, ref mut right) = temp.data {
mem::swap(&mut (**right), &mut (**left));
}
else {
panic!("rotate_right(): attempting to rotate node without branching left child.");
}
}
else {
panic!("rotate_right(): attempting to rotate leaf node.");
}
if let TextNodeData::Branch(_, ref mut right) = temp.data {
mem::swap(&mut (**right), self);
right.update_stats();
}
mem::swap(&mut temp, self);
self.update_stats();
}
pub fn rebalance(&mut self) {
loop {
let mut rot: int;
if let TextNodeData::Branch(ref mut left, ref mut right) = self.data {
let height_diff = (left.tree_height as int) - (right.tree_height as int);
// Left side higher than right side
if height_diff > 1 {
let mut child_rot = false;
if let TextNodeData::Branch(ref lc, ref rc) = left.data {
if lc.tree_height < rc.tree_height {
child_rot = true;
}
}
if child_rot {
left.rotate_left();
}
rot = 1;
}
// Right side higher then left side
else if height_diff < -1 {
let mut child_rot = false;
if let TextNodeData::Branch(ref lc, ref rc) = right.data {
if lc.tree_height > rc.tree_height {
child_rot = true;
}
}
if child_rot {
right.rotate_right();
}
rot = -1;
}
// Balanced, stop
else {
break;
}
}
else {
break;
}
if rot == 1 {
self.rotate_right();
}
else if rot == -1 {
self.rotate_left();
}
}
}
/// 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);
}
self.rebalance();
self.update_height();
}
/// 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()));
self.rebalance();
self.update_height();
}
}
/// 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;
}
}
self.rebalance();
self.update_height();
}
/// 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;
}
}
self.rebalance();
self.update_height();
if self.char_count < MIN_LEAF_SIZE {
self.merge();
self.rebalance();
self.update_height();
}
}
/// Find the closest 1d text position that represents the given
/// 2d position well.
pub fn pos_2d_to_closest_1d(&self, offset: uint, pos: (uint, uint)) -> IndexOrOffset {
match self.data {
TextNodeData::Leaf(ref tb) => {
let mut iter = tb.as_str().chars();
let mut i = 0;
let mut line = 0;
let mut col = offset;
for c in iter {
// Check if we've hit or passed the target column on
// the target line.
if line == pos.0 && col >= pos.1 {
break;
}
// Increment counters
if c == '\n' {
line += 1;
col = 0;
// Check if we've passed the target line
if line > pos.0 {
break;
}
}
else {
col += 1;
}
i += 1;
}
// If we've reached the end of this text block but
// haven't reached the target position, return an
// offset of the amount of this line already consumed.
if pos.0 > line || (pos.0 == line && pos.1 > col) {
return IndexOrOffset::Offset(col);
}
// Otherwise, we've found it!
return IndexOrOffset::Index(i);
},
TextNodeData::Branch(ref left, ref right) => {
// Left child
if pos.0 <= left.newline_count {
match left.pos_2d_to_closest_1d(offset, pos) {
IndexOrOffset::Index(il) => {
return IndexOrOffset::Index(il);
},
IndexOrOffset::Offset(il) => {
match right.pos_2d_to_closest_1d(il, (pos.0 - left.newline_count, pos.1)) {
IndexOrOffset::Index(ir) => {
return IndexOrOffset::Index(ir + left.char_count);
},
IndexOrOffset::Offset(ir) => {
return IndexOrOffset::Offset(ir);
}
}
}
}
}
// Right child
else {
match right.pos_2d_to_closest_1d(0, (pos.0 - left.newline_count, pos.1)) {
IndexOrOffset::Index(ir) => {
return IndexOrOffset::Index(ir + left.char_count);
},
IndexOrOffset::Offset(ir) => {
return IndexOrOffset::Offset(ir);
}
}
}
}
}
}
/// Find the closest 2d text position that represents the given
/// 1d position well.
pub fn pos_1d_to_closest_2d(&self, offset_pos: (uint, uint), pos: uint) -> (uint, uint) {
match self.data {
TextNodeData::Leaf(ref tb) => {
let mut iter = tb.as_str().chars();
let mut line = offset_pos.0;
let mut col = offset_pos.1;
let mut i: uint = 0;
for c in iter {
if i == pos {
break;
}
if c == '\n' {
line += 1;
col = 0;
}
else {
col += 1;
}
i += 1;
}
return (line, col);
},
TextNodeData::Branch(ref left, ref right) => {
if left.char_count > pos {
return left.pos_1d_to_closest_2d(offset_pos, pos);
}
else {
let mut line = offset_pos.0;
let mut col = offset_pos.1;
if left.newline_count > 0 {
line += left.newline_count;
col = 0;
}
col += left.tail_len();
return right.pos_1d_to_closest_2d((line, col), pos - left.char_count);
}
}
}
}
/// Returns the number of newlines contained within the
/// character range [pos_a, pos_b)
pub fn newlines_in_range(&self, pos_a: uint, pos_b: uint) -> uint {
if pos_a > pos_b {
panic!("newlines_in_range(): pos_a must be less than or equal to pos_b.");
}
if pos_a == pos_b {
return 0;
}
match self.data {
TextNodeData::Leaf(ref tb) => {
let mut iter = tb.as_str().chars();
let mut count: uint = 0;
let mut i: uint = 0;
for c in iter {
if i >= pos_b {
break;
}
if i >= pos_a && c == '\n' {
count += 1;
}
i += 1;
}
return count;
},
TextNodeData::Branch(ref left, ref right) => {
let mut count: uint = 0;
// Left
if pos_a == 0 && pos_b >= left.char_count {
count += left.newline_count;
}
else if pos_a < left.char_count {
count += left.newlines_in_range(pos_a, pos_b);
}
// Right
if pos_a <= left.char_count && pos_b >= self.char_count {
count += right.newline_count;
}
else if pos_a < self.char_count && pos_b >= left.char_count {
let pa = if pos_a > left.char_count {pos_a - left.char_count} else {0};
count += right.newlines_in_range(pa, pos_b - left.char_count);
}
return count;
}
}
}
/// Starting at pos, find the end of the line and return its position
pub fn end_of_line(&self, pos: uint) -> uint {
match self.data {
TextNodeData::Leaf(ref tb) => {
let mut iter = tb.as_str().chars();
let mut i: uint = 0;
for c in iter {
if i >= pos {
if c == '\n' {
break;
}
}
i += 1;
}
return i;
},
TextNodeData::Branch(ref left, ref right) => {
if (left.char_count - left.tail_len()) > pos {
return left.end_of_line(pos);
}
else {
return right.end_of_line(pos - left.char_count);
}
}
}
}
/// Returns the number of characters after the last newline in the node,
/// or the total character length of the node if there are no newlines.
pub fn tail_len(&self) -> uint {
match self.data {
TextNodeData::Leaf(ref tb) => {
let mut iter = tb.as_str().chars();
let mut tlen = 0;
for c in iter {
if c == '\n' {
tlen = 0;
}
else {
tlen += 1;
}
}
return tlen;
},
TextNodeData::Branch(ref left, ref right) => {
if right.newline_count > 0 {
return right.tail_len();
}
else {
return left.tail_len() + right.char_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)
}
}
}
}

View File

@ -1,6 +1,6 @@
#![allow(dead_code)]
use buffer::TextBuffer;
use buffer::Buffer;
use std::path::Path;
use files::{load_file_to_buffer, save_buffer_to_file};
use string_utils::char_count;

View File

@ -4,14 +4,14 @@ extern crate "rustc-serialize" as rustc_serialize;
use std::path::Path;
use docopt::Docopt;
use editor::Editor;
use term_ui::TermUI;
//use editor::Editor;
//use term_ui::TermUI;
mod string_utils;
mod buffer;
mod files;
mod editor;
mod term_ui;
//mod files;
//mod editor;
//mod term_ui;
@ -40,17 +40,17 @@ fn main() {
// Get command-line arguments
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit());
// Load file, if specified
let editor = if let Option::Some(s) = args.arg_file {
Editor::new_from_file(&Path::new(s.as_slice()))
}
else {
Editor::new()
};
// Initialize and start UI
let mut ui = TermUI::new_from_editor(editor);
ui.ui_loop();
// // Load file, if specified
// let editor = if let Option::Some(s) = args.arg_file {
// Editor::new_from_file(&Path::new(s.as_slice()))
// }
// else {
// Editor::new()
// };
//
// // Initialize and start UI
// let mut ui = TermUI::new_from_editor(editor);
// ui.ui_loop();
//println!("{}", editor.buffer.root.tree_height);
}

View File

@ -34,6 +34,14 @@ pub fn char_count(text: &str) -> uint {
return count;
}
pub fn grapheme_count(text: &str) -> uint {
let mut count = 0;
for _ in text.graphemes(true) {
count += 1;
}
return count;
}
pub fn char_and_newline_count(text: &str) -> (uint, uint) {
let mut char_count = 0;
let mut newline_count = 0;