Skip to content
Open
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
4 changes: 1 addition & 3 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use console::{Key, Term, measure_text_width};
use termcolor::{Buffer, WriteColor};

use crate::ctrlc;
use crate::keys::{CTRL_U, CTRL_W};
use crate::{Theme, theme};

/// Trait for implementing autocompletion features for text inputs.
Expand Down Expand Up @@ -200,9 +201,6 @@ pub struct Input<'a> {
suggestions_scroll_offset: usize,
}

const CTRL_U: char = '\u{15}';
const CTRL_W: char = '\u{17}';

impl<'a> Input<'a> {
/// Creates a new input with the given title.
pub fn new<S: Into<String>>(title: S) -> Self {
Expand Down
6 changes: 6 additions & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Ctrl-N/Ctrl-P are the readline/emacs bindings for next/previous line
pub(crate) const CTRL_N: char = '\u{e}';
pub(crate) const CTRL_P: char = '\u{10}';

pub(crate) const CTRL_U: char = '\u{15}';
pub(crate) const CTRL_W: char = '\u{17}';
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod dialog;
mod event;
mod height;
mod input;
mod keys;
mod list;
mod multiselect;
mod option;
Expand Down
9 changes: 6 additions & 3 deletions src/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use console::{Key, Term};
use std::io::Write;
use termcolor::{Buffer, WriteColor};

use crate::keys::{CTRL_N, CTRL_P};
use crate::{Theme, ctrlc, theme};

/// Display a list of options
Expand Down Expand Up @@ -152,17 +153,19 @@ impl<'a> List<'a> {
};
if self.filtering {
match key {
Key::ArrowDown | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char(CTRL_P) => self.handle_up(),
Key::Enter => self.handle_stop_filtering(true)?,
Key::Escape => self.handle_stop_filtering(false)?,
Key::Backspace => self.handle_filter_backspace()?,
Key::Char(c) => self.handle_filter_key(c)?,
Key::Char(c) if !c.is_control() => self.handle_filter_key(c)?,
_ => {}
}
} else {
self.term.hide_cursor()?;
match key {
Key::ArrowUp | Key::Char('k') => self.handle_up(),
Key::ArrowDown | Key::Char('j') => self.handle_down()?,
Key::ArrowDown | Key::Char('j') | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char('k') | Key::Char(CTRL_P) => self.handle_up(),
Comment on lines +167 to +168

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep user-facing navigation help synchronized with the new bindings.

Each handler now supports Ctrl-N/Ctrl-P, but its corresponding help text still lists only arrows and j/k.

  • src/list.rs#L165-L166: update the help text near Line 330.
  • src/multiselect.rs#L222-L223: update the help text near Line 610.
  • src/select.rs#L192-L193: update the help text near Line 453.
📍 Affects 3 files
  • src/list.rs#L165-L166 (this comment)
  • src/multiselect.rs#L222-L223
  • src/select.rs#L192-L193
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/list.rs` around lines 165 - 166, The navigation help text must document
the newly supported Ctrl-N/Ctrl-P bindings alongside the existing arrow and j/k
keys. Update the corresponding help entries near the handlers in src/list.rs
(around lines 330), src/multiselect.rs (around line 610), and src/select.rs
(around line 453); no handler changes are needed at the cited sites.

Key::ArrowLeft | Key::Char('h') => self.handle_left()?,
Key::ArrowRight | Key::Char('l') => self.handle_right()?,
Key::Char('/') if self.filterable => self.handle_start_filtering(),
Expand Down
11 changes: 6 additions & 5 deletions src/multiselect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use fuzzy_matcher::skim::SkimMatcherV2;
use itertools::Itertools;
use termcolor::{Buffer, WriteColor};

use crate::keys::{CTRL_N, CTRL_P};
use crate::theme::Theme;
use crate::{DemandOption, ctrlc, theme};

Expand Down Expand Up @@ -199,8 +200,8 @@ impl<'a, T> MultiSelect<'a, T> {
};
if self.filtering {
match key {
Key::ArrowDown => self.handle_down()?,
Key::ArrowUp => self.handle_up()?,
Key::ArrowDown | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char(CTRL_P) => self.handle_up()?,
Comment on lines +203 to +204

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Advertise the new navigation keys in the help text.

print_help_keys still displays only ↑/↓ (filtering) and ↑/↓/k/j (non-filtering), so users cannot discover Ctrl-N/Ctrl-P. Update the help labels alongside these bindings.

Also applies to: 222-223

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/multiselect.rs` around lines 203 - 204, Update the help labels in
print_help_keys for both filtering and non-filtering modes to include Ctrl-N and
Ctrl-P alongside the existing navigation keys, matching the bindings in
handle_down and handle_up.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve custom Ctrl-N/Ctrl-P toggle bindings.

These navigation arms now match before key if key == self.toggle_key. Because toggle_key accepts any Key, configuring it as Ctrl-N or Ctrl-P silently changes its behavior from toggling to navigation. Preserve the custom binding or explicitly reject/document these keys as reserved.

Also applies to: 222-223

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/multiselect.rs` around lines 203 - 204, Update the key dispatch around
the ArrowDown/ArrowUp arms and the toggle_key guard so a custom toggle_key set
to Ctrl-N or Ctrl-P is handled as the toggle action rather than navigation.
Preserve navigation behavior for those keys when they are not configured as the
toggle key, or explicitly reject them as reserved if that is the established
design.

Key::ArrowLeft => self.handle_left()?,
Key::ArrowRight => self.handle_right()?,
Key::Enter => {
Expand All @@ -212,14 +213,14 @@ impl<'a, T> MultiSelect<'a, T> {
Key::Escape => self.handle_stop_filtering(false)?,
Key::Backspace => self.handle_filter_backspace()?,
key if key == self.toggle_key => self.handle_toggle(),
Key::Char(c) => self.handle_filter_key(c)?,
Key::Char(c) if !c.is_control() => self.handle_filter_key(c)?,
_ => {}
}
} else {
self.term.hide_cursor()?;
match key {
Key::ArrowDown | Key::Char('j') => self.handle_down()?,
Key::ArrowUp | Key::Char('k') => self.handle_up()?,
Key::ArrowDown | Key::Char('j') | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char('k') | Key::Char(CTRL_P) => self.handle_up()?,
Key::ArrowLeft | Key::Char('h') => self.handle_left()?,
Key::ArrowRight | Key::Char('l') => self.handle_right()?,
key if key == self.toggle_key || key == Key::Char('x') => self.handle_toggle(),
Expand Down
11 changes: 6 additions & 5 deletions src/select.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io;
use std::io::Write;

use crate::keys::{CTRL_N, CTRL_P};
use crate::theme::Theme;
use crate::{DemandOption, ctrlc, theme};
use console::{Alignment, Key, Term};
Expand Down Expand Up @@ -174,22 +175,22 @@ impl<'a, T> Select<'a, T> {
};
if self.filtering {
match key {
Key::ArrowDown => self.handle_down()?,
Key::ArrowUp => self.handle_up()?,
Key::ArrowDown | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char(CTRL_P) => self.handle_up()?,
Key::ArrowLeft => self.handle_left()?,
Key::ArrowRight => self.handle_right()?,
Key::Enter if !self.visible_options().is_empty() => {
return enter(self);
}
Key::Escape => self.handle_stop_filtering(false)?,
Key::Backspace => self.handle_filter_backspace()?,
Key::Char(c) => self.handle_filter_key(c)?,
Key::Char(c) if !c.is_control() => self.handle_filter_key(c)?,
_ => {}
}
} else {
match key {
Key::ArrowDown | Key::Char('j') => self.handle_down()?,
Key::ArrowUp | Key::Char('k') => self.handle_up()?,
Key::ArrowDown | Key::Char('j') | Key::Char(CTRL_N) => self.handle_down()?,
Key::ArrowUp | Key::Char('k') | Key::Char(CTRL_P) => self.handle_up()?,
Key::ArrowLeft | Key::Char('h') => self.handle_left()?,
Key::ArrowRight | Key::Char('l') => self.handle_right()?,
Key::Char('/') if self.filterable => self.handle_start_filtering(),
Expand Down