Updated code to work with new versions of libraries.
Also added backspace support.
This commit is contained in:
parent
f469240860
commit
16945f4183
|
@ -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;
|
||||||
|
|
121
src/main.rs
121
src/main.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
216
src/term_ui.rs
216
src/term_ui.rs
|
@ -1,53 +1,191 @@
|
||||||
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;
|
||||||
let mut tb_iter = editor.buffer.root_iter();
|
use rustbox::Color;
|
||||||
let mut line: uint = 0;
|
use editor::Editor;
|
||||||
let mut column: uint = 0;
|
use std::char;
|
||||||
let height = c2.0 - c1.0;
|
use std::time::duration::Duration;
|
||||||
let width = c2.1 - c1.1;
|
|
||||||
|
|
||||||
|
// 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 {
|
pub fn new_from_editor(editor: Editor) -> TermUI {
|
||||||
if let Option::Some(c) = tb_iter.next() {
|
TermUI {
|
||||||
if c == '\n' {
|
rb: rustbox::RustBox::init(&[None]).unwrap(),
|
||||||
if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width {
|
editor: editor,
|
||||||
rustbox::print(editor.cursor.1, line, Style::Normal, Color::Black, Color::White, " ".to_string());
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
e = self.rb.peek_event(Duration::milliseconds(0)); // Get next event (if any)
|
||||||
column = 0;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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 {
|
if editor.cursor.0 == line && editor.cursor.1 >= column && editor.cursor.1 <= width {
|
||||||
break;
|
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 {
|
||||||
if column > width {
|
self.rb.print(editor.cursor.1, editor.cursor.0, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
|
||||||
tb_iter.next_line();
|
|
||||||
line += 1;
|
|
||||||
column = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user