use anyhow::Result; use unicode_width::UnicodeWidthStr; use yazi_config::{KEYMAP, YAZI, keymap::{Chord, Key}}; use yazi_macro::{act, render, render_and}; use yazi_shared::Layer; use yazi_term::{CursorStyle, TERM, event::KeyCode}; use yazi_widgets::Scrollable; use crate::help::HELP_MARGIN; #[derive(Default)] pub struct Help { pub visible: bool, pub layer: Layer, pub(super) bindings: Vec<&'static Chord>, // Filter pub keyword: String, pub in_filter: Option, pub offset: usize, pub cursor: usize, } impl Help { pub fn r#type(&mut self, key: &Key) -> Result { let Some(input) = &mut self.in_filter else { return Ok(false) }; match key { Key { code: KeyCode::Escape, shift: false, ctrl: false, alt: false, super_: false } => { self.in_filter = None; render!(); } Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false, super_: false } => { self.in_filter = None; return Ok(render_and!(true)); // Don't do the `filter_apply` below, since we already have the filtered results. } Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false, super_: false } => { act!(backspace, input)?; } _ => { input.r#type(key)?; } } self.filter_apply(); Ok(true) } pub fn filter_apply(&mut self) { let kw = self.in_filter.as_ref().map_or("", |i| i.value()); if kw.is_empty() { self.keyword = String::new(); self.bindings = KEYMAP.get(self.layer).iter().collect(); } else if self.keyword != kw { self.keyword = kw.to_owned(); self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).collect(); } render!(self.scroll(0)); } } impl Help { // --- Keyword pub fn keyword(&self) -> Option { self .in_filter .as_ref() .map(|i| i.value()) .or(Some(self.keyword.as_str()).filter(|&s| !s.is_empty())) .map(|s| format!("Filter: {s}")) } // --- Bindings pub fn window(&self) -> &[&Chord] { let end = (self.offset + self.limit()).min(self.bindings.len()); &self.bindings[self.offset..end] } // --- Cursor pub fn cursor(&self) -> Option<(u16, u16)> { if !self.visible || self.in_filter.is_none() { return None; } if let Some(kw) = self.keyword() { return Some((kw.width() as u16, TERM.dimension().rows)); } None } pub fn rel_cursor(&self) -> usize { self.cursor - self.offset } pub fn cursor_shape(&self) -> CursorStyle { if YAZI.input.cursor_blink { CursorStyle::BlinkingBlock } else { CursorStyle::SteadyBlock } } } impl Scrollable for Help { fn total(&self) -> usize { self.bindings.len() } fn limit(&self) -> usize { TERM.dimension().rows.saturating_sub(HELP_MARGIN) as usize } fn cursor_mut(&mut self) -> &mut usize { &mut self.cursor } fn offset_mut(&mut self) -> &mut usize { &mut self.offset } }