diff --git a/src/buffer.rs b/src/buffer.rs index 34d5d71..e0cef85 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,12 +1,15 @@ +extern crate unicode_segmentation; + use std::io::Write; use termion::{clear, cursor, color}; use termion::raw::RawTerminal; use crate::layout::Layout; -use crate::utils; +use unicode_segmentation::UnicodeSegmentation; const PROMPT: &str = "Add alarm: "; +// Input buffer. pub struct Buffer { content: String, // Used for error messages. @@ -23,21 +26,46 @@ impl Buffer { } } + // Return reference to buffer content. pub fn read(&mut self) -> &String { self.altered = false; &self.content } + // Append char to buffer. pub fn push(&mut self, value: char) { self.altered = true; + // Reset error message. self.message = None; self.content.push(value); } - pub fn pop(&mut self) -> Option { - self.altered = true; + // Remove last char. Return true if a char was removed. + pub fn strip_char(&mut self) -> bool { + // Reset error message. self.message = None; - self.content.pop() + if self.content.pop().is_some() { + self.altered = true; + true + } else { + false + } + } + + // Remove last word. Return true if a word was removed. + pub fn strip_word(&mut self) -> bool { + // Reset error message. + self.message = None; + let iter = UnicodeSegmentation::split_word_bound_indices( + self.content.as_str().trim_end()); + + if let Some((index, _)) = iter.last() { + self.content.truncate(index); + self.altered = true; + true + } else { + false + } } pub fn is_empty(&self) -> bool { @@ -63,6 +91,7 @@ impl Buffer { layout: &mut Layout, ) -> Result<(), std::io::Error> { + // Write error message if present and return. if let Some(msg) = self.message { write!(stdout, "{}{}{}{}{}{}", @@ -85,13 +114,6 @@ impl Buffer { PROMPT, cursor::Show, &self.content)?; - layout.cursor.col = - layout.buffer.col - + 11 - + utils::unicode_length(&self.content); - // TODO: This would be a much better alternative, but panics because - // of interference with async_reader. - //layout.cursor.col = stdout.cursor_pos()?.0; } else { // Clear buffer display. write!(stdout, diff --git a/src/layout.rs b/src/layout.rs index b8d3ada..859ae32 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -29,7 +29,6 @@ pub struct Layout { pub roster_width: u16, pub roster_height: u16, pub buffer: Position, - pub cursor: Position, } impl Layout { @@ -51,7 +50,6 @@ impl Layout { roster_width: 0, roster_height: 0, buffer: Position {col: 0, line: 0}, - cursor: Position {col: 1, line: 1}, } } @@ -139,9 +137,6 @@ impl Layout { line: self.height, col: 1, }; - - // Cursor. Column will be set by main loop. - self.cursor.line = self.buffer.line; } pub fn set_roster_width(&mut self, width: u16) { diff --git a/src/lib.rs b/src/lib.rs index b2ff1cd..b92db3b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,6 @@ use termion::{clear, cursor, style}; use termion::raw::{IntoRawMode, RawTerminal}; use termion::event::Key; use termion::input::TermRead; -//use termion::cursor::DetectCursorPos; use buffer::Buffer; use clock::Clock; use layout::Layout; @@ -102,10 +101,12 @@ pub fn run( } // Update input buffer display, if requested. + /* if buffer.altered { buffer.draw(&mut stdout, &mut layout)?; stdout.flush()?; } + */ // Update elapsed time. let elapsed = if clock.paused { @@ -194,14 +195,6 @@ pub fn run( countdown.draw(&mut stdout); } - // Move cursor to buffer position. - if insert_mode { - write!( - stdout, - "{}", - cursor::Goto(layout.cursor.col, layout.cursor.line))?; - } - // Check any spawned child process. if let Some(ref mut child) = spawned { match child.try_wait() { @@ -228,6 +221,12 @@ pub fn run( stdout.flush()?; } + // Update buffer whenever the cursor is visible. + if insert_mode || buffer.altered { + buffer.draw(&mut stdout, &mut layout)?; + stdout.flush()?; + } + // Process input. if let Some(key) = input_keys.next() { match key.expect("Error reading input") { @@ -248,21 +247,27 @@ pub fn run( } }, // Escape ^W, and ^U clear input buffer. - Key::Esc | Key::Ctrl('w') | Key::Ctrl('u') => { + Key::Esc | Key::Ctrl('u') => { buffer.reset(); insert_mode = false; update_menu = true; layout.force_redraw = true; }, + // ^W removes last word. + Key::Ctrl('w') => { + if !buffer.strip_word() { + insert_mode = false; + update_menu = true; + layout.force_redraw = true; + } + }, // Backspace. Key::Backspace => { // Delete last char in buffer. - if buffer.pop().is_some() { - if buffer.is_empty() { - insert_mode = false; - update_menu = true; - layout.force_redraw = true; - } + if buffer.strip_char() && buffer.is_empty() { + insert_mode = false; + update_menu = true; + layout.force_redraw = true; } }, // Forward every char if in insert mode.