Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Shift-Up & Shift-Down to navigate in lists #31

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ target/

# Ignore GIF files in the tapes directory
tapes/*.gif

.vscode
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ anyhow = "1.0.80"
clap = { version = "4.5.1", features = ["derive"] }
gag = "1.0.0"
j9 = "0.1.3"
promkit = "0.3.2"
promkit = "0.3.3"
radix_trie = "0.2.1"

# The profile that 'cargo dist' will build with
Expand Down
5 changes: 5 additions & 0 deletions src/jnv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use promkit::{
text, text_editor, Prompt, PromptSignal, Renderer,
};

mod editing;
mod keymap;
mod render;
mod trie;
Expand Down Expand Up @@ -83,6 +84,7 @@ impl Jnv {
active_char_style: StyleBuilder::new().bgc(Color::Magenta).build(),
inactive_char_style: StyleBuilder::new().build(),
edit_mode,
word_break_chars: Default::default(),
lines: Default::default(),
},
hint_message_renderer: text::Renderer {
Expand Down Expand Up @@ -292,6 +294,8 @@ impl Jnv {
let suggest_clone = rc_self_clone.borrow().suggest.clone();
let suggest_renderer_clone = rc_self_clone.borrow().suggest_renderer.clone();
let json_renderer_clone = rc_self_clone.borrow().json_renderer.clone();
let input_json_stream_clone = rc_self_clone.borrow().input_json_stream.clone();

Ok(Prompt::try_new(
Box::new(self::render::Renderer {
keymap: keymap_clone,
Expand All @@ -302,6 +306,7 @@ impl Jnv {
suggest: suggest_clone,
suggest_snapshot: Snapshot::<listbox::Renderer>::new(suggest_renderer_clone),
json_snapshot: Snapshot::<json::Renderer>::new(json_renderer_clone),
input_json_stream: input_json_stream_clone
}),
Box::new(
move |event: &Event,
Expand Down
119 changes: 119 additions & 0 deletions src/jnv/editing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use gag::Gag;
use promkit::serde_json::Value;

pub fn add_to_nearest_array_index(renderer: &mut crate::jnv::render::Renderer, value: isize) -> bool {
let query_editor_after_mut = renderer.query_editor_snapshot.after_mut();
let cursor = query_editor_after_mut.texteditor.position();
let query = query_editor_after_mut
.texteditor
.text_without_cursor()
.to_string();
let mut previous = None;
let mut previous_distance = usize::MAX;
let mut pos = 0;

while pos < query.len() {
match find_array_index(&query, 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 = query.len()
} else {
pos = end + 1;
}
}
None => {
pos += 1
}
}
}
match previous {
None => false,
Some((start, end)) => {
let before = &query[0..start + 1]; // ends with '['
let after = &query[end..]; // starts with ']'
let number = &query[start + 1..end];
let current_value: isize = match number.parse() {
Ok(value) => value,
Err(_) => return false,
};
let mut new_value = current_value + value;

// array bound checking and cycling
let array = &before[..before.len() - 1];
let len = query_array_length(&renderer.input_json_stream, array);
if len == -1 {
return false;
}

if current_value == 0 && new_value == -1 {
new_value = len - 1;
}
if new_value > 0 && new_value >= len {
new_value = 0;
} else if new_value < -len {
new_value = -1;
}
let new_query = format!("{before}{new_value}{after}");
query_editor_after_mut.texteditor.replace(&new_query);
let mut npos = query_editor_after_mut.texteditor.position();
while npos > cursor {
query_editor_after_mut.texteditor.backward();
npos = query_editor_after_mut.texteditor.position();
}
true
}
}
}

fn query_array_length(input_json_stream: &[Value], array: &str) -> isize {
let stream_len = input_json_stream.len();
if stream_len == 0 || stream_len > 1 {
// honestly, don't now how to handle it
return -1;
}
let query_len = format!("{array} | length");
let v = &input_json_stream[0];

let _ignore_err = Gag::stderr().unwrap();

match j9::run(&query_len, &v.to_string()) {
Ok(ret) => {
if ret.is_empty() {
-1
} else {
ret[0].parse().unwrap()
}
}
Err(_e) => -1,
}
}

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_array_index(query: &str, start: usize) -> Option<(usize, usize)> {
let iter = query.chars().enumerate().skip(start);
let mut iter = iter.skip_while(|(_, ch)| *ch != '[');
let start = match iter.next() {
None => return None,
Some((pos, _)) => pos,
};
let mut iter = iter.skip_while(|(i, ch)| *i == start + 1 && *ch == '-');
let _minus = iter.next().is_some();
let mut iter = iter.skip_while(|(_, ch)| ch.is_ascii_digit());
match iter.next() {
Some((end, ']')) => Some((start, end)),
_ => None
}
}
18 changes: 18 additions & 0 deletions src/jnv/keymap.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::editing::add_to_nearest_array_index;
use promkit::{
crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers},
listbox::Listbox,
Expand Down Expand Up @@ -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::SHIFT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
add_to_nearest_array_index(renderer, 1);
}
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::SHIFT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}) => {
add_to_nearest_array_index(renderer, -1);
}

// Input char.
Event::Key(KeyEvent {
code: KeyCode::Char(ch),
Expand Down
4 changes: 2 additions & 2 deletions src/jnv/render.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use promkit::{
impl_as_any, impl_cast, json, keymap::KeymapManager, listbox, pane::Pane, snapshot::Snapshot,
suggest::Suggest, text, text_editor,
impl_as_any, impl_cast, json, keymap::KeymapManager, listbox, pane::Pane, serde_json::Value, snapshot::Snapshot, suggest::Suggest, text, text_editor
};

#[derive(Clone)]
Expand All @@ -11,6 +10,7 @@ pub struct Renderer {
pub suggest: Suggest,
pub suggest_snapshot: Snapshot<listbox::Renderer>,
pub json_snapshot: Snapshot<json::Renderer>,
pub input_json_stream: Vec<Value>
}

impl_as_any!(Renderer);
Expand Down