Implement cursor (#412)
This commit is contained in:
parent
119ab9e007
commit
9ac0c60cc4
1 changed files with 86 additions and 15 deletions
|
@ -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(),
|
||||||
|
|
Loading…
Reference in a new issue