From d3097b0bb0379e3b5d0dbf7e9ffbfa5d3e482537 Mon Sep 17 00:00:00 2001 From: Rebel Cat Date: Mon, 25 Mar 2024 21:00:37 +0100 Subject: [PATCH] Use Ctl-Up & Ctl-Down to navigate in lists. Say your prompt is something like this, with `^` indicating your cursor position. ``` .[0].hosts[0] ^ ``` if you type `Ctl-Down`, it will become ``` .[0].hosts[1] ^ ``` --- src/jnv.rs | 1 + src/jnv/editing.rs | 78 ++++++++++++++++++++++++++++++++++++++++++++++ src/jnv/keymap.rs | 18 +++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/jnv/editing.rs diff --git a/src/jnv.rs b/src/jnv.rs index 19a4497..800b025 100644 --- a/src/jnv.rs +++ b/src/jnv.rs @@ -18,6 +18,7 @@ use promkit::{ text, text_editor, Prompt, PromptSignal, Renderer, }; +mod editing; mod keymap; mod render; mod trie; diff --git a/src/jnv/editing.rs b/src/jnv/editing.rs new file mode 100644 index 0000000..7a39a2c --- /dev/null +++ b/src/jnv/editing.rs @@ -0,0 +1,78 @@ +pub fn add_to_nearest_integer(renderer: &mut promkit::text_editor::Renderer, value: isize) -> bool { + let cursor = renderer.texteditor.position(); + let chars = renderer.texteditor.text_without_cursor().chars(); + let mut previous = None; + let mut previous_distance = usize::MAX; + let mut pos = 0; + while pos < chars.len() { + match find_number(&chars, pos) { + Some((start, end)) => { + let distance = distance_from(start, end, cursor); + if distance < previous_distance { + previous = Some((start, end)); + previous_distance = distance; + } + if distance == 0 { + pos = chars.len() + } else { + pos = end + 1; + } + } + None => pos = chars.len(), + } + } + match previous { + None => false, + Some((start, end)) => { + let before: String = chars[0..start].into_iter().collect(); + let after: String = chars[end..].into_iter().collect(); + let number: String = chars[start..end].into_iter().collect(); + let current_value: isize = match number.parse() { + Ok(value) => value, + Err(_) => return false, + }; + let new_value = current_value + value; + let new_query = format!("{before}{new_value}{after}"); + renderer.texteditor.replace(&new_query); + let mut npos = renderer.texteditor.position(); + while npos > cursor { + renderer.texteditor.backward(); + npos = renderer.texteditor.position(); + } + true + } + } +} + +fn distance_from(start: usize, end: usize, cursor: usize) -> usize { + if start <= cursor && cursor < end { + 0 + } else if cursor < start { + start - cursor + } else { + cursor - end + } +} + +fn find_number(chars: &Vec, start: usize) -> Option<(usize, usize)> { + if start >= chars.len() { + return None; + } + match chars[start..] + .iter() + .position(|ch| *ch == '-' || ch.is_digit(10)) + { + Some(num_start) => { + let ch = chars[num_start]; + eprintln!("matched: {ch} at offset {num_start}"); + let end = chars[start + num_start + 1..] + .iter() + .position(|ch| !ch.is_digit(10)); + match end { + Some(end) => Some((start + num_start, start + num_start + 1 + end)), + None => Some((start + num_start, chars.len())), + } + } + None => None, + } +} diff --git a/src/jnv/keymap.rs b/src/jnv/keymap.rs index cc48bab..9d0afee 100644 --- a/src/jnv/keymap.rs +++ b/src/jnv/keymap.rs @@ -1,3 +1,4 @@ +use super::editing::add_to_nearest_integer; use promkit::{ crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers}, listbox::Listbox, @@ -163,6 +164,23 @@ pub fn default(event: &Event, renderer: &mut crate::jnv::render::Renderer) -> Re json_after_mut.stream.collapse_all(); } + Event::Key(KeyEvent { + code: KeyCode::Down, + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + state: KeyEventState::NONE, + }) => { + add_to_nearest_integer(query_editor_after_mut, 1); + } + Event::Key(KeyEvent { + code: KeyCode::Up, + modifiers: KeyModifiers::CONTROL, + kind: KeyEventKind::Press, + state: KeyEventState::NONE, + }) => { + add_to_nearest_integer(query_editor_after_mut, -1); + } + // Input char. Event::Key(KeyEvent { code: KeyCode::Char(ch),