Implemented ^W.

This commit is contained in:
shy 2021-04-11 16:38:19 +02:00
parent 4a32bbb9af
commit 21222baa71
3 changed files with 54 additions and 32 deletions

View file

@ -1,12 +1,15 @@
extern crate unicode_segmentation;
use std::io::Write; use std::io::Write;
use termion::{clear, cursor, color}; use termion::{clear, cursor, color};
use termion::raw::RawTerminal; use termion::raw::RawTerminal;
use crate::layout::Layout; use crate::layout::Layout;
use crate::utils; use unicode_segmentation::UnicodeSegmentation;
const PROMPT: &str = "Add alarm: "; const PROMPT: &str = "Add alarm: ";
// Input buffer.
pub struct Buffer { pub struct Buffer {
content: String, content: String,
// Used for error messages. // Used for error messages.
@ -23,21 +26,46 @@ impl Buffer {
} }
} }
// Return reference to buffer content.
pub fn read(&mut self) -> &String { pub fn read(&mut self) -> &String {
self.altered = false; self.altered = false;
&self.content &self.content
} }
// Append char to buffer.
pub fn push(&mut self, value: char) { pub fn push(&mut self, value: char) {
self.altered = true; self.altered = true;
// Reset error message.
self.message = None; self.message = None;
self.content.push(value); self.content.push(value);
} }
pub fn pop(&mut self) -> Option<char> { // Remove last char. Return true if a char was removed.
self.altered = true; pub fn strip_char(&mut self) -> bool {
// Reset error message.
self.message = None; 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 { pub fn is_empty(&self) -> bool {
@ -63,6 +91,7 @@ impl Buffer {
layout: &mut Layout, layout: &mut Layout,
) -> Result<(), std::io::Error> ) -> Result<(), std::io::Error>
{ {
// Write error message if present and return.
if let Some(msg) = self.message { if let Some(msg) = self.message {
write!(stdout, write!(stdout,
"{}{}{}{}{}{}", "{}{}{}{}{}{}",
@ -85,13 +114,6 @@ impl Buffer {
PROMPT, PROMPT,
cursor::Show, cursor::Show,
&self.content)?; &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 { } else {
// Clear buffer display. // Clear buffer display.
write!(stdout, write!(stdout,

View file

@ -29,7 +29,6 @@ pub struct Layout {
pub roster_width: u16, pub roster_width: u16,
pub roster_height: u16, pub roster_height: u16,
pub buffer: Position, pub buffer: Position,
pub cursor: Position,
} }
impl Layout { impl Layout {
@ -51,7 +50,6 @@ impl Layout {
roster_width: 0, roster_width: 0,
roster_height: 0, roster_height: 0,
buffer: Position {col: 0, line: 0}, buffer: Position {col: 0, line: 0},
cursor: Position {col: 1, line: 1},
} }
} }
@ -139,9 +137,6 @@ impl Layout {
line: self.height, line: self.height,
col: 1, 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) { pub fn set_roster_width(&mut self, width: u16) {

View file

@ -17,7 +17,6 @@ use termion::{clear, cursor, style};
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
use termion::event::Key; use termion::event::Key;
use termion::input::TermRead; use termion::input::TermRead;
//use termion::cursor::DetectCursorPos;
use buffer::Buffer; use buffer::Buffer;
use clock::Clock; use clock::Clock;
use layout::Layout; use layout::Layout;
@ -102,10 +101,12 @@ pub fn run(
} }
// Update input buffer display, if requested. // Update input buffer display, if requested.
/*
if buffer.altered { if buffer.altered {
buffer.draw(&mut stdout, &mut layout)?; buffer.draw(&mut stdout, &mut layout)?;
stdout.flush()?; stdout.flush()?;
} }
*/
// Update elapsed time. // Update elapsed time.
let elapsed = if clock.paused { let elapsed = if clock.paused {
@ -194,14 +195,6 @@ pub fn run(
countdown.draw(&mut stdout); 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. // Check any spawned child process.
if let Some(ref mut child) = spawned { if let Some(ref mut child) = spawned {
match child.try_wait() { match child.try_wait() {
@ -228,6 +221,12 @@ pub fn run(
stdout.flush()?; stdout.flush()?;
} }
// Update buffer whenever the cursor is visible.
if insert_mode || buffer.altered {
buffer.draw(&mut stdout, &mut layout)?;
stdout.flush()?;
}
// Process input. // Process input.
if let Some(key) = input_keys.next() { if let Some(key) = input_keys.next() {
match key.expect("Error reading input") { match key.expect("Error reading input") {
@ -248,21 +247,27 @@ pub fn run(
} }
}, },
// Escape ^W, and ^U clear input buffer. // Escape ^W, and ^U clear input buffer.
Key::Esc | Key::Ctrl('w') | Key::Ctrl('u') => { Key::Esc | Key::Ctrl('u') => {
buffer.reset(); buffer.reset();
insert_mode = false; insert_mode = false;
update_menu = true; update_menu = true;
layout.force_redraw = 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. // Backspace.
Key::Backspace => { Key::Backspace => {
// Delete last char in buffer. // Delete last char in buffer.
if buffer.pop().is_some() { if buffer.strip_char() && buffer.is_empty() {
if buffer.is_empty() { insert_mode = false;
insert_mode = false; update_menu = true;
update_menu = true; layout.force_redraw = true;
layout.force_redraw = true;
}
} }
}, },
// Forward every char if in insert mode. // Forward every char if in insert mode.