Skip to content

Commit

Permalink
Use tui-input to handle readline
Browse files Browse the repository at this point in the history
  • Loading branch information
YS-L committed Dec 31, 2023
1 parent fe09917 commit df876e0
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 53 deletions.
15 changes: 13 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ clap = { version = "4.2", features = ["derive"] }
tempfile = "3.5"
regex = "1.8"
csv-sniffer = "0.3.1"
tui-input = { version = "0.8", features = ["crossterm"] }

[target.'cfg(windows)'.dependencies]
crossterm = "0.27.0"
Expand Down
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,9 +366,9 @@ impl App {
self.csv_table_state.reset_buffer();
self.csv_table_state.set_cols_offset(0);
}
Control::BufferContent(buf) => {
Control::BufferContent(input) => {
self.csv_table_state
.set_buffer(self.input_handler.mode(), buf.as_str());
.set_buffer(self.input_handler.mode(), input.clone());
}
Control::BufferReset => {
self.csv_table_state.reset_buffer();
Expand Down
67 changes: 24 additions & 43 deletions src/input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::util::events::{CsvlensEvent, CsvlensEvents};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use std::collections::HashMap;
use tui_input::backend::crossterm::EventHandler;
use tui_input::Input;

pub enum Control {
ScrollUp,
Expand All @@ -26,7 +28,7 @@ pub enum Control {
Filter(String),
FilterColumns(String),
Quit,
BufferContent(String),
BufferContent(Input),
BufferReset,
Select,
ToggleSelectionType,
Expand All @@ -44,7 +46,7 @@ impl Control {
}

enum BufferState {
Active(String),
Active(Input),
Inactive,
}

Expand Down Expand Up @@ -129,9 +131,9 @@ impl InputHandler {
KeyCode::Char('d') => Control::ScrollHalfPageDown,
KeyCode::Char('u') => Control::ScrollHalfPageUp,
KeyCode::Char(x) if "0123456789".contains(x.to_string().as_str()) => {
self.buffer_state = BufferState::Active(x.to_string());
self.buffer_state = BufferState::Active(Input::new(x.to_string()));
self.mode = InputMode::GotoLine;
Control::BufferContent(x.to_string())
Control::BufferContent(Input::new(x.to_string()))
}
KeyCode::Char('/') => {
self.init_buffer(InputMode::Find);
Expand Down Expand Up @@ -172,9 +174,9 @@ impl InputHandler {
}

fn handler_buffering(&mut self, key_event: KeyEvent) -> Control {
let cur_buffer = match &self.buffer_state {
BufferState::Active(buffer) => buffer.as_str(),
BufferState::Inactive => "",
let input = match &mut self.buffer_state {
BufferState::Active(input) => input,
BufferState::Inactive => return Control::Nothing,
};
// SHIFT needed to capture capitalised characters
if key_event.modifiers != KeyModifiers::NONE && key_event.modifiers != KeyModifiers::SHIFT {
Expand All @@ -188,21 +190,9 @@ impl InputHandler {
self.reset_buffer();
Control::BufferReset
}
KeyCode::Backspace => {
let new_buffer = match &self.buffer_state {
BufferState::Active(buffer) => {
let mut chars = buffer.chars();
chars.next_back();
chars.as_str().to_owned()
}
BufferState::Inactive => "".to_owned(),
};
self.buffer_state = BufferState::Active(new_buffer.clone());
Control::BufferContent(new_buffer)
}
KeyCode::Char('g' | 'G') | KeyCode::Enter if self.mode == InputMode::GotoLine => {
let goto_line = match &self.buffer_state {
BufferState::Active(buf) => buf.parse::<usize>().ok(),
BufferState::Active(input) => input.value().parse::<usize>().ok(),
BufferState::Inactive => None,
};
let res = if let Some(n) = goto_line {
Expand All @@ -219,49 +209,40 @@ impl InputHandler {
_ => self.mode,
};
if let Some(buf) = self.buffer_history.get(mode) {
self.buffer_state = BufferState::Active(buf.clone());
Control::BufferContent(buf)
self.buffer_state = BufferState::Active(Input::new(buf.clone()));
Control::BufferContent(Input::new(buf))
} else {
Control::Nothing
}
}
KeyCode::Enter => {
let control;
if cur_buffer.is_empty() {
if input.value().is_empty() {
control = Control::BufferReset;
} else if self.mode == InputMode::Find {
control = Control::Find(cur_buffer.to_string());
control = Control::Find(input.value().to_string());
} else if self.mode == InputMode::Filter {
control = Control::Filter(cur_buffer.to_string());
control = Control::Filter(input.value().to_string());
} else if self.mode == InputMode::FilterColumns {
control = Control::FilterColumns(cur_buffer.to_string());
control = Control::FilterColumns(input.value().to_string());
} else {
control = Control::BufferReset;
}
if self.mode == InputMode::Filter {
// Share buffer history between Find and Filter, see also KeyCode::Up
self.buffer_history.set(InputMode::Find, cur_buffer);
self.buffer_history.set(InputMode::Find, input.value());
} else {
self.buffer_history.set(self.mode, cur_buffer);
self.buffer_history.set(self.mode, input.value());
}
self.reset_buffer();
control
}
KeyCode::Char('/') => {
if cur_buffer.is_empty() && self.mode == InputMode::Find {
self.mode = InputMode::Filter;
_ => {
if input.handle_event(&Event::Key(key_event)).is_some() {
return Control::BufferContent(input.clone());
}
Control::BufferContent("".to_string())
}
KeyCode::Char(x) => {
let new_buffer = match &self.buffer_state {
BufferState::Active(buffer) => buffer.to_owned() + x.to_string().as_str(),
BufferState::Inactive => x.to_string(),
};
self.buffer_state = BufferState::Active(new_buffer.clone());
Control::BufferContent(new_buffer)
Control::Nothing
}
_ => Control::Nothing,
}
}

Expand Down Expand Up @@ -297,7 +278,7 @@ impl InputHandler {
}

fn init_buffer(&mut self, mode: InputMode) {
self.buffer_state = BufferState::Active("".into());
self.buffer_state = BufferState::Active(Input::default());
self.mode = mode;
}

Expand Down
24 changes: 18 additions & 6 deletions src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ratatui::text::{Line, Span};
use ratatui::widgets::Widget;
use ratatui::widgets::{Block, Borders, StatefulWidget};
use regex::Regex;
use tui_input::Input;

use std::cmp::{max, min};
use std::collections::HashMap;
Expand Down Expand Up @@ -503,15 +504,26 @@ impl<'a> CsvTable<'a> {
}
}

fn format_input(&self, input: &Input) -> String {
let chars_before: String = input.value().chars().take(input.cursor()).collect();
let chars_after: String = input.value().chars().skip(input.cursor()).collect();
format!("{chars_before}❙{chars_after}")
}

fn render_status(&self, area: Rect, buf: &mut Buffer, state: &mut CsvTableState) {
// Content of status line (separator already plotted elsewhere)
let style = Style::default().fg(Color::Rgb(128, 128, 128));
let mut content: String;
if let Some(msg) = &state.transient_message {
content = msg.to_owned();
} else if let BufferState::Enabled(buffer_mode, buf) = &state.buffer_content {
content = buf.to_owned();
let format_buffer = |prefix: &str| format!("{prefix}: {content}█");
} else if let BufferState::Enabled(buffer_mode, input) = &state.buffer_content {
content = input.value().to_string();
let format_buffer = |prefix: &str| {
format!(
"{prefix}: {formatted_input}",
formatted_input = self.format_input(input)
)
};
match buffer_mode {
InputMode::GotoLine => {
content = format_buffer("Go to line");
Expand Down Expand Up @@ -733,7 +745,7 @@ impl ViewLayout {

pub enum BufferState {
Disabled,
Enabled(InputMode, String),
Enabled(InputMode, Input),
}

pub enum FinderState {
Expand Down Expand Up @@ -974,8 +986,8 @@ impl CsvTableState {
self.total_cols = n;
}

pub fn set_buffer(&mut self, mode: InputMode, buf: &str) {
self.buffer_content = BufferState::Enabled(mode, buf.to_string());
pub fn set_buffer(&mut self, mode: InputMode, input: Input) {
self.buffer_content = BufferState::Enabled(mode, input);
}

pub fn reset_buffer(&mut self) {
Expand Down

0 comments on commit df876e0

Please sign in to comment.