Implemented ^W.
This commit is contained in:
parent
4a32bbb9af
commit
21222baa71
3 changed files with 54 additions and 32 deletions
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
37
src/lib.rs
37
src/lib.rs
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue