WIP refactor to switch over to Rope from BufferNode.
This commit is contained in:
parent
0ee364f6ce
commit
02f24e4e6f
|
@ -5,15 +5,12 @@ use std::old_path::Path;
|
|||
use std::old_io::fs::File;
|
||||
use std::old_io::{IoResult, BufferedReader, BufferedWriter};
|
||||
|
||||
use self::line::Line;
|
||||
use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
||||
pub use self::rope::{Rope, RopeSlice, RopeGraphemeIter, RopeLineIter};
|
||||
use self::undo_stack::{UndoStack};
|
||||
use self::undo_stack::Operation::*;
|
||||
use string_utils::{is_line_ending, grapheme_count};
|
||||
use string_utils::grapheme_count;
|
||||
|
||||
pub mod line;
|
||||
mod rope;
|
||||
mod node;
|
||||
mod undo_stack;
|
||||
|
||||
|
||||
|
@ -23,7 +20,7 @@ mod undo_stack;
|
|||
|
||||
/// A text buffer
|
||||
pub struct Buffer {
|
||||
text: BufferNode,
|
||||
text: Rope,
|
||||
file_path: Option<Path>,
|
||||
undo_stack: UndoStack,
|
||||
}
|
||||
|
@ -33,7 +30,7 @@ pub struct Buffer {
|
|||
impl Buffer {
|
||||
pub fn new() -> Buffer {
|
||||
Buffer {
|
||||
text: BufferNode::new(),
|
||||
text: Rope::new(),
|
||||
file_path: None,
|
||||
undo_stack: UndoStack::new(),
|
||||
}
|
||||
|
@ -42,58 +39,23 @@ impl Buffer {
|
|||
|
||||
pub fn new_from_file(path: &Path) -> IoResult<Buffer> {
|
||||
let mut f = BufferedReader::new(try!(File::open(path)));
|
||||
let string = f.read_to_string().unwrap();
|
||||
|
||||
let mut buf = Buffer {
|
||||
text: BufferNode::new(),
|
||||
let buf = Buffer {
|
||||
text: Rope::new_from_str(string.as_slice()),
|
||||
file_path: Some(path.clone()),
|
||||
undo_stack: UndoStack::new(),
|
||||
};
|
||||
|
||||
let string = f.read_to_string().unwrap();
|
||||
let mut g_iter = string.as_slice().grapheme_indices(true);
|
||||
let mut done = false;
|
||||
let mut a = 0;
|
||||
let mut b = 0;
|
||||
|
||||
while !done {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
if let Some((i, g)) = g_iter.next() {
|
||||
count += 1;
|
||||
b = i + g.len();
|
||||
if is_line_ending(g) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if a != b {
|
||||
let substr = &string[a..b];
|
||||
let line = Line::new_from_str_with_count_unchecked(substr, count);
|
||||
let node = BufferNode::new_from_line_with_count_unchecked(line, count);
|
||||
buf.append_leaf_node_unchecked(node);
|
||||
}
|
||||
|
||||
a = b;
|
||||
}
|
||||
|
||||
// Remove initial blank line
|
||||
buf.remove_lines(0, 1);
|
||||
|
||||
return Ok(buf);
|
||||
}
|
||||
|
||||
|
||||
pub fn save_to_file(&self, path: &Path) -> IoResult<()> {
|
||||
// TODO: make more efficient
|
||||
let mut f = BufferedWriter::new(try!(File::create(path)));
|
||||
|
||||
for g in self.grapheme_iter() {
|
||||
let _ = f.write_str(g);
|
||||
for c in self.text.chunk_iter() {
|
||||
let _ = f.write_str(c);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
@ -107,12 +69,12 @@ impl Buffer {
|
|||
//------------------------------------------------------------------------
|
||||
|
||||
pub fn grapheme_count(&self) -> usize {
|
||||
self.text.grapheme_count
|
||||
self.text.grapheme_count()
|
||||
}
|
||||
|
||||
|
||||
pub fn line_count(&self) -> usize {
|
||||
self.text.line_count
|
||||
self.text.line_count()
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,7 +92,7 @@ impl Buffer {
|
|||
}
|
||||
|
||||
fn _insert_text(&mut self, text: &str, pos: usize) {
|
||||
self.text.insert_text(text, pos);
|
||||
self.text.insert_text_at_grapheme_index(text, pos);
|
||||
}
|
||||
|
||||
|
||||
|
@ -173,16 +135,13 @@ impl Buffer {
|
|||
panic!("Buffer::_remove_text(): attempt to remove text past the end of buffer.");
|
||||
}
|
||||
// Complete removal of all text
|
||||
else if pos_a == 0 && pos_b == self.text.grapheme_count {
|
||||
let mut temp_node = BufferNode::new();
|
||||
else if pos_a == 0 && pos_b == self.text.grapheme_count() {
|
||||
let mut temp_node = Rope::new();
|
||||
mem::swap(&mut (self.text), &mut temp_node);
|
||||
}
|
||||
// All other cases
|
||||
else {
|
||||
if self.text.remove_text_recursive(pos_a, pos_b, true) {
|
||||
panic!("Buffer::_remove_text(): dangling left side remains. This should never happen!");
|
||||
}
|
||||
self.text.set_last_line_ending_recursive();
|
||||
self.text.remove_text_between_grapheme_indices(pos_a, pos_b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,6 +191,7 @@ impl Buffer {
|
|||
|
||||
|
||||
/// Removes the lines in line indices [line_a, line_b).
|
||||
/// TODO: undo
|
||||
pub fn remove_lines(&mut self, line_a: usize, line_b: usize) {
|
||||
// Nothing to do
|
||||
if line_a == line_b {
|
||||
|
@ -246,32 +206,25 @@ impl Buffer {
|
|||
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.text.line_count {
|
||||
let mut temp_node = BufferNode::new();
|
||||
else if line_a == 0 && line_b == self.text.line_count() {
|
||||
let mut temp_node = Rope::new();
|
||||
mem::swap(&mut (self.text), &mut temp_node);
|
||||
}
|
||||
// All other cases
|
||||
else {
|
||||
self.text.remove_lines_recursive(line_a, line_b);
|
||||
self.text.set_last_line_ending_recursive();
|
||||
let a = self.text.line_index_to_grapheme_index(line_a);
|
||||
let b = if line_b < self.line_count() {
|
||||
self.text.line_index_to_grapheme_index(line_b)
|
||||
}
|
||||
else {
|
||||
self.text.grapheme_count()
|
||||
};
|
||||
|
||||
self.text.remove_text_between_grapheme_indices(a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Blindly appends a line to the end of the current text without
|
||||
/// doing any sanity checks. This is primarily for efficient
|
||||
/// file loading.
|
||||
pub fn append_line_unchecked(&mut self, line: Line) {
|
||||
self.text.append_line_unchecked_recursive(line);
|
||||
}
|
||||
|
||||
|
||||
fn append_leaf_node_unchecked(&mut self, node: BufferNode) {
|
||||
self.text.append_leaf_node_unchecked_recursive(node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
@ -359,7 +312,7 @@ impl Buffer {
|
|||
/// If the index is off the end of the text, returns the line and column
|
||||
/// number of the last valid text position.
|
||||
pub fn index_to_line_col(&self, pos: usize) -> (usize, usize) {
|
||||
return self.text.index_to_line_col_recursive(pos);
|
||||
return self.text.grapheme_index_to_line_col(pos);
|
||||
}
|
||||
|
||||
|
||||
|
@ -371,7 +324,7 @@ impl Buffer {
|
|||
/// beyond the end of the buffer, returns the index of the buffer's last
|
||||
/// valid position.
|
||||
pub fn line_col_to_index(&self, pos: (usize, usize)) -> usize {
|
||||
return self.text.line_col_to_index_recursive(pos);
|
||||
return self.text.line_col_to_grapheme_index(pos);
|
||||
}
|
||||
|
||||
|
||||
|
@ -384,20 +337,25 @@ impl Buffer {
|
|||
panic!("Buffer::get_grapheme(): index past last grapheme.");
|
||||
}
|
||||
else {
|
||||
return self.text.get_grapheme_recursive(index);
|
||||
return &self.text[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn get_line<'a>(&'a self, index: usize) -> &'a Line {
|
||||
pub fn get_line<'a>(&'a self, index: usize) -> RopeSlice<'a> {
|
||||
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.text.get_line_recursive(index);
|
||||
let a = self.text.line_index_to_grapheme_index(index);
|
||||
let b = if index+1 < self.line_count() {
|
||||
self.text.line_index_to_grapheme_index(index+1)
|
||||
}
|
||||
else {
|
||||
self.text.grapheme_count()
|
||||
};
|
||||
|
||||
return self.text.slice(a, b);
|
||||
}
|
||||
|
||||
|
||||
|
@ -416,7 +374,7 @@ impl Buffer {
|
|||
let mut i = 0;
|
||||
let i_end = pos_b - pos_a;
|
||||
|
||||
for g in self.grapheme_iter_at_index(pos_a) {
|
||||
for g in self.text.grapheme_iter_at_index(pos_a) {
|
||||
if i == i_end {
|
||||
break;
|
||||
}
|
||||
|
@ -436,34 +394,26 @@ impl Buffer {
|
|||
//------------------------------------------------------------------------
|
||||
|
||||
/// Creates an iterator at the first character
|
||||
pub fn grapheme_iter<'a>(&'a self) -> BufferGraphemeIter<'a> {
|
||||
BufferGraphemeIter {
|
||||
gi: self.text.grapheme_iter()
|
||||
}
|
||||
pub fn grapheme_iter<'a>(&'a self) -> RopeGraphemeIter<'a> {
|
||||
self.text.grapheme_iter()
|
||||
}
|
||||
|
||||
|
||||
/// Creates an iterator starting at the specified grapheme index.
|
||||
/// If the index is past the end of the text, then the iterator will
|
||||
/// return None on next().
|
||||
pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> BufferGraphemeIter<'a> {
|
||||
BufferGraphemeIter {
|
||||
gi: self.text.grapheme_iter_at_index(index)
|
||||
}
|
||||
pub fn grapheme_iter_at_index<'a>(&'a self, index: usize) -> RopeGraphemeIter<'a> {
|
||||
self.text.grapheme_iter_at_index(index)
|
||||
}
|
||||
|
||||
|
||||
pub fn line_iter<'a>(&'a self) -> BufferLineIter<'a> {
|
||||
BufferLineIter {
|
||||
li: self.text.line_iter()
|
||||
}
|
||||
pub fn line_iter<'a>(&'a self) -> RopeLineIter<'a> {
|
||||
self.text.line_iter()
|
||||
}
|
||||
|
||||
|
||||
pub fn line_iter_at_index<'a>(&'a self, index: usize) -> BufferLineIter<'a> {
|
||||
BufferLineIter {
|
||||
li: self.text.line_iter_at_index(index)
|
||||
}
|
||||
pub fn line_iter_at_index<'a>(&'a self, index: usize) -> RopeLineIter<'a> {
|
||||
self.text.line_iter_at_index(index)
|
||||
}
|
||||
|
||||
|
||||
|
@ -471,77 +421,6 @@ impl Buffer {
|
|||
|
||||
|
||||
|
||||
|
||||
//=============================================================
|
||||
// Buffer iterators
|
||||
//=============================================================
|
||||
|
||||
/// An iterator over a text buffer's graphemes
|
||||
pub struct BufferGraphemeIter<'a> {
|
||||
gi: BufferNodeGraphemeIter<'a>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> BufferGraphemeIter<'a> {
|
||||
// Puts the iterator on the next line.
|
||||
// Returns true if there was a next line,
|
||||
// false if there wasn't.
|
||||
pub fn next_line(&mut self) -> bool {
|
||||
self.gi.next_line()
|
||||
}
|
||||
|
||||
|
||||
// Skips the iterator n graphemes ahead.
|
||||
// If it runs out of graphemes before reaching the desired skip count,
|
||||
// returns false. Otherwise returns true.
|
||||
pub fn skip_graphemes(&mut self, n: usize) -> bool {
|
||||
self.gi.skip_graphemes(n)
|
||||
}
|
||||
|
||||
//pub fn skip_non_newline_graphemes(&mut self, n: usize) -> bool {
|
||||
// let mut i: usize = 0;
|
||||
//
|
||||
// for g in self.gi {
|
||||
// if is_line_ending(g) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// i += 1;
|
||||
// if i >= n {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Iterator for BufferGraphemeIter<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
self.gi.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct BufferLineIter<'a> {
|
||||
li: BufferNodeLineIter<'a>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Iterator for BufferLineIter<'a> {
|
||||
type Item = &'a Line;
|
||||
|
||||
fn next(&mut self) -> Option<&'a Line> {
|
||||
self.li.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================================================================
|
||||
// TESTS
|
||||
//================================================================
|
||||
|
@ -549,7 +428,7 @@ impl<'a> Iterator for BufferLineIter<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(unused_imports)]
|
||||
use super::{Buffer, BufferGraphemeIter, BufferLineIter};
|
||||
use super::{Buffer, BufferLineIter};
|
||||
|
||||
#[test]
|
||||
fn insert_text() {
|
||||
|
|
|
@ -8,7 +8,9 @@ use string_utils::{
|
|||
insert_text_at_grapheme_index,
|
||||
remove_text_between_grapheme_indices,
|
||||
split_string_at_grapheme_index,
|
||||
is_line_ending
|
||||
is_line_ending,
|
||||
LineEnding,
|
||||
str_to_line_ending,
|
||||
};
|
||||
|
||||
pub const MIN_NODE_SIZE: usize = 64;
|
||||
|
@ -259,6 +261,11 @@ impl Rope {
|
|||
}
|
||||
|
||||
|
||||
pub fn grapheme_at_index<'a>(&'a self, index: usize) -> &'a str {
|
||||
&self[index]
|
||||
}
|
||||
|
||||
|
||||
/// Inserts the given text at the given grapheme index.
|
||||
/// For small lengths of 'text' runs in O(log N) time.
|
||||
/// For large lengths of 'text', dunno. But it seems to perform
|
||||
|
@ -478,10 +485,50 @@ impl Rope {
|
|||
return RopeGraphemeIter {
|
||||
chunk_iter: chunk_iter,
|
||||
cur_chunk: giter,
|
||||
length: None,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Creates an iterator that starts a pos_a and stops just before pos_b.
|
||||
pub fn grapheme_iter_between_indices<'a>(&'a self, pos_a: usize, pos_b: usize) -> RopeGraphemeIter<'a> {
|
||||
let mut iter = self.grapheme_iter_at_index(pos_a);
|
||||
iter.length = Some(pos_b - pos_a);
|
||||
return iter;
|
||||
}
|
||||
|
||||
|
||||
/// Creates an iterator over the lines in the rope.
|
||||
pub fn line_iter<'a>(&'a self) -> RopeLineIter<'a> {
|
||||
RopeLineIter {
|
||||
rope: self,
|
||||
li: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Creates an iterator over the lines in the rope, starting at the given
|
||||
/// line index.
|
||||
pub fn line_iter_at_index<'a>(&'a self, index: usize) -> RopeLineIter<'a> {
|
||||
RopeLineIter {
|
||||
rope: self,
|
||||
li: index,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn slice<'a>(&'a self, pos_a: usize, pos_b: usize) -> RopeSlice<'a> {
|
||||
let a = pos_a;
|
||||
let b = min(self.grapheme_count_, pos_b);
|
||||
|
||||
RopeSlice {
|
||||
rope: self,
|
||||
start: a,
|
||||
end: b,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Creates a graphviz document of the Rope's structure, and returns
|
||||
// it as a string. For debugging purposes.
|
||||
pub fn to_graphviz(&self) -> String {
|
||||
|
@ -944,6 +991,7 @@ impl<'a> Iterator for RopeChunkIter<'a> {
|
|||
pub struct RopeGraphemeIter<'a> {
|
||||
chunk_iter: RopeChunkIter<'a>,
|
||||
cur_chunk: Graphemes<'a>,
|
||||
length: Option<usize>,
|
||||
}
|
||||
|
||||
|
||||
|
@ -951,8 +999,17 @@ impl<'a> Iterator for RopeGraphemeIter<'a> {
|
|||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
if let Some(ref mut l) = self.length {
|
||||
if *l == 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
if let Some(g) = self.cur_chunk.next() {
|
||||
if let Some(ref mut l) = self.length {
|
||||
*l -= 1;
|
||||
}
|
||||
return Some(g);
|
||||
}
|
||||
else {
|
||||
|
@ -970,6 +1027,106 @@ impl<'a> Iterator for RopeGraphemeIter<'a> {
|
|||
|
||||
|
||||
|
||||
/// An iterator over a rope's lines, returned as RopeSlice's
|
||||
pub struct RopeLineIter<'a> {
|
||||
rope: &'a Rope,
|
||||
li: usize,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Iterator for RopeLineIter<'a> {
|
||||
type Item = RopeSlice<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<RopeSlice<'a>> {
|
||||
if self.li >= self.rope.line_count() {
|
||||
return None;
|
||||
}
|
||||
else {
|
||||
let a = self.rope.line_index_to_grapheme_index(self.li);
|
||||
let b = if self.li+1 < self.rope.line_count() {
|
||||
self.rope.line_index_to_grapheme_index(self.li+1)
|
||||
}
|
||||
else {
|
||||
self.rope.grapheme_count()
|
||||
};
|
||||
|
||||
self.li += 1;
|
||||
|
||||
return Some(self.rope.slice(a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============================================================
|
||||
// Rope slice
|
||||
//=============================================================
|
||||
|
||||
/// An immutable slice into a Rope
|
||||
pub struct RopeSlice<'a> {
|
||||
rope: &'a Rope,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> RopeSlice<'a> {
|
||||
pub fn grapheme_count(&self) -> usize {
|
||||
self.end - self.start
|
||||
}
|
||||
|
||||
|
||||
pub fn grapheme_iter(&self) -> RopeGraphemeIter<'a> {
|
||||
self.rope.grapheme_iter_between_indices(self.start, self.end)
|
||||
}
|
||||
|
||||
pub fn grapheme_iter_at_index(&self, pos: usize) -> RopeGraphemeIter<'a> {
|
||||
let a = min(self.end, self.start + pos);
|
||||
|
||||
self.rope.grapheme_iter_between_indices(a, self.end)
|
||||
}
|
||||
|
||||
pub fn grapheme_iter_between_indices(&self, pos_a: usize, pos_b: usize) -> RopeGraphemeIter<'a> {
|
||||
let a = min(self.end, self.start + pos_a);
|
||||
let b = min(self.end, self.start + pos_b);
|
||||
|
||||
self.rope.grapheme_iter_between_indices(a, b)
|
||||
}
|
||||
|
||||
|
||||
pub fn grapheme_at_index(&self, index: usize) -> &'a str {
|
||||
&self.rope[self.start+index]
|
||||
}
|
||||
|
||||
|
||||
/// Convenience function for when the slice represents a line
|
||||
pub fn ending(&self) -> LineEnding {
|
||||
if self.grapheme_count() > 0 {
|
||||
let g = self.grapheme_at_index(self.grapheme_count() - 1);
|
||||
return str_to_line_ending(g);
|
||||
}
|
||||
else {
|
||||
return LineEnding::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn slice(&self, pos_a: usize, pos_b: usize) -> RopeSlice<'a> {
|
||||
let a = min(self.end, self.start + pos_a);
|
||||
let b = min(self.end, self.start + pos_b);
|
||||
|
||||
RopeSlice {
|
||||
rope: self.rope,
|
||||
start: a,
|
||||
end: b,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===================================================================
|
||||
// Unit test
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use buffer::Buffer;
|
||||
use buffer::line::LineEnding;
|
||||
use formatter::LineFormatter;
|
||||
use formatter::RoundingBehavior::*;
|
||||
use std::old_path::Path;
|
||||
use std::cmp::{min, max};
|
||||
use string_utils::grapheme_count;
|
||||
use string_utils::{grapheme_count, LineEnding};
|
||||
use utils::digit_count;
|
||||
use self::cursor::CursorSet;
|
||||
|
||||
|
@ -103,7 +102,7 @@ impl<T: LineFormatter> Editor<T> {
|
|||
// Collect statistics
|
||||
let mut line_i: usize = 0;
|
||||
for line in self.buffer.line_iter() {
|
||||
match line.ending {
|
||||
match line.ending() {
|
||||
LineEnding::None => {
|
||||
},
|
||||
LineEnding::CRLF => {
|
||||
|
|
|
@ -47,7 +47,7 @@ pub trait LineFormatter {
|
|||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
||||
|
||||
// Get an iter into the right block
|
||||
let g_iter = line.grapheme_iter_at_index_with_max_length(line_block * LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH);
|
||||
let g_iter = line.grapheme_iter_between_indices(line_block * LINE_BLOCK_LENGTH, line_block * (LINE_BLOCK_LENGTH+1));
|
||||
return self.index_to_v2d(g_iter, col_i_adjusted).1;
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ pub trait LineFormatter {
|
|||
// Find the right block in the line, and the index within that block
|
||||
let (line_block, col_i_adjusted) = block_index_and_offset(col_i);
|
||||
|
||||
let (mut y, x) = self.index_to_v2d(buf.get_line(line_i).grapheme_iter_at_index_with_max_length(line_block*LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH), col_i_adjusted);
|
||||
let (mut y, x) = self.index_to_v2d(buf.get_line(line_i).grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1)), col_i_adjusted);
|
||||
let mut new_y = y as isize + offset;
|
||||
|
||||
// First, find the right line while keeping track of the vertical offset
|
||||
|
@ -72,7 +72,7 @@ pub trait LineFormatter {
|
|||
let mut block_index: usize = line_block;
|
||||
loop {
|
||||
line = buf.get_line(line_i);
|
||||
let (h, _) = self.dimensions(line.grapheme_iter_at_index_with_max_length(line_block*LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH));
|
||||
let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1)));
|
||||
|
||||
if new_y >= 0 && new_y < h as isize {
|
||||
y = new_y as usize;
|
||||
|
@ -110,7 +110,7 @@ pub trait LineFormatter {
|
|||
else {
|
||||
block_index -= 1;
|
||||
}
|
||||
let (h, _) = self.dimensions(line.grapheme_iter_at_index_with_max_length(line_block*LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH));
|
||||
let (h, _) = self.dimensions(line.grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1)));
|
||||
new_y += h as isize;
|
||||
}
|
||||
else {
|
||||
|
@ -121,7 +121,7 @@ pub trait LineFormatter {
|
|||
|
||||
// Next, convert the resulting coordinates back into buffer-wide
|
||||
// coordinates.
|
||||
col_i = (block_index * LINE_BLOCK_LENGTH) + self.v2d_to_index(line.grapheme_iter_at_index_with_max_length(block_index*LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH), (y, x), rounding);
|
||||
col_i = (block_index * LINE_BLOCK_LENGTH) + self.v2d_to_index(line.grapheme_iter_between_indices(line_block*LINE_BLOCK_LENGTH, line_block*(LINE_BLOCK_LENGTH+1)), (y, x), rounding);
|
||||
|
||||
return buf.line_col_to_index((line_i, col_i));
|
||||
}
|
||||
|
@ -136,8 +136,8 @@ pub trait LineFormatter {
|
|||
let start_index = line_block * LINE_BLOCK_LENGTH;
|
||||
|
||||
// Calculate the horizontal position
|
||||
let (v, _) = self.index_to_v2d(line.grapheme_iter_at_index_with_max_length(start_index, LINE_BLOCK_LENGTH), col_i_adjusted);
|
||||
let mut new_col_i = start_index + self.v2d_to_index(line.grapheme_iter_at_index_with_max_length(start_index, LINE_BLOCK_LENGTH), (v, horizontal), (RoundingBehavior::Floor, rounding));
|
||||
let (v, _) = self.index_to_v2d(line.grapheme_iter_between_indices(start_index, start_index+LINE_BLOCK_LENGTH), col_i_adjusted);
|
||||
let mut new_col_i = start_index + self.v2d_to_index(line.grapheme_iter_between_indices(start_index, start_index+LINE_BLOCK_LENGTH), (v, horizontal), (RoundingBehavior::Floor, rounding));
|
||||
|
||||
// Make sure we're not pushing the index off the end of the line
|
||||
if (line_i + 1) < buf.line_count()
|
||||
|
|
|
@ -189,4 +189,95 @@ pub fn split_string_at_grapheme_index(s1: &mut String, pos: usize) -> String {
|
|||
}
|
||||
|
||||
return s2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Represents one of the valid Unicode line endings.
|
||||
/// Also acts as an index into `LINE_ENDINGS`.
|
||||
#[derive(PartialEq, Copy)]
|
||||
pub enum LineEnding {
|
||||
None = 0, // No line ending
|
||||
CRLF = 1, // CarriageReturn followed by LineFeed
|
||||
LF = 2, // U+000A -- LineFeed
|
||||
VT = 3, // U+000B -- VerticalTab
|
||||
FF = 4, // U+000C -- FormFeed
|
||||
CR = 5, // U+000D -- CarriageReturn
|
||||
NEL = 6, // U+0085 -- NextLine
|
||||
LS = 7, // U+2028 -- Line Separator
|
||||
PS = 8, // U+2029 -- ParagraphSeparator
|
||||
}
|
||||
|
||||
pub fn str_to_line_ending(g: &str) -> LineEnding {
|
||||
match g {
|
||||
//==============
|
||||
// Line endings
|
||||
//==============
|
||||
|
||||
// CRLF
|
||||
"\u{000D}\u{000A}" => {
|
||||
return LineEnding::CRLF;
|
||||
},
|
||||
|
||||
// LF
|
||||
"\u{000A}" => {
|
||||
return LineEnding::LF;
|
||||
},
|
||||
|
||||
// VT
|
||||
"\u{000B}" => {
|
||||
return LineEnding::VT;
|
||||
},
|
||||
|
||||
// FF
|
||||
"\u{000C}" => {
|
||||
return LineEnding::FF;
|
||||
},
|
||||
|
||||
// CR
|
||||
"\u{000D}" => {
|
||||
return LineEnding::CR;
|
||||
},
|
||||
|
||||
// NEL
|
||||
"\u{0085}" => {
|
||||
return LineEnding::NEL;
|
||||
},
|
||||
|
||||
// LS
|
||||
"\u{2028}" => {
|
||||
return LineEnding::LS;
|
||||
},
|
||||
|
||||
// PS
|
||||
"\u{2029}" => {
|
||||
return LineEnding::PS;
|
||||
},
|
||||
|
||||
// Not a line ending
|
||||
_ => {
|
||||
return LineEnding::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_ending_to_str(ending: LineEnding) -> &'static str {
|
||||
LINE_ENDINGS[ending as usize]
|
||||
}
|
||||
|
||||
/// An array of string literals corresponding to the possible
|
||||
/// unicode line endings.
|
||||
pub const LINE_ENDINGS: [&'static str; 9] = ["",
|
||||
"\u{000D}\u{000A}",
|
||||
"\u{000A}",
|
||||
"\u{000B}",
|
||||
"\u{000C}",
|
||||
"\u{000D}",
|
||||
"\u{0085}",
|
||||
"\u{2028}",
|
||||
"\u{2029}"
|
||||
];
|
||||
|
|
|
@ -6,9 +6,8 @@ use editor::Editor;
|
|||
use formatter::{LineFormatter, LINE_BLOCK_LENGTH, block_index_and_offset};
|
||||
use std::char;
|
||||
use std::time::duration::Duration;
|
||||
use string_utils::{is_line_ending};
|
||||
use string_utils::{is_line_ending, line_ending_to_str, LineEnding};
|
||||
use utils::digit_count;
|
||||
use buffer::line::{line_ending_to_str, LineEnding};
|
||||
use self::formatter::ConsoleLineFormatter;
|
||||
|
||||
pub mod formatter;
|
||||
|
@ -360,7 +359,7 @@ impl TermUI {
|
|||
let (line_index, col_i) = editor.buffer.index_to_line_col(editor.view_pos.0);
|
||||
let (mut line_block_index, _) = block_index_and_offset(col_i);
|
||||
let mut grapheme_index = editor.buffer.line_col_to_index((line_index, line_block_index * LINE_BLOCK_LENGTH));
|
||||
let (vis_line_offset, _) = editor.formatter.index_to_v2d(editor.buffer.get_line(line_index).grapheme_iter_at_index_with_max_length(line_block_index*LINE_BLOCK_LENGTH, LINE_BLOCK_LENGTH), editor.view_pos.0 - grapheme_index);
|
||||
let (vis_line_offset, _) = editor.formatter.index_to_v2d(editor.buffer.get_line(line_index).grapheme_iter_between_indices(line_block_index*LINE_BLOCK_LENGTH, (line_block_index+1)*LINE_BLOCK_LENGTH), editor.view_pos.0 - grapheme_index);
|
||||
|
||||
let mut screen_line = c1.0 as isize - vis_line_offset as isize;
|
||||
let screen_col = c1.1 as isize + gutter_width as isize;
|
||||
|
|
Loading…
Reference in New Issue
Block a user