Updated code to work with new versions of libraries.

Also added backspace support.
This commit is contained in:
Nathan Vegdahl 2014-12-26 17:15:20 -08:00
parent f469240860
commit 16945f4183
3 changed files with 211 additions and 152 deletions

View File

@ -33,7 +33,7 @@ impl Editor {
} }
pub fn new_from_file(path: &Path) -> 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 { Editor {
buffer: buf, buffer: buf,
@ -63,6 +63,7 @@ impl Editor {
self.dirty = true; self.dirty = true;
// TODO: handle multi-character strings properly
if text == "\n" { if text == "\n" {
self.cursor.0 += 1; self.cursor.0 += 1;
self.cursor.1 = 0; 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) { pub fn cursor_left(&mut self) {
if self.cursor.1 > 0 { if self.cursor.1 > 0 {
self.cursor.1 -= 1; self.cursor.1 -= 1;

View File

@ -1,12 +1,11 @@
extern crate rustbox; extern crate rustbox;
extern crate docopt; extern crate docopt;
extern crate serialize; extern crate "rustc-serialize" as rustc_serialize;
use std::char;
use std::path::Path; use std::path::Path;
use docopt::Docopt; use docopt::Docopt;
use editor::Editor; use editor::Editor;
use term_ui::draw_editor; use term_ui::TermUI;
mod buffer; mod buffer;
mod files; mod files;
@ -14,6 +13,8 @@ mod editor;
mod term_ui; mod term_ui;
// Usage documentation string // Usage documentation string
static USAGE: &'static str = " static USAGE: &'static str = "
Usage: led [<file>] Usage: led [<file>]
@ -25,134 +26,30 @@ Options:
// Struct for storing command-line arguments // Struct for storing command-line arguments
#[deriving(Decodable, Show)] #[deriving(RustcDecodable, Show)]
struct Args { struct Args {
arg_file: Option<String>, arg_file: Option<String>,
flag_help: bool, 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() { fn main() {
// Get command-line arguments // Get command-line arguments
let args: Args = Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()); 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 // 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())) Editor::new_from_file(&Path::new(s.as_slice()))
} }
else { else {
Editor::new() Editor::new()
}; };
rustbox::init(); // Initialize and start UI
let mut ui = TermUI::new_from_editor(editor);
loop { ui.ui_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();
//println!("{}", editor.buffer.root.tree_height); //println!("{}", editor.buffer.root.tree_height);
} }

View File

@ -1,8 +1,145 @@
use rustbox; #![allow(dead_code)]
use rustbox::{Style,Color};
use editor::Editor;
pub fn draw_editor(editor: &Editor, c1: (uint, uint), c2: (uint, uint)) { 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(),
}
}
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;
}
}
e = self.rb.peek_event(Duration::milliseconds(0)); // Get next event (if any)
}
// 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 tb_iter = editor.buffer.root_iter();
let mut line: uint = 0; let mut line: uint = 0;
let mut column: uint = 0; let mut column: uint = 0;
@ -13,7 +150,7 @@ pub fn draw_editor(editor: &Editor, c1: (uint, uint), c2: (uint, uint)) {
if let Option::Some(c) = tb_iter.next() { if let Option::Some(c) = tb_iter.next() {
if c == '\n' { if c == '\n' {
if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { 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()); self.rb.print(editor.cursor.1, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice());
} }
line += 1; line += 1;
@ -22,10 +159,10 @@ pub fn draw_editor(editor: &Editor, c1: (uint, uint), c2: (uint, uint)) {
} }
if editor.cursor.0 == line && editor.cursor.1 == column { if editor.cursor.0 == line && editor.cursor.1 == column {
rustbox::print(column, line, Style::Normal, Color::Black, Color::White, c.to_string()); self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, c.to_string().as_slice());
} }
else { else {
rustbox::print(column, line, Style::Normal, Color::White, Color::Black, c.to_string()); self.rb.print(column, line, rustbox::RB_NORMAL, Color::White, Color::Black, c.to_string().as_slice());
} }
column += 1; column += 1;
} }
@ -45,9 +182,10 @@ pub fn draw_editor(editor: &Editor, c1: (uint, uint), c2: (uint, uint)) {
} }
if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width { 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()); self.rb.print(editor.cursor.1, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
} }
else if editor.cursor.0 > line && editor.cursor.0 <= height && editor.cursor.1 <= width { 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()); self.rb.print(editor.cursor.1, editor.cursor.0, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
}
} }
} }