Implement cursor (#412)

This commit is contained in:
b3nj5m1n 2022-05-24 22:44:12 +02:00 committed by GitHub
parent 119ab9e007
commit 9ac0c60cc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -110,6 +110,8 @@ impl Cmd {
struct State { struct State {
input: String, input: String,
cursor_index: usize,
filter_mode: FilterMode, filter_mode: FilterMode,
results: Vec<History>, results: Vec<History>,
@ -277,6 +279,31 @@ async fn query_results(
Ok(()) Ok(())
} }
fn get_input_prefix(app: &mut State, i: usize) -> String {
return app.input.chars().take(i).collect();
}
fn get_input_suffix(app: &mut State, i: usize) -> String {
return app.input.chars().skip(i).collect();
}
fn insert_char_into_input(app: &mut State, i: usize, c: char) {
let mut result = String::from("");
result.push_str(&get_input_prefix(app, i));
result.push_str(&c.to_string());
result.push_str(&get_input_suffix(app, i));
app.input = result;
}
fn remove_char_from_input(app: &mut State, i: usize) -> char {
let mut result = String::from("");
result.push_str(&get_input_prefix(app, i - 1));
result.push_str(&get_input_suffix(app, i));
let c = app.input.chars().nth(i - 1).unwrap();
app.input = result;
c
}
#[allow(clippy::too_many_lines)]
async fn key_handler( async fn key_handler(
input: Key, input: Key,
search_mode: SearchMode, search_mode: SearchMode,
@ -304,29 +331,56 @@ async fn key_handler(
.map_or(app.input.clone(), |h| h.command.clone()), .map_or(app.input.clone(), |h| h.command.clone()),
); );
} }
Key::Left | Key::Ctrl('h') => {
if app.cursor_index != 0 {
app.cursor_index -= 1;
}
}
Key::Right | Key::Ctrl('l') => {
if app.cursor_index < app.input.width() {
app.cursor_index += 1;
}
}
Key::Ctrl('a') => {
app.cursor_index = 0;
}
Key::Ctrl('e') => {
app.cursor_index = app.input.chars().count();
}
Key::Char(c) => { Key::Char(c) => {
app.input.push(c); insert_char_into_input(app, app.cursor_index, c);
app.cursor_index += 1;
query_results(app, search_mode, db).await.unwrap(); query_results(app, search_mode, db).await.unwrap();
} }
Key::Backspace => { Key::Backspace => {
app.input.pop(); if app.cursor_index == 0 {
query_results(app, search_mode, db).await.unwrap();
}
// \u{7f} is escape sequence for backspace
Key::Alt('\u{7f}') => {
let words: Vec<&str> = app.input.split(' ').collect();
if words.is_empty() {
return None; return None;
} }
if words.len() == 1 { remove_char_from_input(app, app.cursor_index);
app.input = String::from(""); app.cursor_index -= 1;
} else { query_results(app, search_mode, db).await.unwrap();
app.input = words[0..(words.len() - 1)].join(" "); }
Key::Ctrl('w') => {
let mut stop_on_next_whitespace = false;
loop {
if app.cursor_index == 0 {
break;
}
if app.input.chars().nth(app.cursor_index - 1) == Some(' ')
&& stop_on_next_whitespace
{
break;
}
if !remove_char_from_input(app, app.cursor_index).is_whitespace() {
stop_on_next_whitespace = true;
}
app.cursor_index -= 1;
} }
query_results(app, search_mode, db).await.unwrap(); query_results(app, search_mode, db).await.unwrap();
} }
Key::Ctrl('u') => { Key::Ctrl('u') => {
app.input = String::from(""); app.input = String::from("");
app.cursor_index = 0;
query_results(app, search_mode, db).await.unwrap(); query_results(app, search_mode, db).await.unwrap();
} }
Key::Ctrl('r') => { Key::Ctrl('r') => {
@ -442,9 +496,16 @@ fn draw<T: Backend>(f: &mut Frame<'_, T>, history_count: i64, app: &mut State) {
); );
f.render_widget(input, chunks[2]); f.render_widget(input, chunks[2]);
let width = UnicodeWidthStr::width(
app.input
.chars()
.take(app.cursor_index)
.collect::<String>()
.as_str(),
);
f.set_cursor( f.set_cursor(
// Put cursor past the end of the input text // Put cursor past the end of the input text
chunks[2].x + app.input.width() as u16 + 1, chunks[2].x + width as u16 + 1,
// Move one line down, from the border to the input line // Move one line down, from the border to the input line
chunks[2].y + 1, chunks[2].y + 1,
); );
@ -514,7 +575,13 @@ fn draw_compact<T: Backend>(f: &mut Frame<'_, T>, history_count: i64, app: &mut
app.render_results(f, chunks[1], Block::default()); app.render_results(f, chunks[1], Block::default());
f.render_widget(input, chunks[2]); f.render_widget(input, chunks[2]);
let extra_width = app.input.width() + filter_mode.len(); let extra_width = UnicodeWidthStr::width(
app.input
.chars()
.take(app.cursor_index)
.collect::<String>()
.as_str(),
) + filter_mode.len();
f.set_cursor( f.set_cursor(
// Put cursor past the end of the input text // Put cursor past the end of the input text
@ -544,8 +611,12 @@ async fn select_history(
// Setup event handlers // Setup event handlers
let events = Events::new(); let events = Events::new();
let input = query.join(" ");
// Put the cursor at the end of the query by default
let cursor_index = input.chars().count();
let mut app = State { let mut app = State {
input: query.join(" "), input,
cursor_index,
results: Vec::new(), results: Vec::new(),
results_state: ListState::default(), results_state: ListState::default(),
context: current_context(), context: current_context(),