Split buffer.rs into various sub-modules for better organization.

This commit is contained in:
Nathan Vegdahl 2014-12-19 23:22:30 -08:00
parent baafe5284d
commit 421b5288a4
6 changed files with 307 additions and 292 deletions

143
src/buffer/mod.rs Normal file
View File

@ -0,0 +1,143 @@
use std::fmt;
use std;
use self::text_node::{TextNode, TextNodeData};
mod utils;
mod text_block;
mod text_node;
/// A text buffer
pub struct TextBuffer {
pub 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;
}
}
}
}

102
src/buffer/text_block.rs Normal file
View File

@ -0,0 +1,102 @@
#![allow(dead_code)]
use std::mem;
use std::fmt;
use super::utils::char_pos_to_byte_pos;
/// 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())
}
}

View File

@ -1,159 +1,14 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::cmp::{min, max};
use std::mem;
use std::fmt; use std::fmt;
use std; use std::mem;
use std::cmp::{min, max};
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())
}
}
use super::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;
/// A text rope node, using TextBlocks for its underlying text /// A text rope node, using TextBlocks for its underlying text
@ -171,9 +26,6 @@ pub enum TextNodeData {
Branch(Box<TextNode>, Box<TextNode>) Branch(Box<TextNode>, Box<TextNode>)
} }
const MIN_LEAF_SIZE: uint = 64;
const MAX_LEAF_SIZE: uint = MIN_LEAF_SIZE * 2;
impl TextNode { impl TextNode {
pub fn new() -> TextNode { pub fn new() -> TextNode {
@ -488,137 +340,3 @@ impl fmt::Show for TextNode {
} }
} }
} }
/// A text buffer
pub struct TextBuffer {
pub 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;
}
}
}
}

51
src/buffer/utils.rs Normal file
View File

@ -0,0 +1,51 @@
#![allow(dead_code)]
//! Misc helpful utility functions for TextBuffer related stuff.
pub fn newline_count(text: &str) -> uint {
let mut count = 0;
for c in text.chars() {
if c == '\n' {
count += 1;
}
}
return count;
}
pub fn char_count(text: &str) -> uint {
let mut count = 0;
for _ in text.chars() {
count += 1;
}
return count;
}
pub 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);
}
pub 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.");
}

View File

@ -27,7 +27,7 @@ pub fn save_buffer_to_file(tb: &TextBuffer, path: &Path) -> IoResult<()> {
let mut f = BufferedWriter::new(try!(File::create(path))); let mut f = BufferedWriter::new(try!(File::create(path)));
for c in iter { for c in iter {
f.write_char(c); let _ = f.write_char(c);
} }
return Ok(()); return Ok(());

View File

@ -1,8 +1,9 @@
#![allow(dead_code)]
extern crate rustbox; extern crate rustbox;
extern crate docopt; extern crate docopt;
extern crate serialize; extern crate serialize;
use std::char; use std::char;
use std::path::Path; use std::path::Path;
use docopt::Docopt; use docopt::Docopt;
@ -36,7 +37,7 @@ Options:
const K_ENTER: u16 = 13; const K_ENTER: u16 = 13;
const K_TAB: u16 = 9; const K_TAB: u16 = 9;
const K_SPACE: u16 = 32; const K_SPACE: u16 = 32;
const K_BACKSPACE: u16 = 127; //const K_BACKSPACE: u16 = 127;
//const K_DOWN: u16 = 65516; //const K_DOWN: u16 = 65516;
//const K_LEFT: u16 = 65515; //const K_LEFT: u16 = 65515;
//const K_RIGHT: u16 = 65514; //const K_RIGHT: u16 = 65514;
@ -119,7 +120,7 @@ fn main() {
}, },
K_CTRL_S => { K_CTRL_S => {
save_buffer_to_file(&tb, &Path::new("untitled.txt")); let _ = save_buffer_to_file(&tb, &Path::new("untitled.txt"));
}, },
K_ENTER => { K_ENTER => {