mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
ca4327eb22
commit
44abc4c997
13 changed files with 188 additions and 60 deletions
|
|
@ -62,6 +62,9 @@ impl Ctx {
|
|||
let Rect { x, y, .. } = self.area(&self.input.position);
|
||||
return Some((x + 1 + self.input.cursor(), y + 1));
|
||||
}
|
||||
if let Some((x, y)) = self.help.cursor() {
|
||||
return Some((x, y));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +72,7 @@ impl Ctx {
|
|||
pub(super) fn layer(&self) -> KeymapLayer {
|
||||
if self.which.visible {
|
||||
KeymapLayer::Which
|
||||
} else if self.help.visible {
|
||||
} else if self.help.visible() {
|
||||
KeymapLayer::Help
|
||||
} else if self.input.visible {
|
||||
KeymapLayer::Input
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@ impl Executor {
|
|||
return cx.which.press(key);
|
||||
}
|
||||
|
||||
if layer == KeymapLayer::Input && cx.input.mode() == InputMode::Insert {
|
||||
if let Some(c) = key.plain() {
|
||||
return cx.input.type_(c);
|
||||
}
|
||||
if layer == KeymapLayer::Input && cx.input.type_(&key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if layer == KeymapLayer::Help && cx.help.type_(&key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for Control { on, exec, .. } in KEYMAP.get(layer) {
|
||||
|
|
@ -245,10 +247,7 @@ impl Executor {
|
|||
"help" => cx.help.toggle(cx.layer()),
|
||||
_ => false,
|
||||
},
|
||||
InputMode::Insert => match exec.cmd.as_str() {
|
||||
"backspace" => cx.input.backspace(),
|
||||
_ => false,
|
||||
},
|
||||
InputMode::Insert => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,35 +17,18 @@ impl Widget for Bindings<'_> {
|
|||
return;
|
||||
}
|
||||
|
||||
let col1 = bindings
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let item = ListItem::new(c.on.iter().map(ToString::to_string).collect::<String>());
|
||||
item
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let col1 = bindings.iter().map(|c| ListItem::new(c.on())).collect::<Vec<_>>();
|
||||
|
||||
let col2 = bindings
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let item = ListItem::new(c.exec());
|
||||
item
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let col2 = bindings.iter().map(|c| ListItem::new(c.exec())).collect::<Vec<_>>();
|
||||
|
||||
let col3 = bindings
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let item = ListItem::new(if let Some(ref desc) = c.desc { desc } else { "-" });
|
||||
item
|
||||
})
|
||||
.map(|c| ListItem::new(if let Some(ref desc) = c.desc { desc } else { "-" }))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let chunks = layout::Layout::new()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[Constraint::Ratio(1, 9), Constraint::Ratio(4, 9), Constraint::Ratio(4, 9)].as_ref(),
|
||||
)
|
||||
.constraints([Constraint::Ratio(2, 10), Constraint::Ratio(3, 10), Constraint::Ratio(5, 10)])
|
||||
.split(area);
|
||||
|
||||
let cursor = self.cx.help.rel_cursor() as u16;
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ impl<'a> Widget for Layout<'a> {
|
|||
.split(area);
|
||||
|
||||
Clear.render(area, buf);
|
||||
Paragraph::new("manager.help")
|
||||
.style(Style::new().fg(Color::Rgb(35, 39, 59)).bg(Color::Rgb(200, 211, 248)))
|
||||
|
||||
let help = &self.cx.help;
|
||||
Paragraph::new(help.keyword().unwrap_or_else(|| format!("{}.help", help.layer())))
|
||||
.style(Style::new().fg(Color::Black).bg(Color::White))
|
||||
.render(chunks[1], buf);
|
||||
|
||||
Bindings::new(self.cx).render(chunks[0], buf);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ impl<'a> Widget for Root<'a> {
|
|||
input::Input::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
if self.cx.help.visible {
|
||||
if self.cx.help.visible() {
|
||||
help::Layout::new(self.cx).render(area, buf);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,6 @@
|
|||
- `--submit`: Submit the input.
|
||||
|
||||
- escape: Cancel insert mode and enter normal mode.
|
||||
- backspace: Delete the character before the cursor.
|
||||
|
||||
## Help
|
||||
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ keymap = [
|
|||
[tasks]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "<Esc>" ], exec = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the task manager" },
|
||||
{ on = [ "w" ], exec = "close", desc = "Hide the task manager" },
|
||||
|
||||
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
|
||||
|
|
@ -152,7 +152,6 @@ keymap = [
|
|||
{ on = [ "<C-q>" ], exec = "close", desc = "Cancel input" },
|
||||
{ on = [ "<Enter>" ], exec = "close --submit", desc = "Submit the input" },
|
||||
{ on = [ "<Esc>" ], exec = "escape", desc = "Go back the normal mode, or cancel input" },
|
||||
{ on = [ "<Backspace>" ], exec = "backspace", desc = "Delete the character before the cursor" },
|
||||
|
||||
# Mode
|
||||
{ on = [ "i" ], exec = "insert", desc = "Enter insert mode" },
|
||||
|
|
@ -196,8 +195,9 @@ keymap = [
|
|||
[help]
|
||||
|
||||
keymap = [
|
||||
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the help" },
|
||||
{ on = [ "<Esc>" ], exec = "escape", desc = "Clear the filter, or hide the help" },
|
||||
{ on = [ "q" ], exec = "close", desc = "Exit the process" },
|
||||
{ on = [ "<C-q>" ], exec = "close", desc = "Hide the help" },
|
||||
|
||||
# Navigation
|
||||
{ on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" },
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ pub struct Control {
|
|||
}
|
||||
|
||||
impl Control {
|
||||
#[inline]
|
||||
pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() }
|
||||
|
||||
#[inline]
|
||||
pub fn exec(&self) -> String {
|
||||
self.exec.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
|
||||
|
|
@ -22,4 +25,11 @@ impl Control {
|
|||
pub fn desc_or_exec(&self) -> Cow<str> {
|
||||
if let Some(ref s) = self.desc { Cow::Borrowed(s) } else { self.exec().into() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, s: &str) -> bool {
|
||||
self.desc.as_ref().map(|d| d.contains(s)).unwrap_or(false)
|
||||
|| self.exec().contains(s)
|
||||
|| self.on().contains(s)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ impl Key {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_enter(&self) -> bool {
|
||||
matches!(self, Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Key {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use std::fmt::{self, Display};
|
||||
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
||||
use super::Control;
|
||||
|
|
@ -12,16 +14,6 @@ pub struct Keymap {
|
|||
pub help: Vec<Control>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum KeymapLayer {
|
||||
Manager,
|
||||
Tasks,
|
||||
Select,
|
||||
Input,
|
||||
Help,
|
||||
Which,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Keymap {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
|
|
@ -68,3 +60,27 @@ impl Keymap {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub enum KeymapLayer {
|
||||
#[default]
|
||||
Manager,
|
||||
Tasks,
|
||||
Select,
|
||||
Input,
|
||||
Help,
|
||||
Which,
|
||||
}
|
||||
|
||||
impl Display for KeymapLayer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
KeymapLayer::Manager => write!(f, "manager"),
|
||||
KeymapLayer::Tasks => write!(f, "tasks"),
|
||||
KeymapLayer::Select => write!(f, "select"),
|
||||
KeymapLayer::Input => write!(f, "input"),
|
||||
KeymapLayer::Help => write!(f, "help"),
|
||||
KeymapLayer::Which => write!(f, "which"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
use config::{keymap::{Control, KeymapLayer}, KEYMAP};
|
||||
use config::{keymap::{Control, Key, KeymapLayer}, KEYMAP};
|
||||
use shared::Term;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use super::HELP_MARGIN;
|
||||
use crate::emit;
|
||||
use crate::{emit, input::Input};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Help {
|
||||
pub visible: bool,
|
||||
bindings: Vec<Control>,
|
||||
visible: bool,
|
||||
layer: KeymapLayer,
|
||||
bindings: Vec<Control>,
|
||||
|
||||
// Filter
|
||||
keyword: Option<String>,
|
||||
in_filter: Option<Input>,
|
||||
|
||||
offset: usize,
|
||||
cursor: usize,
|
||||
|
|
@ -19,16 +25,35 @@ impl Help {
|
|||
|
||||
pub fn toggle(&mut self, layer: KeymapLayer) -> bool {
|
||||
self.visible = !self.visible;
|
||||
self.bindings = if self.visible { KEYMAP.get(layer).clone() } else { Vec::new() };
|
||||
self.layer = layer;
|
||||
|
||||
self.keyword = Some(String::new());
|
||||
self.in_filter = None;
|
||||
self.filter_apply();
|
||||
|
||||
self.offset = 0;
|
||||
self.cursor = 0;
|
||||
|
||||
emit!(Peek); // Show/hide preview for images
|
||||
true
|
||||
}
|
||||
|
||||
pub fn escape(&mut self) -> bool { todo!() }
|
||||
pub fn escape(&mut self) -> bool {
|
||||
if self.in_filter.is_some() {
|
||||
self.in_filter = None;
|
||||
self.filter_apply();
|
||||
true
|
||||
} else {
|
||||
self.toggle(self.layer)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn arrow(&mut self, step: isize) -> bool {
|
||||
let len = self.bindings.len();
|
||||
self.offset = self.offset.min(len);
|
||||
self.cursor = self.cursor.min(len.saturating_sub(1));
|
||||
|
||||
if step > 0 { self.next(step as usize) } else { self.prev(step.unsigned_abs()) }
|
||||
}
|
||||
|
||||
|
|
@ -60,17 +85,86 @@ impl Help {
|
|||
old != self.cursor
|
||||
}
|
||||
|
||||
pub fn filter(&mut self) -> bool {
|
||||
self.in_filter = Some(Default::default());
|
||||
self.filter_apply();
|
||||
true
|
||||
}
|
||||
|
||||
fn filter_apply(&mut self) -> bool {
|
||||
let kw = self.in_filter.as_ref().map(|i| i.value()).filter(|v| !v.is_empty());
|
||||
if self.keyword.as_deref() == kw {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(kw) = kw {
|
||||
self.bindings = KEYMAP.get(self.layer).iter().filter(|&c| c.contains(kw)).cloned().collect();
|
||||
} else {
|
||||
self.bindings = KEYMAP.get(self.layer).clone();
|
||||
}
|
||||
|
||||
self.keyword = kw.map(|s| s.to_owned());
|
||||
self.arrow(0);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn type_(&mut self, key: &Key) -> bool {
|
||||
let Some(input) = &mut self.in_filter else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if key.is_enter() {
|
||||
self.in_filter = None;
|
||||
return true;
|
||||
}
|
||||
|
||||
if input.type_(key) {
|
||||
return self.filter_apply();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Help {
|
||||
// --- Visible
|
||||
#[inline]
|
||||
pub fn visible(&self) -> bool { self.visible }
|
||||
|
||||
// --- Layer
|
||||
#[inline]
|
||||
pub fn layer(&self) -> KeymapLayer { self.layer }
|
||||
|
||||
// --- Keyword
|
||||
#[inline]
|
||||
pub fn keyword(&self) -> Option<String> {
|
||||
self
|
||||
.in_filter
|
||||
.as_ref()
|
||||
.map(|i| i.value())
|
||||
.or(self.keyword.as_deref())
|
||||
.map(|s| format!("/{}", s))
|
||||
}
|
||||
|
||||
// --- Bindings
|
||||
#[inline]
|
||||
pub fn window(&self) -> &[Control] {
|
||||
let end = (self.offset + Self::limit()).min(self.bindings.len());
|
||||
&self.bindings[self.offset..end]
|
||||
}
|
||||
|
||||
pub fn filter(&mut self) -> bool { todo!() }
|
||||
}
|
||||
|
||||
impl Help {
|
||||
// --- Cursor
|
||||
#[inline]
|
||||
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::size().rows));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rel_cursor(&self) -> usize { self.cursor - self.offset }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use config::keymap::Key;
|
||||
use crossterm::event::KeyCode;
|
||||
use shared::CharKind;
|
||||
use tokio::sync::oneshot::Sender;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
|
@ -177,8 +179,23 @@ impl Input {
|
|||
self.move_(snap.len() as isize)
|
||||
}
|
||||
|
||||
pub fn type_(&mut self, key: &Key) -> bool {
|
||||
if self.mode() != InputMode::Insert {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(c) = key.plain() {
|
||||
return self.type_char(c);
|
||||
}
|
||||
|
||||
match key {
|
||||
Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => self.backspace(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_(&mut self, c: char) -> bool {
|
||||
pub fn type_char(&mut self, c: char) -> bool {
|
||||
let mut bits = [0; 4];
|
||||
self.type_str(c.encode_utf8(&mut bits))
|
||||
}
|
||||
|
|
@ -253,7 +270,7 @@ impl Input {
|
|||
|
||||
self.insert(!before);
|
||||
for c in s.to_string_lossy().chars() {
|
||||
self.type_(c);
|
||||
self.type_char(c);
|
||||
}
|
||||
self.escape();
|
||||
true
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord"],"language":"en","version":"0.2"}
|
||||
{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","readline"],"version":"0.2"}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue