Added a some text manipulation methods, and made backspace work properly.

This commit is contained in:
Nathan Vegdahl 2014-12-26 21:03:13 -08:00
parent c1c33ae618
commit 833e92c5a0
6 changed files with 121 additions and 39 deletions

View File

@ -4,7 +4,6 @@ use std::fmt;
use std;
use self::text_node::{TextNode, TextNodeData};
mod utils;
mod text_block;
mod text_node;
@ -20,19 +19,22 @@ impl TextBuffer {
root: TextNode::new()
}
}
pub fn len(&self) -> uint {
self.root.char_count
}
pub fn newline_count(&self) -> uint {
self.root.newline_count
}
//pub fn pos_2d_to_1d(&self, pos: (uint, uint)) -> Option<uint> {
// // TODO
// return Option::None;
//}
pub fn end_of_line(&self, pos: uint) -> uint {
self.root.end_of_line(pos)
}
pub fn pos_2d_to_closest_1d(&self, pos: (uint, uint)) -> uint {
match self.root.pos_2d_to_closest_1d(0, pos) {
@ -40,30 +42,24 @@ impl TextBuffer {
_ => self.len()
}
}
//pub fn pos_2d_to_closest_2d(&self, pos: (uint, uint)) -> (uint, uint) {
// // TODO
// return (0, 0);
//}
//pub fn pos_1d_to_2d(&self, pos: uint) -> Option<(uint, uint)> {
// // TODO
// return Option::None;
//}
pub fn pos_1d_to_closest_2d(&self, pos: uint) -> (uint, uint) {
self.root.pos_1d_to_closest_2d((0,0), 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);
}
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new();

View File

@ -2,7 +2,7 @@
use std::mem;
use std::fmt;
use super::utils::char_pos_to_byte_pos;
use string_utils::char_pos_to_byte_pos;
/// A block of text, contiguous in memory
pub struct TextBlock {

View File

@ -4,7 +4,7 @@ use std::fmt;
use std::mem;
use std::cmp::{min, max};
use super::utils::{newline_count, char_count, char_and_newline_count};
use string_utils::{newline_count, char_count, char_and_newline_count};
use super::text_block::TextBlock;
const MIN_LEAF_SIZE: uint = 64;
@ -461,6 +461,96 @@ impl TextNode {
}
/// 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 {

View File

@ -3,6 +3,7 @@
use buffer::TextBuffer;
use std::path::Path;
use files::{load_file_to_buffer, save_buffer_to_file};
use string_utils::char_count;
pub struct Editor {
@ -58,19 +59,15 @@ impl Editor {
pub fn insert_text_at_cursor(&mut self, text: &str) {
let pos = self.buffer.pos_2d_to_closest_1d(self.cursor);
let str_len = char_count(text);
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
// Insert text
self.buffer.insert_text(text, pos);
self.dirty = true;
// TODO: handle multi-character strings properly
if text == "\n" {
self.cursor.0 += 1;
self.cursor.1 = 0;
}
else {
self.cursor.1 += 1;
}
// Move cursor
self.cursor = self.buffer.pos_1d_to_closest_2d(p + str_len);
}
pub fn insert_text_at_char(&mut self, text: &str, pos: uint) {
@ -83,31 +80,29 @@ impl Editor {
let pos_b = self.buffer.pos_2d_to_closest_1d(self.cursor);
let pos_a = if pos_b >= char_count {pos_b - char_count} else {0};
// Move cursor
self.cursor = self.buffer.pos_1d_to_closest_2d(pos_a);
// Remove text
self.buffer.remove_text(pos_a, pos_b);
self.dirty = true;
// TODO: handle multi-character removal properly
if self.cursor.1 == 0 && self.cursor.0 > 0 {
self.cursor.0 -= 1;
}
else if self.cursor.1 > 0{
self.cursor.1 -= 1;
}
}
pub fn cursor_left(&mut self) {
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
self.cursor = self.buffer.pos_1d_to_closest_2d(p);
if self.cursor.1 > 0 {
self.cursor.1 -= 1;
if p > 0 {
self.cursor = self.buffer.pos_1d_to_closest_2d(p - 1);
}
else {
self.cursor = self.buffer.pos_1d_to_closest_2d(0);
}
}
pub fn cursor_right(&mut self) {
self.cursor.1 += 1;
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
self.cursor = self.buffer.pos_1d_to_closest_2d(p);
self.cursor = self.buffer.pos_1d_to_closest_2d(p + 1);
}
pub fn cursor_up(&mut self) {

View File

@ -7,6 +7,7 @@ use docopt::Docopt;
use editor::Editor;
use term_ui::TermUI;
mod string_utils;
mod buffer;
mod files;
mod editor;