Moved tab_width to Buffer, and misc cleanup related to that.

This commit is contained in:
Nathan Vegdahl 2015-01-10 23:42:59 -08:00
parent 8b1f1b1e96
commit f8cf8e620b
4 changed files with 53 additions and 49 deletions

View File

@ -21,8 +21,9 @@ mod undo_stack;
/// A text buffer
pub struct Buffer {
text: BufferNode,
pub line_ending_type: LineEnding,
undo_stack: UndoStack,
pub line_ending_type: LineEnding,
pub tab_width: usize,
}
@ -30,8 +31,9 @@ impl Buffer {
pub fn new() -> Buffer {
Buffer {
text: BufferNode::new(),
line_ending_type: LineEnding::LF,
undo_stack: UndoStack::new(),
line_ending_type: LineEnding::LF,
tab_width: 4,
}
}
@ -231,9 +233,9 @@ impl Buffer {
///
/// If the index is off the end of the text, returns the visual line and
/// column number of the last valid text position.
pub fn index_to_v2d(&self, pos: usize, tab_width: usize) -> (usize, usize) {
pub fn index_to_v2d(&self, pos: usize) -> (usize, usize) {
let (v, h) = self.text.pos_1d_to_closest_2d_recursive(pos);
let vis_h = self.get_line(v).grapheme_index_to_closest_vis_pos(h, tab_width);
let vis_h = self.get_line(v).grapheme_index_to_closest_vis_pos(h, self.tab_width);
return (v, vis_h);
}
@ -244,13 +246,13 @@ impl Buffer {
/// index of the horizontally-closest valid position. If the visual line
/// number given is beyond the end of the buffer, returns the index of
/// the buffer's last valid position.
pub fn v2d_to_index(&self, pos: (usize, usize), tab_width: usize) -> usize {
pub fn v2d_to_index(&self, pos: (usize, usize)) -> usize {
if pos.0 >= self.line_count() {
return self.grapheme_count();
}
else {
let gs = self.line_col_to_index((pos.0, 0));
let h = self.get_line(pos.0).vis_pos_to_closest_grapheme_index(pos.1, tab_width);
let h = self.get_line(pos.0).vis_pos_to_closest_grapheme_index(pos.1, self.tab_width);
return gs + h;
}
}
@ -271,17 +273,17 @@ impl Buffer {
}
pub fn get_grapheme_width(&self, index: usize, tab_width: usize) -> usize {
pub fn get_grapheme_width(&self, index: usize) -> usize {
if index >= self.grapheme_count() {
panic!("Buffer::get_grapheme_width(): index past last grapheme.");
}
else {
return self.text.get_grapheme_width_recursive(index, tab_width);
return self.text.get_grapheme_width_recursive(index, self.tab_width);
}
}
pub fn get_line<'a>(&'a self, index: usize) -> &'a Line {
fn get_line<'a>(&'a self, index: usize) -> &'a Line {
if index >= self.line_count() {
panic!("get_line(): index out of bounds.");
}

View File

@ -26,9 +26,9 @@ impl Cursor {
}
}
pub fn update_vis_start(&mut self, buf: &Buffer, tab_width: usize) {
let (v, h) = buf.index_to_line_col(self.range.0);
self.vis_start = buf.get_line(v).grapheme_index_to_closest_vis_pos(h, tab_width);
pub fn update_vis_start(&mut self, buf: &Buffer) {
let (v, h) = buf.index_to_v2d(self.range.0);
self.vis_start = h;
}
}
@ -37,7 +37,6 @@ pub struct Editor {
pub buffer: Buffer,
pub file_path: Path,
pub soft_tabs: bool,
pub tab_width: usize,
pub dirty: bool,
// The dimensions and position of the editor's view within the buffer
@ -56,7 +55,6 @@ impl Editor {
buffer: Buffer::new(),
file_path: Path::new(""),
soft_tabs: false,
tab_width: 4,
dirty: false,
view_dim: (0, 0),
view_pos: (0, 0),
@ -71,7 +69,6 @@ impl Editor {
buffer: buf,
file_path: path.clone(),
soft_tabs: false,
tab_width: 4,
dirty: false,
view_dim: (0, 0),
view_pos: (0, 0),
@ -177,7 +174,7 @@ impl Editor {
}
self.soft_tabs = true;
self.tab_width = width;
self.buffer.tab_width = width;
}
else {
self.soft_tabs = false;
@ -193,7 +190,7 @@ impl Editor {
if let Some(pos) = self.buffer.undo() {
self.cursor.range.0 = pos;
self.cursor.range.1 = pos;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
self.move_view_to_cursor();
}
@ -204,7 +201,7 @@ impl Editor {
if let Some(pos) = self.buffer.redo() {
self.cursor.range.0 = pos;
self.cursor.range.1 = pos;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
self.move_view_to_cursor();
}
@ -213,7 +210,7 @@ impl Editor {
/// Moves the editor's view the minimum amount to show the cursor
pub fn move_view_to_cursor(&mut self) {
let (v, h) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width);
let (v, h) = self.buffer.index_to_v2d(self.cursor.range.0);
// Horizontal
if h < self.view_pos.1 {
@ -242,7 +239,7 @@ impl Editor {
// Move cursor
self.cursor.range.0 += str_len;
self.cursor.range.1 += str_len;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -252,8 +249,8 @@ impl Editor {
if self.soft_tabs {
// Figure out how many spaces to insert
let (v, h) = self.buffer.index_to_line_col(self.cursor.range.0);
let vis_pos = self.buffer.get_line(v).grapheme_index_to_closest_vis_pos(h, self.tab_width);
let next_tab_stop = ((vis_pos / self.tab_width) + 1) * self.tab_width;
let (_, vis_pos) = self.buffer.index_to_v2d(self.cursor.range.0);
let next_tab_stop = ((vis_pos / self.buffer.tab_width) + 1) * self.buffer.tab_width;
let space_count = min(next_tab_stop - vis_pos, 8);
@ -265,7 +262,7 @@ impl Editor {
// Move cursor
self.cursor.range.0 += space_count;
self.cursor.range.1 += space_count;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -286,6 +283,11 @@ impl Editor {
}
pub fn remove_text_behind_cursor(&mut self, grapheme_count: usize) {
// Do nothing if there's nothing to delete.
if self.cursor.range.0 == 0 {
return;
}
let pos_b = self.cursor.range.0;
let pos_a = if pos_b >= grapheme_count {pos_b - grapheme_count} else {0};
let tot_g = pos_b - pos_a;
@ -297,13 +299,18 @@ impl Editor {
// Move cursor
self.cursor.range.0 -= tot_g;
self.cursor.range.1 -= tot_g;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
}
pub fn remove_text_in_front_of_cursor(&mut self, grapheme_count: usize) {
// Do nothing if there's nothing to delete.
if self.cursor.range.0 == self.buffer.grapheme_count() {
return;
}
let pos_a = self.cursor.range.1;
let pos_b = if (pos_a + grapheme_count) <= self.buffer.grapheme_count() {pos_a + grapheme_count} else {self.buffer.grapheme_count()};
@ -312,7 +319,7 @@ impl Editor {
self.dirty = true;
// Move cursor
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -327,7 +334,7 @@ impl Editor {
// Move cursor
self.cursor.range.1 = self.cursor.range.0;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -335,7 +342,7 @@ impl Editor {
pub fn cursor_to_beginning_of_buffer(&mut self) {
self.cursor.range = (0, 0);
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -344,7 +351,7 @@ impl Editor {
pub fn cursor_to_end_of_buffer(&mut self) {
let end = self.buffer.grapheme_count();
self.cursor.range = (end, end);
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -359,7 +366,7 @@ impl Editor {
}
self.cursor.range.1 = self.cursor.range.0;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
@ -374,17 +381,17 @@ impl Editor {
}
self.cursor.range.0 = self.cursor.range.1;
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
self.cursor.update_vis_start(&(self.buffer));
// Adjust view
self.move_view_to_cursor();
}
pub fn cursor_up(&mut self, n: usize) {
let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width);
let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0);
if v >= n {
self.cursor.range.0 = self.buffer.v2d_to_index((v - n, self.cursor.vis_start), self.tab_width);
self.cursor.range.0 = self.buffer.v2d_to_index((v - n, self.cursor.vis_start));
self.cursor.range.1 = self.cursor.range.0;
}
else {
@ -396,10 +403,10 @@ impl Editor {
}
pub fn cursor_down(&mut self, n: usize) {
let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0, self.tab_width);
let (v, _) = self.buffer.index_to_v2d(self.cursor.range.0);
if v < (self.buffer.line_count() - n) {
self.cursor.range.0 = self.buffer.v2d_to_index((v + n, self.cursor.vis_start), self.tab_width);
self.cursor.range.0 = self.buffer.v2d_to_index((v + n, self.cursor.vis_start));
self.cursor.range.1 = self.cursor.range.0;
}
else {
@ -457,8 +464,8 @@ impl Editor {
pub fn jump_to_line(&mut self, n: usize) {
let pos = self.buffer.line_col_to_index((n, 0));
let (v, _) = self.buffer.index_to_v2d(pos, self.tab_width);
self.cursor.range.0 = self.buffer.v2d_to_index((v, self.cursor.vis_start), self.tab_width);
let (v, _) = self.buffer.index_to_v2d(pos);
self.cursor.range.0 = self.buffer.v2d_to_index((v, self.cursor.vis_start));
self.cursor.range.1 = self.cursor.range.0;
// Adjust view

View File

@ -323,7 +323,7 @@ impl TermUI {
LineEnding::PS => "PS",
};
let soft_tabs_str = if editor.soft_tabs {"spaces"} else {"tabs"};
let info_line = format!("UTF8:{} {}:{}", nl, soft_tabs_str, editor.tab_width);
let info_line = format!("UTF8:{} {}:{}", nl, soft_tabs_str, editor.buffer.tab_width);
self.rb.print(c2.1 - 30, c1.0, rustbox::RB_NORMAL, foreground, background, info_line.as_slice());
// Draw main text editing area
@ -347,13 +347,13 @@ impl TermUI {
loop {
if let Some(line) = line_iter.next() {
let mut g_iter = line.grapheme_vis_iter(editor.tab_width);
let mut g_iter = line.grapheme_vis_iter(editor.buffer.tab_width);
let excess = g_iter.skip_vis_positions(editor.view_pos.1);
vis_col_num += excess;
print_col_num += excess;
grapheme_index = editor.buffer.v2d_to_index((vis_line_num, vis_col_num), editor.tab_width);
grapheme_index = editor.buffer.v2d_to_index((vis_line_num, vis_col_num));
for (g, pos, width) in g_iter {
print_col_num = pos - editor.view_pos.1;
@ -406,7 +406,7 @@ impl TermUI {
// Print cursor if it's at the end of the text, and thus wasn't printed
// already.
if editor.cursor.range.0 >= editor.buffer.grapheme_count() {
let vis_cursor_pos = editor.buffer.index_to_v2d(editor.cursor.range.0, editor.tab_width);
let vis_cursor_pos = editor.buffer.index_to_v2d(editor.cursor.range.0);
if (vis_cursor_pos.0 >= editor.view_pos.0) && (vis_cursor_pos.1 >= editor.view_pos.1) {
let print_cursor_pos = (vis_cursor_pos.0 - editor.view_pos.0 + c1.0, vis_cursor_pos.1 - editor.view_pos.1 + c1.1);

13
todo.md
View File

@ -7,11 +7,6 @@
- Clean up text buffer interface:
- Buffer should know its own tab size, font, etc. Led will NOT support
multiple views into the same buffer with different fonts, tab sizes, etc.
This may seem an odd choice, but it helps to think of the text buffer as
a 2d representation of the text, for which it needs that information to
know the relative positions of things.
- Editing (these are undoable):
//- insert_text
//- remove_text
@ -26,10 +21,10 @@
//- grapheme_count
//- line_count
- Position conversions:
- index -> line_col
- line_col -> index
- index -> vis_2d
- vis_2d -> index
//- index -> line_col
//- line_col -> index
//- index -> vis_2d
//- vis_2d -> index
- Reading text:
- grapheme at index (includes visual width in return)
- Bidirectional grapheme iterator (useful for search code, etc.)