Split buffer.rs into various sub-modules for better organization.
This commit is contained in:
parent
baafe5284d
commit
421b5288a4
143
src/buffer/mod.rs
Normal file
143
src/buffer/mod.rs
Normal 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
102
src/buffer/text_block.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
51
src/buffer/utils.rs
Normal 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.");
|
||||||
|
}
|
|
@ -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(());
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user