diff --git a/src/buffer/mod.rs b/src/buffer/mod.rs index a711f93..e1adc1b 100644 --- a/src/buffer/mod.rs +++ b/src/buffer/mod.rs @@ -2,7 +2,7 @@ use std::mem; - +use font::Font; use self::line::{Line, LineEnding}; use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter}; use self::undo_stack::{UndoStack}; @@ -24,6 +24,7 @@ pub struct Buffer { undo_stack: UndoStack, pub line_ending_type: LineEnding, pub tab_width: usize, + pub font: Option, } @@ -34,6 +35,7 @@ impl Buffer { undo_stack: UndoStack::new(), line_ending_type: LineEnding::LF, tab_width: 4, + font: None, } } diff --git a/src/editor/cursor.rs b/src/editor/cursor.rs new file mode 100644 index 0000000..2ab2797 --- /dev/null +++ b/src/editor/cursor.rs @@ -0,0 +1,112 @@ +#![allow(dead_code)] + +use std::slice::{Iter, IterMut}; +use std::ops::{Index, IndexMut}; +use std::cmp::Ordering; + +use buffer::Buffer; + + +/// A text cursor. Also represents selections when range.0 != range.1. +/// +/// `range` is a pair of 1d grapheme indexes into the text. +/// +/// `vis_start` is the visual 2d horizontal position of the cursor. This +/// doesn't affect editing operations at all, but is used for cursor movement. +#[derive(Copy)] +pub struct Cursor { + pub range: (usize, usize), // start, end + pub vis_start: usize, // start +} + +impl Cursor { + pub fn new() -> Cursor { + Cursor { + range: (0, 0), + vis_start: 0, + } + } + + pub fn update_vis_start(&mut self, buf: &Buffer) { + let (_, h) = buf.index_to_v2d(self.range.0); + self.vis_start = h; + } +} + + +/// A collection of cursors, managed to always be in a consistent +/// state for multi-cursor editing. +pub struct CursorSet { + cursors: Vec +} + + +impl CursorSet { + pub fn new() -> CursorSet { + CursorSet { + cursors: vec!(Cursor::new()), + } + } + + pub fn add_cursor(&mut self, cursor: Cursor) { + self.cursors.push(cursor); + self.make_consistent(); + } + + pub fn truncate(&mut self, len: usize) { + self.cursors.truncate(len); + } + + pub fn iter<'a>(&'a self) -> Iter<'a, Cursor> { + self.cursors.as_slice().iter() + } + + pub fn iter_mut<'a>(&'a mut self) -> IterMut<'a, Cursor> { + self.cursors.as_mut_slice().iter_mut() + } + + pub fn make_consistent(&mut self) { + // First, sort the cursors by starting position + self.cursors.sort_by(|a, b| { + if a.range.0 < b.range.0 { + Ordering::Less + } + else if a.range.0 > b.range.0 { + Ordering::Greater + } + else { + Ordering::Equal + } + }); + + // Next, merge overlapping cursors + let mut i = 0; + while i < (self.cursors.len()-1) { + if self.cursors[i].range.1 >= self.cursors[i+1].range.0 { + self.cursors[i].range.1 = self.cursors[i+1].range.1; + self.cursors.remove(i+1); + } + else { + i += 1; + } + } + } +} + + +impl Index for CursorSet { + type Output = Cursor; + + fn index<'a>(&'a self, _index: &usize) -> &'a Cursor { + &(self.cursors[*_index]) + } +} + + +impl IndexMut for CursorSet { + type Output = Cursor; + + fn index_mut<'a>(&'a mut self, _index: &usize) -> &'a mut Cursor { + &mut (self.cursors[*_index]) + } +} \ No newline at end of file diff --git a/src/editor.rs b/src/editor/mod.rs similarity index 90% rename from src/editor.rs rename to src/editor/mod.rs index 0cdd1d5..3be2b21 100644 --- a/src/editor.rs +++ b/src/editor/mod.rs @@ -5,32 +5,9 @@ use std::path::Path; use std::cmp::min; use files::{load_file_to_buffer, save_buffer_to_file}; use string_utils::grapheme_count; +use self::cursor::{Cursor, CursorSet}; - -/// A text cursor. Also represents selections when range.0 != range.1. -/// -/// `range` is a pair of 1d grapheme indexes into the text. -/// -/// `vis_start` is the visual 2d horizontal position of the cursor. This -/// doesn't affect editing operations at all, but is used for cursor movement. -pub struct Cursor { - pub range: (usize, usize), // start, end - pub vis_start: usize, // start -} - -impl Cursor { - pub fn new() -> Cursor { - Cursor { - range: (0, 0), - vis_start: 0, - } - } - - pub fn update_vis_start(&mut self, buf: &Buffer) { - let (_, h) = buf.index_to_v2d(self.range.0); - self.vis_start = h; - } -} +mod cursor; pub struct Editor { @@ -44,7 +21,7 @@ pub struct Editor { pub view_pos: (usize, usize), // (line, col) // The editing cursor position - pub cursors: Vec, + pub cursors: CursorSet, } @@ -58,7 +35,7 @@ impl Editor { dirty: false, view_dim: (0, 0), view_pos: (0, 0), - cursors: vec!(Cursor::new()), + cursors: CursorSet::new(), } } @@ -75,14 +52,15 @@ impl Editor { dirty: false, view_dim: (0, 0), view_pos: (0, 0), - cursors: vec!(Cursor::new()), + cursors: CursorSet::new(), }; - // // For multiple-cursor testing - // ed.cursors.push(Cursor::new()); - // ed.cursors[1].range.0 = 30; - // ed.cursors[1].range.1 = 30; - // ed.cursors[1].update_vis_start(&(ed.buffer)); + // For multiple-cursor testing + let mut cur = Cursor::new(); + cur.range.0 = 30; + cur.range.1 = 30; + cur.update_vis_start(&(ed.buffer)); + ed.cursors.add_cursor(cur); ed.auto_detect_indentation_style(); @@ -206,6 +184,8 @@ impl Editor { self.move_view_to_cursor(); self.dirty = true; + + self.cursors.make_consistent(); } } @@ -221,6 +201,8 @@ impl Editor { self.move_view_to_cursor(); self.dirty = true; + + self.cursors.make_consistent(); } } @@ -250,10 +232,12 @@ impl Editor { } pub fn insert_text_at_cursor(&mut self, text: &str) { + self.cursors.make_consistent(); + let str_len = grapheme_count(text); let mut offset = 0; - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { // Insert text self.buffer.insert_text(text, c.range.0 + offset); self.dirty = true; @@ -272,10 +256,12 @@ impl Editor { } pub fn insert_tab_at_cursor(&mut self) { + self.cursors.make_consistent(); + if self.soft_tabs { let mut offset = 0; - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { // Update cursor with offset c.range.0 += offset; c.range.1 += offset; @@ -319,9 +305,11 @@ impl Editor { } pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) { + self.cursors.make_consistent(); + let mut offset = 0; - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { // Update cursor with offset c.range.0 -= offset; c.range.1 -= offset; @@ -346,14 +334,18 @@ impl Editor { offset += len; } + self.cursors.make_consistent(); + // Adjust view self.move_view_to_cursor(); } pub fn remove_text_in_front_of_cursor(&mut self, grapheme_count: usize) { + self.cursors.make_consistent(); + let mut offset = 0; - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { // Update cursor with offset c.range.0 -= min(c.range.0, offset); c.range.1 -= min(c.range.1, offset); @@ -377,14 +369,18 @@ impl Editor { offset += len; } + self.cursors.make_consistent(); + // Adjust view self.move_view_to_cursor(); } pub fn remove_text_inside_cursor(&mut self) { + self.cursors.make_consistent(); + let mut offset = 0; - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { // Update cursor with offset c.range.0 -= min(c.range.0, offset); c.range.1 -= min(c.range.1, offset); @@ -406,12 +402,14 @@ impl Editor { c.update_vis_start(&(self.buffer)); } + self.cursors.make_consistent(); + // Adjust view self.move_view_to_cursor(); } pub fn cursor_to_beginning_of_buffer(&mut self) { - self.cursors = vec!(Cursor::new()); + self.cursors = CursorSet::new(); self.cursors[0].range = (0, 0); self.cursors[0].update_vis_start(&(self.buffer)); @@ -423,7 +421,7 @@ impl Editor { pub fn cursor_to_end_of_buffer(&mut self) { let end = self.buffer.grapheme_count(); - self.cursors = vec!(Cursor::new()); + self.cursors = CursorSet::new(); self.cursors[0].range = (end, end); self.cursors[0].update_vis_start(&(self.buffer)); @@ -432,7 +430,7 @@ impl Editor { } pub fn cursor_left(&mut self, n: usize) { - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { if c.range.0 >= n { c.range.0 -= n; } @@ -449,7 +447,7 @@ impl Editor { } pub fn cursor_right(&mut self, n: usize) { - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { c.range.1 += n; if c.range.1 > self.buffer.grapheme_count() { @@ -465,7 +463,7 @@ impl Editor { } pub fn cursor_up(&mut self, n: usize) { - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { let (v, _) = self.buffer.index_to_v2d(c.range.0); if v >= n { @@ -483,7 +481,7 @@ impl Editor { } pub fn cursor_down(&mut self, n: usize) { - for c in self.cursors.as_mut_slice().iter_mut() { + for c in self.cursors.iter_mut() { let (v, _) = self.buffer.index_to_v2d(c.range.0); if v < (self.buffer.line_count() - n) { diff --git a/src/term_ui.rs b/src/term_ui.rs index 5443aa0..9a8835d 100644 --- a/src/term_ui.rs +++ b/src/term_ui.rs @@ -364,7 +364,7 @@ impl TermUI { // Check if the character is within a cursor let mut at_cursor = false; - for c in editor.cursors.as_slice().iter() { + for c in editor.cursors.iter() { if grapheme_index >= c.range.0 && grapheme_index <= c.range.1 { at_cursor = true; } @@ -418,7 +418,7 @@ impl TermUI { // Print cursor(s) if it's at the end of the text, and thus wasn't printed // already. - for c in editor.cursors.as_slice().iter() { + for c in editor.cursors.iter() { if c.range.0 >= editor.buffer.grapheme_count() { let vis_cursor_pos = editor.buffer.index_to_v2d(c.range.0); if (vis_cursor_pos.0 >= editor.view_pos.0) && (vis_cursor_pos.1 >= editor.view_pos.1) {