From 896887e9abb3dd6c704ad5aab99db93f05e1492e Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Fri, 29 Mar 2013 01:21:45 -0500 Subject: use the trie to implement reading with escape codes --- src/term.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/rl.rs | 32 ++++++++++++++------- 2 files changed, 117 insertions(+), 11 deletions(-) diff --git a/src/term.rs b/src/term.rs index 27fc8c9..ae21cc0 100644 --- a/src/term.rs +++ b/src/term.rs @@ -3,9 +3,12 @@ #[crate_type = "lib"]; use core::libc::c_int; +use core::option::{Option,Some,None}; +use core::io::ReaderUtil; pub use ios::{cooked,cbreak,raw,echo,size}; use info::{init,escape,escape2}; +use util::Trie; struct Writer { priv cleanup: bool, @@ -59,6 +62,99 @@ impl Drop for Writer { } } +enum Keypress { + KeyCharacter(char), + KeyBackspace, + KeyReturn, + KeyTab, + KeyCtrl(char), + KeyF(int), + KeyUp, + KeyDown, + KeyLeft, + KeyRight, + KeyHome, + KeyEnd, + KeyInsert, + KeyDelete, + KeyEscape, +} + +struct Reader { + priv escapes: ~Trie, +} + +pub fn Reader () -> Reader { + Reader { escapes: build_escapes_trie() } +} + +impl Reader { + pub fn read (&self) -> Option { + let mut buf = ~""; + loop { + let c = io::stdin().read_char(); + if c as int == -1 { + return None; + } + + str::push_char(&mut buf, c); + io::print(str::escape_default(buf)); + + if !self.escapes.has_prefix(buf) { + match self.escapes.find(buf) { + &Some(k) => { return Some(k) } + &None => { + if str::len(buf) == 1 { + return Some(KeyCharacter(str::char_at(buf, 0))); + } + else { + // XXX this is... suboptimal + // really, we need to do an ungetc sort of thing + // with the characters in buf, and then just + // return the first character as a KeyCharacter + fail!("unknown escape"); + } + } + } + } + } + } +} + +// XXX this whole thing needs to be able to deal with caps that don't exist +fn build_escapes_trie () -> ~Trie { + let mut trie = ~Trie(); + + trie.insert(escape("kbs"), KeyBackspace); + trie.insert(escape("cr"), KeyReturn); + trie.insert(escape("ht"), KeyTab); + + trie.insert(escape("kcuu1"), KeyUp); + trie.insert(escape("kcud1"), KeyDown); + trie.insert(escape("kcub1"), KeyLeft); + trie.insert(escape("kcuf1"), KeyRight); + + trie.insert(escape("khome"), KeyHome); + trie.insert(escape("kend"), KeyEnd); + trie.insert(escape("kich1"), KeyInsert); + trie.insert(escape("kdch1"), KeyDelete); + + for uint::range(1, 12) |i| { + trie.insert(escape(fmt!("kf%d", i as int)), KeyF(i as int)); + } + + for uint::range(1, 26) |i| { + let s = str::from_char(i as char); + if (trie.find(s).is_none()) { + trie.insert(s, KeyCtrl(i as char)); + } + } + + trie.insert(str::from_char(27 as char), KeyEscape); + + trie +} + pub fn isatty() -> bool { unsafe { c_isatty(0) as bool } } diff --git a/test/rl.rs b/test/rl.rs index 9b096b4..fe1103b 100644 --- a/test/rl.rs +++ b/test/rl.rs @@ -1,11 +1,12 @@ extern mod term; -use core::io::ReaderUtil; +use term::{KeyCharacter,KeyEscape,KeyUp,KeyDown,KeyLeft,KeyRight}; -fn term_app (body: &fn (w: &term::Writer)) { +fn term_app (body: &fn (r: &term::Reader, w: &term::Writer)) { let writer = term::Writer(true); + let reader = term::Reader(); do term::ios::preserve { writer.alternate_screen(true); - body(&writer); + body(&reader, &writer); } } @@ -30,7 +31,7 @@ fn draw_ground (w: &term::Writer, x: uint, y: uint) { fn main () { let (cols, rows) = term::size(); - do term_app |w| { + do term_app |r, w| { term::cbreak(); term::echo(false); w.clear(); @@ -41,13 +42,22 @@ fn main () { let mut cursor = true; loop { draw_character(w, x, y); - match io::stdin().read_char() { - 'q' => { break } - 'h' if x > 0 => { draw_ground(w, x, y); x -= 1 } - 'j' if y < rows - 1 => { draw_ground(w, x, y); y += 1 } - 'k' if y > 0 => { draw_ground(w, x, y); y -= 1 } - 'l' if x < cols - 1 => { draw_ground(w, x, y); x += 1 } - ' ' => { w.cursor(cursor); cursor = !cursor } + let k = match r.read() { + Some(key) => key, + None => break, + }; + draw_ground(w, x, y); + + match k { + KeyCharacter('q') | KeyEscape => { break } + + KeyCharacter('h') | KeyLeft if x > 0 => { x -= 1 } + KeyCharacter('j') | KeyDown if y < rows - 1 => { y += 1 } + KeyCharacter('k') | KeyUp if y > 0 => { y -= 1 } + KeyCharacter('l') | KeyRight if x < cols - 1 => { x += 1 } + + KeyCharacter(' ') => { w.cursor(cursor); cursor = !cursor } + _ => { } } } -- cgit v1.2.3-54-g00ecf