Added a some text manipulation methods, and made backspace work properly.
This commit is contained in:
parent
c1c33ae618
commit
833e92c5a0
|
@ -4,7 +4,6 @@ use std::fmt;
|
||||||
use std;
|
use std;
|
||||||
use self::text_node::{TextNode, TextNodeData};
|
use self::text_node::{TextNode, TextNodeData};
|
||||||
|
|
||||||
mod utils;
|
|
||||||
mod text_block;
|
mod text_block;
|
||||||
mod text_node;
|
mod text_node;
|
||||||
|
|
||||||
|
@ -20,19 +19,22 @@ impl TextBuffer {
|
||||||
root: TextNode::new()
|
root: TextNode::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn len(&self) -> uint {
|
pub fn len(&self) -> uint {
|
||||||
self.root.char_count
|
self.root.char_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn newline_count(&self) -> uint {
|
pub fn newline_count(&self) -> uint {
|
||||||
self.root.newline_count
|
self.root.newline_count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//pub fn pos_2d_to_1d(&self, pos: (uint, uint)) -> Option<uint> {
|
pub fn end_of_line(&self, pos: uint) -> uint {
|
||||||
// // TODO
|
self.root.end_of_line(pos)
|
||||||
// return Option::None;
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
pub fn pos_2d_to_closest_1d(&self, pos: (uint, uint)) -> uint {
|
pub fn pos_2d_to_closest_1d(&self, pos: (uint, uint)) -> uint {
|
||||||
match self.root.pos_2d_to_closest_1d(0, pos) {
|
match self.root.pos_2d_to_closest_1d(0, pos) {
|
||||||
|
@ -40,30 +42,24 @@ impl TextBuffer {
|
||||||
_ => self.len()
|
_ => 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) {
|
pub fn pos_1d_to_closest_2d(&self, pos: uint) -> (uint, uint) {
|
||||||
self.root.pos_1d_to_closest_2d((0,0), pos)
|
self.root.pos_1d_to_closest_2d((0,0), pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Insert 'text' at char position 'pos'.
|
/// Insert 'text' at char position 'pos'.
|
||||||
pub fn insert_text(&mut self, text: &str, pos: uint) {
|
pub fn insert_text(&mut self, text: &str, pos: uint) {
|
||||||
self.root.insert_text(text, pos);
|
self.root.insert_text(text, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Remove the text between char positions 'pos_a' and '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) {
|
pub fn remove_text(&mut self, pos_a: uint, pos_b: uint) {
|
||||||
self.root.remove_text(pos_a, pos_b);
|
self.root.remove_text(pos_a, pos_b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
|
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
|
||||||
let mut node_stack: Vec<&'a TextNode> = Vec::new();
|
let mut node_stack: Vec<&'a TextNode> = Vec::new();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::fmt;
|
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
|
/// A block of text, contiguous in memory
|
||||||
pub struct TextBlock {
|
pub struct TextBlock {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::fmt;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::cmp::{min, max};
|
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;
|
use super::text_block::TextBlock;
|
||||||
|
|
||||||
const MIN_LEAF_SIZE: uint = 64;
|
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,
|
/// 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.
|
/// or the total character length of the node if there are no newlines.
|
||||||
pub fn tail_len(&self) -> uint {
|
pub fn tail_len(&self) -> uint {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use buffer::TextBuffer;
|
use buffer::TextBuffer;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use files::{load_file_to_buffer, save_buffer_to_file};
|
use files::{load_file_to_buffer, save_buffer_to_file};
|
||||||
|
use string_utils::char_count;
|
||||||
|
|
||||||
|
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
|
@ -58,19 +59,15 @@ impl Editor {
|
||||||
|
|
||||||
pub fn insert_text_at_cursor(&mut self, text: &str) {
|
pub fn insert_text_at_cursor(&mut self, text: &str) {
|
||||||
let pos = self.buffer.pos_2d_to_closest_1d(self.cursor);
|
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.buffer.insert_text(text, pos);
|
||||||
|
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
||||||
// TODO: handle multi-character strings properly
|
// Move cursor
|
||||||
if text == "\n" {
|
self.cursor = self.buffer.pos_1d_to_closest_2d(p + str_len);
|
||||||
self.cursor.0 += 1;
|
|
||||||
self.cursor.1 = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.cursor.1 += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_text_at_char(&mut self, text: &str, pos: uint) {
|
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_b = self.buffer.pos_2d_to_closest_1d(self.cursor);
|
||||||
let pos_a = if pos_b >= char_count {pos_b - char_count} else {0};
|
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.buffer.remove_text(pos_a, pos_b);
|
||||||
|
|
||||||
self.dirty = true;
|
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) {
|
pub fn cursor_left(&mut self) {
|
||||||
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
|
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 {
|
if p > 0 {
|
||||||
self.cursor.1 -= 1;
|
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) {
|
pub fn cursor_right(&mut self) {
|
||||||
self.cursor.1 += 1;
|
|
||||||
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
|
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) {
|
pub fn cursor_up(&mut self) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ use docopt::Docopt;
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
use term_ui::TermUI;
|
use term_ui::TermUI;
|
||||||
|
|
||||||
|
mod string_utils;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
mod files;
|
mod files;
|
||||||
mod editor;
|
mod editor;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user