Scrolling in the editor by following the cursor now works.
This commit is contained in:
parent
833e92c5a0
commit
74cd79745f
|
@ -61,6 +61,7 @@ impl TextBuffer {
|
|||
}
|
||||
|
||||
|
||||
/// Creates an iterator at the first character
|
||||
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
|
||||
let mut node_stack: Vec<&'a TextNode> = Vec::new();
|
||||
let mut cur_node = &self.root;
|
||||
|
@ -86,6 +87,49 @@ impl TextBuffer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Creates an iterator starting at the specified character index.
|
||||
/// If the index is past the end of the text, then the iterator will
|
||||
/// return None on next().
|
||||
pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> {
|
||||
let mut node_stack: Vec<&'a TextNode> = Vec::new();
|
||||
let mut cur_node = &self.root;
|
||||
let mut char_i = index;
|
||||
|
||||
loop {
|
||||
match cur_node.data {
|
||||
TextNodeData::Leaf(_) => {
|
||||
let mut char_iter = match cur_node.data {
|
||||
TextNodeData::Leaf(ref tb) => tb.as_str().chars(),
|
||||
_ => panic!("This should never happen.")
|
||||
};
|
||||
|
||||
while char_i > 0 {
|
||||
char_iter.next();
|
||||
char_i -= 1;
|
||||
}
|
||||
|
||||
return TextBufferIter {
|
||||
node_stack: node_stack,
|
||||
cur_block: char_iter,
|
||||
};
|
||||
},
|
||||
|
||||
TextNodeData::Branch(ref left, ref right) => {
|
||||
if left.char_count > char_i {
|
||||
node_stack.push(&(**right));
|
||||
cur_node = &(**left);
|
||||
}
|
||||
else {
|
||||
cur_node = &(**right);
|
||||
char_i -= left.char_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl fmt::Show for TextBuffer {
|
||||
|
@ -129,6 +173,30 @@ impl<'a> TextBufferIter<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Skips the iterator n characters ahead, unless it hits a newline
|
||||
// character. If it hits a newline character, returns true, otherwise,
|
||||
// false.
|
||||
pub fn skip_non_newline_chars(&mut self, n: uint) -> bool {
|
||||
// TODO: more efficient implementation, taking advantage of rope
|
||||
// structure.
|
||||
for _ in range(0, n) {
|
||||
match self.next() {
|
||||
Option::Some(c) => {
|
||||
if c == '\n' {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
Option::None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -57,6 +57,26 @@ impl Editor {
|
|||
self.view_dim = (h, w);
|
||||
}
|
||||
|
||||
|
||||
/// Moves the editor's view the minimum amount to show the cursor
|
||||
pub fn move_view_to_cursor(&mut self) {
|
||||
// Horizontal
|
||||
if self.cursor.1 < self.view_pos.1 {
|
||||
self.view_pos.1 = self.cursor.1;
|
||||
}
|
||||
else if self.cursor.1 >= (self.view_pos.1 + self.view_dim.1) {
|
||||
self.view_pos.1 = 1 + self.cursor.1 - self.view_dim.1;
|
||||
}
|
||||
|
||||
// Vertical
|
||||
if self.cursor.0 < self.view_pos.0 {
|
||||
self.view_pos.0 = self.cursor.0;
|
||||
}
|
||||
else if self.cursor.0 >= (self.view_pos.0 + self.view_dim.0) {
|
||||
self.view_pos.0 = 1 + self.cursor.0 - self.view_dim.0;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -68,6 +88,8 @@ impl Editor {
|
|||
|
||||
// Move cursor
|
||||
self.cursor = self.buffer.pos_1d_to_closest_2d(p + str_len);
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
|
||||
pub fn insert_text_at_char(&mut self, text: &str, pos: uint) {
|
||||
|
@ -87,6 +109,8 @@ impl Editor {
|
|||
self.buffer.remove_text(pos_a, pos_b);
|
||||
|
||||
self.dirty = true;
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
|
||||
pub fn cursor_left(&mut self) {
|
||||
|
@ -98,22 +122,30 @@ impl Editor {
|
|||
else {
|
||||
self.cursor = self.buffer.pos_1d_to_closest_2d(0);
|
||||
}
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
|
||||
pub fn cursor_right(&mut self) {
|
||||
let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
|
||||
self.cursor = self.buffer.pos_1d_to_closest_2d(p + 1);
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
|
||||
pub fn cursor_up(&mut self) {
|
||||
if self.cursor.0 > 0 {
|
||||
self.cursor.0 -= 1;
|
||||
}
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
|
||||
pub fn cursor_down(&mut self) {
|
||||
if self.cursor.0 < self.buffer.newline_count() {
|
||||
self.cursor.0 += 1;
|
||||
}
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ impl TermUI {
|
|||
|
||||
let mut width = self.rb.width();
|
||||
let mut height = self.rb.height();
|
||||
self.editor.update_dim(height, width);
|
||||
|
||||
loop {
|
||||
// Draw the editor to screen
|
||||
|
@ -121,6 +122,7 @@ impl TermUI {
|
|||
Ok(rustbox::Event::ResizeEvent(w, h)) => {
|
||||
width = w as uint;
|
||||
height = h as uint;
|
||||
self.editor.update_dim(height, width);
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -140,12 +142,14 @@ impl TermUI {
|
|||
}
|
||||
|
||||
pub fn draw_editor(&self, editor: &Editor, c1: (uint, uint), c2: (uint, uint)) {
|
||||
let mut tb_iter = editor.buffer.root_iter();
|
||||
let mut line: uint = 0;
|
||||
let mut column: uint = 0;
|
||||
let mut pos: uint = 0;
|
||||
let height = c2.0 - c1.0;
|
||||
let width = c2.1 - c1.1;
|
||||
let mut tb_iter = editor.buffer.iter_at_char(editor.buffer.pos_2d_to_closest_1d(editor.view_pos));
|
||||
let mut pline = c1.0;
|
||||
let mut pcol = c1.1;
|
||||
let mut line = editor.view_pos.0;
|
||||
let mut column = editor.view_pos.1;
|
||||
let mut pos = editor.buffer.pos_2d_to_closest_1d(editor.view_pos);
|
||||
let max_line = line + (c2.0 - c1.0);
|
||||
let max_col = column + (c2.1 - c1.1);
|
||||
|
||||
let cursor_pos = editor.buffer.pos_2d_to_closest_1d(editor.cursor);
|
||||
|
||||
|
@ -153,20 +157,23 @@ impl TermUI {
|
|||
if let Option::Some(c) = tb_iter.next() {
|
||||
if c == '\n' {
|
||||
if pos == cursor_pos {
|
||||
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice());
|
||||
self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice());
|
||||
}
|
||||
|
||||
pline += 1;
|
||||
pcol = c1.1;
|
||||
line += 1;
|
||||
column = 0;
|
||||
}
|
||||
else {
|
||||
if pos == cursor_pos {
|
||||
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, c.to_string().as_slice());
|
||||
self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, c.to_string().as_slice());
|
||||
}
|
||||
else {
|
||||
self.rb.print(column, line, rustbox::RB_NORMAL, Color::White, Color::Black, c.to_string().as_slice());
|
||||
self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::White, Color::Black, c.to_string().as_slice());
|
||||
}
|
||||
|
||||
pcol += 1;
|
||||
column += 1;
|
||||
}
|
||||
}
|
||||
|
@ -174,24 +181,57 @@ impl TermUI {
|
|||
// Show cursor at end of document if it's past the end of
|
||||
// the document
|
||||
if cursor_pos >= pos {
|
||||
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
||||
self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
||||
}
|
||||
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
pos += 1;
|
||||
|
||||
if line > height {
|
||||
break;
|
||||
if line > max_line {
|
||||
return;
|
||||
}
|
||||
|
||||
if column > width {
|
||||
// If we're past the edge of the display, go to the next line
|
||||
if column > max_col {
|
||||
tb_iter.next_line();
|
||||
|
||||
pline += 1;
|
||||
pcol = c1.1;
|
||||
line += 1;
|
||||
column = 0;
|
||||
pos = editor.buffer.pos_2d_to_closest_1d((line, column));
|
||||
}
|
||||
|
||||
if line > max_line {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're before the edge of the display, move forward to get
|
||||
// to it.
|
||||
loop {
|
||||
if column < editor.view_pos.1 {
|
||||
let nl = tb_iter.skip_non_newline_chars(editor.view_pos.1);
|
||||
if !nl {
|
||||
column = editor.view_pos.1;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
pline += 1;
|
||||
line += 1;
|
||||
}
|
||||
|
||||
if line > max_line {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the 1d position of the char to be printed next
|
||||
pos = editor.buffer.pos_2d_to_closest_1d((line, column));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user