From 16945f4183d895163d04601a3be9e75b543a8ad9 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Fri, 26 Dec 2014 17:15:20 -0800 Subject: [PATCH] Updated code to work with new versions of libraries. Also added backspace support. --- src/editor.rs | 26 +++++- src/main.rs | 121 +++------------------------ src/term_ui.rs | 216 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 211 insertions(+), 152 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 5dbd383..4c82a00 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -33,7 +33,7 @@ impl Editor { } pub fn new_from_file(path: &Path) -> Editor { - let mut buf = load_file_to_buffer(path).unwrap(); + let buf = load_file_to_buffer(path).unwrap(); Editor { buffer: buf, @@ -63,6 +63,7 @@ impl Editor { self.dirty = true; + // TODO: handle multi-character strings properly if text == "\n" { self.cursor.0 += 1; self.cursor.1 = 0; @@ -72,6 +73,29 @@ impl Editor { } } + pub fn insert_text_at_char(&mut self, text: &str, pos: uint) { + self.dirty = true; + let buf_len = self.buffer.len(); + self.buffer.insert_text(text, if pos < buf_len {pos} else {buf_len}); + } + + pub fn remove_text_behind_cursor(&mut self, char_count: uint) { + 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}; + + self.buffer.remove_text(pos_a, pos_b); + + 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) { if self.cursor.1 > 0 { self.cursor.1 -= 1; diff --git a/src/main.rs b/src/main.rs index 236fb52..86ccdaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,11 @@ extern crate rustbox; extern crate docopt; -extern crate serialize; +extern crate "rustc-serialize" as rustc_serialize; -use std::char; use std::path::Path; use docopt::Docopt; use editor::Editor; -use term_ui::draw_editor; +use term_ui::TermUI; mod buffer; mod files; @@ -14,6 +13,8 @@ mod editor; mod term_ui; + + // Usage documentation string static USAGE: &'static str = " Usage: led [] @@ -25,134 +26,30 @@ Options: // Struct for storing command-line arguments -#[deriving(Decodable, Show)] +#[deriving(RustcDecodable, Show)] struct Args { arg_file: Option, flag_help: bool, } -// Key codes -const K_ENTER: u16 = 13; -const K_TAB: u16 = 9; -const K_SPACE: u16 = 32; -//const K_BACKSPACE: u16 = 127; -const K_DOWN: u16 = 65516; -const K_LEFT: u16 = 65515; -const K_RIGHT: u16 = 65514; -const K_UP: u16 = 65517; -const K_ESC: u16 = 27; -const K_CTRL_Q: u16 = 17; -const K_CTRL_S: u16 = 19; - fn main() { // Get command-line arguments let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); - // Quitting flag - let mut quit = false; - - let mut width = rustbox::width(); - let mut height = rustbox::height(); - // Load file, if specified - let mut editor = if let Option::Some(s) = args.arg_file { + let editor = if let Option::Some(s) = args.arg_file { Editor::new_from_file(&Path::new(s.as_slice())) } else { Editor::new() }; - rustbox::init(); - - loop { - // Draw the editor to screen - rustbox::clear(); - draw_editor(&editor, (0, 0), (height-1, width-1)); - rustbox::present(); - - - // Handle events. We block on the first event, so that the - // program doesn't loop like crazy, but then continue pulling - // events in a non-blocking way until we run out of events - // to handle. - let mut e = rustbox::poll_event(); // Block until we get an event - loop { - match e { - rustbox::Event::KeyEvent(modifier, key, character) => { - //println!(" {} {} {}", modifier, key, character); - match key { - K_CTRL_Q | K_ESC => { - quit = true; - break; - }, - - K_CTRL_S => { - editor.save_if_dirty(); - }, - - K_UP => { - editor.cursor_up(); - }, - - K_DOWN => { - editor.cursor_down(); - }, - - K_LEFT => { - editor.cursor_left(); - }, - - K_RIGHT => { - editor.cursor_right(); - }, - - K_ENTER => { - editor.insert_text_at_cursor("\n"); - }, - - K_SPACE => { - editor.insert_text_at_cursor(" "); - }, - - K_TAB => { - editor.insert_text_at_cursor("\t"); - }, - - // Character - 0 => { - if let Option::Some(c) = char::from_u32(character) { - editor.insert_text_at_cursor(c.to_string().as_slice()); - } - }, - - _ => {} - } - }, - - rustbox::Event::ResizeEvent(w, h) => { - width = w as uint; - height = h as uint; - } - - _ => { - break; - } - } - - e = rustbox::peek_event(0); // Get next event (if any) - } - - - // Quit if quit flag is set - if quit { - break; - } - } - - rustbox::shutdown(); + // Initialize and start UI + let mut ui = TermUI::new_from_editor(editor); + ui.ui_loop(); //println!("{}", editor.buffer.root.tree_height); } diff --git a/src/term_ui.rs b/src/term_ui.rs index f66062c..c47fb11 100644 --- a/src/term_ui.rs +++ b/src/term_ui.rs @@ -1,53 +1,191 @@ -use rustbox; -use rustbox::{Style,Color}; -use editor::Editor; +#![allow(dead_code)] -pub fn draw_editor(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 height = c2.0 - c1.0; - let width = c2.1 - c1.1; +use rustbox; +use rustbox::Color; +use editor::Editor; +use std::char; +use std::time::duration::Duration; + + +// Key codes +const K_ENTER: u16 = 13; +const K_TAB: u16 = 9; +const K_SPACE: u16 = 32; +const K_BACKSPACE: u16 = 127; +const K_DOWN: u16 = 65516; +const K_LEFT: u16 = 65515; +const K_RIGHT: u16 = 65514; +const K_UP: u16 = 65517; +const K_ESC: u16 = 27; +const K_CTRL_Q: u16 = 17; +const K_CTRL_S: u16 = 19; + + +pub struct TermUI { + rb: rustbox::RustBox, + editor: Editor, +} + + +impl TermUI { + pub fn new() -> TermUI { + TermUI { + rb: rustbox::RustBox::init(&[None]).unwrap(), + editor: Editor::new(), + } + } - loop { - if let Option::Some(c) = tb_iter.next() { - if c == '\n' { - if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { - rustbox::print(editor.cursor.1, line, Style::Normal, Color::Black, Color::White, " ".to_string()); + pub fn new_from_editor(editor: Editor) -> TermUI { + TermUI { + rb: rustbox::RustBox::init(&[None]).unwrap(), + editor: editor, + } + } + + pub fn ui_loop(&mut self) { + // Quitting flag + let mut quit = false; + + let mut width = self.rb.width(); + let mut height = self.rb.height(); + + loop { + // Draw the editor to screen + self.rb.clear(); + self.draw_editor(&self.editor, (0, 0), (height-1, width-1)); + self.rb.present(); + + + // Handle events. We block on the first event, so that the + // program doesn't loop like crazy, but then continue pulling + // events in a non-blocking way until we run out of events + // to handle. + let mut e = self.rb.poll_event(); // Block until we get an event + loop { + match e { + Ok(rustbox::Event::KeyEvent(_, key, character)) => { + //println!(" {} {} {}", modifier, key, character); + match key { + K_CTRL_Q | K_ESC => { + quit = true; + break; + }, + + K_CTRL_S => { + self.editor.save_if_dirty(); + }, + + K_UP => { + self.editor.cursor_up(); + }, + + K_DOWN => { + self.editor.cursor_down(); + }, + + K_LEFT => { + self.editor.cursor_left(); + }, + + K_RIGHT => { + self.editor.cursor_right(); + }, + + K_ENTER => { + self.editor.insert_text_at_cursor("\n"); + }, + + K_SPACE => { + self.editor.insert_text_at_cursor(" "); + }, + + K_TAB => { + self.editor.insert_text_at_cursor("\t"); + }, + + K_BACKSPACE => { + self.editor.remove_text_behind_cursor(1); + }, + + // Character + 0 => { + if let Option::Some(c) = char::from_u32(character) { + self.editor.insert_text_at_cursor(c.to_string().as_slice()); + } + }, + + _ => {} + } + }, + + Ok(rustbox::Event::ResizeEvent(w, h)) => { + width = w as uint; + height = h as uint; + } + + _ => { + break; + } } - - line += 1; - column = 0; - continue; + + e = self.rb.peek_event(Duration::milliseconds(0)); // Get next event (if any) } - if editor.cursor.0 == line && editor.cursor.1 == column { - rustbox::print(column, line, Style::Normal, Color::Black, Color::White, c.to_string()); + + // Quit if quit flag is set + if quit { + break; + } + } + } + + 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 height = c2.0 - c1.0; + let width = c2.1 - c1.1; + + loop { + if let Option::Some(c) = tb_iter.next() { + if c == '\n' { + if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { + self.rb.print(editor.cursor.1, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice()); + } + + line += 1; + column = 0; + continue; + } + + if editor.cursor.0 == line && editor.cursor.1 == column { + self.rb.print(column, line, 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()); + } + column += 1; } else { - rustbox::print(column, line, Style::Normal, Color::White, Color::Black, c.to_string()); + break; + } + + if line > height { + break; + } + + if column > width { + tb_iter.next_line(); + line += 1; + column = 0; } - column += 1; - } - else { - break; } - if line > height { - break; + if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { + self.rb.print(editor.cursor.1, line, rustbox::RB_NORMAL, Color::Black, Color::White, " "); } - - if column > width { - tb_iter.next_line(); - line += 1; - column = 0; + else if editor.cursor.0 > line && editor.cursor.0 <= height && editor.cursor.1 <= width { + self.rb.print(editor.cursor.1, editor.cursor.0, rustbox::RB_NORMAL, Color::Black, Color::White, " "); } } - - if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { - rustbox::print(editor.cursor.1, line, Style::Normal, Color::Black, Color::White, " ".to_string()); - } - else if editor.cursor.0 > line && editor.cursor.0 <= height && editor.cursor.1 <= width { - rustbox::print(editor.cursor.1, editor.cursor.0, Style::Normal, Color::Black, Color::White, " ".to_string()); - } } \ No newline at end of file