diff --git a/Cargo.lock b/Cargo.lock index c8886bc..705b95c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,11 +15,6 @@ name = "cfg-if" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "lazy_static" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "libc" version = "0.2.62" @@ -42,15 +37,6 @@ name = "rs-kilo" version = "0.1.0" dependencies = [ "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term-parser 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "term-parser" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -62,8 +48,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "b548a4ee81fccb95919d4e22cfea83c7693ebfd78f0495493178db20b3139da7" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -"checksum term-parser 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c483a684059c71636d0e4ff927bd5feb5f2a533e7a8f16a0008dd33c8752cbc" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" diff --git a/Cargo.toml b/Cargo.toml index ca28f08..e1e20f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,5 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +# Rust wrappers for C/POSIX headers nix = "0.15.0" -term-parser = "0.2.0" \ No newline at end of file diff --git a/src/editor.rs b/src/editor.rs index a69a7c1..e8aa139 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -9,18 +9,15 @@ use std::io::prelude::*; use std::io::{BufReader, Error, Stdin}; use std::str::Chars; -use term_parser::{Action, ActionIter}; - /// Main structure for the editor -pub struct Editor { - -} +/// +/// impl blocks are split similarly to the original C implementation +pub struct Editor {} // init impl Editor { pub fn new() -> Self { - Editor { - } + Editor {} } } @@ -28,8 +25,9 @@ impl Editor { impl Editor { fn read_key(&mut self) -> Option> { let stdin = io::stdin(); + let stdin = stdin.lock(); let mut in_str = String::new(); - let mut input = BufReader::new(stdin.take(1)); + let mut input = BufReader::with_capacity(3, stdin); input.read_to_string(&mut in_str).unwrap(); let mut output: Vec = vec![]; @@ -39,68 +37,44 @@ impl Editor { } if output.len() == 0 { - return None + return None; } - return Some(output) - } - - fn read_term_code(&mut self) -> ActionIter> { - let stdin = io::stdin(); - let buffer = BufReader::new(stdin); - let term_code_iter = ActionIter::new(buffer); - - /* - for term_code in self.read_term_code() { - let term_code = match term_code { - Ok(code) => code, - Err(e) => panic!("{:?}\r\n", e), - }; - print!("{:?}\r\n", term_code); - } - */ - - term_code_iter + return Some(output); } } // Input impl Editor { pub fn process_keypress(&mut self) -> Option<()> { - let chars= self.read_key(); + match self.read_key() { + // Just continue the input loop on an "empty" keypress + None => Some(()), + Some(chars) => { + let first = chars[0]; - // No input, just continue - if chars.is_none() { - return Some(()) - } - let chars = chars.unwrap(); + if first == ctrl_key('q') { + // Break out of the input loop + return None; + } - if chars.len() == 1 { - let char = chars[0]; + print!("{:?}\r\n", chars); - if char == CTRL_KEY('q') { - // Break out of the input loop - return None; + // Continue the main input loop + Some(()) } - - // Echo characters - if char.is_ascii_control() { - print!("{}\r\n", char as u8); - } else { - print!("{} ('{}')\r\n", char as u8, char); - } - - } else { - print!("{:?}\r\n", chars); - // Handle escape sequences } - - Some(()) } } -const fn CTRL_KEY(c: char) -> char { - let key = c as u8; +// Output +impl Editor { + pub fn refresh_screen() { + let stdout = io::stdout(); + let mut handle = stdout.lock(); - (key & 0x1f) as char + let mut buffer = String::from("\x1b[2J").into_bytes(); + + handle.write_all(&mut buffer).unwrap(); + } } diff --git a/src/helpers.rs b/src/helpers.rs index 4df82ec..13efbd3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -11,21 +11,40 @@ use std::os::unix::io::RawFd; use std::process::exit; // Redefine the posix constants for rust land + +/// The value of the raw file descriptor for STDIN pub const STDIN_FILENO: i32 = 0; // pub const STDOUT_FILENO: i32 = 1; // pub const STDERR_FILENO: i32 = 2; +/// Convert Ctrl+letter chords to their +/// ASCII table equivalents +#[inline] +pub fn ctrl_key(c: char) -> char { + let key = c as u8; + + if !c.is_ascii() { + panic!("CTRL_KEY only accepts ASCII characters"); + } + + // Intentionally "overflow" u8 to wrap around to the + // beginning of the ASCII table. Ctrl+a is 1, Ctrl+b is 2, etc. + (key & 0x1f) as char +} + pub fn die(code: &Errno, msg: &str) -> ! { eprintln!("{:?} ({})", code, msg); exit(1) } +/// Get a `Termios` struct, for getting/setting terminal flags pub fn get_termios(fd: RawFd) -> Termios { termios::tcgetattr(fd).unwrap() } -pub fn enable_raw_mode() -> Result<(), Error> { +/// Put terminal into raw mode so there is full control of terminal output +pub fn enable_raw_mode() { let mut raw = get_termios(STDIN_FILENO); raw.input_flags.remove( @@ -59,21 +78,11 @@ pub fn enable_raw_mode() -> Result<(), Error> { raw.control_chars[SpecialCharacterIndices::VMIN as usize] = 0; raw.control_chars[SpecialCharacterIndices::VTIME as usize] = 1; - match termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, &raw) { - Ok(()) => Ok(()), - Err(e) => match e.as_errno() { - Some(errno) => die(&errno, "tcsetattr"), - None => panic!("Failed to enable raw mode"), - }, - } + // Raw mode or bust! + termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, &raw).unwrap(); } -pub fn disable_raw_mode(original: &Termios) -> Result<(), Error> { - match termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, original) { - Ok(()) => Ok(()), - Err(E) => match E.as_errno() { - Some(errno) => die(&errno, "tcsetattr"), - None => panic!("Failed to disable raw mode"), - }, - } +/// Restore terminal to "cooked"/canonical mode +pub fn disable_raw_mode(original: &Termios) { + termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSAFLUSH, original).unwrap(); } diff --git a/src/main.rs b/src/main.rs index d48a562..4025c8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,15 +5,22 @@ use crate::editor::Editor; use crate::helpers::*; fn main() { + // Save original terminal flags let original_termios = get_termios(STDIN_FILENO); - enable_raw_mode().unwrap(); + // Disable canonical/"cooked" terminal mode + enable_raw_mode(); + // Initialize the editor let mut editor = Editor::new(); + // Main input loop. Editor::process_keypress uses an Option Enum as a sentinel. + // `None` is returned on a quit action, in other cases, `Some(())` is returned, + // continuing the loop while editor.process_keypress().is_some() { // loop } - disable_raw_mode(&original_termios).unwrap(); + // Restore previous terminal flags before exit + disable_raw_mode(&original_termios); }