refactor: tab-specific config

This commit is contained in:
sxyazi 2023-10-20 09:10:07 +08:00
parent 9d4ba510df
commit bed8172347
No known key found for this signature in database
21 changed files with 177 additions and 104 deletions

View file

@ -89,13 +89,13 @@ impl App {
fn dispatch_resize(&mut self) { fn dispatch_resize(&mut self) {
self.cx.manager.current_mut().set_page(true); self.cx.manager.current_mut().set_page(true);
self.cx.manager.active_mut().preview_reset(); self.cx.manager.active_mut().preview.reset(|_| true);
self.cx.manager.peek(true, self.cx.image_layer()); self.cx.manager.peek(true, self.cx.image_layer());
emit!(Render); emit!(Render);
} }
fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) { fn dispatch_stop(&mut self, state: bool, tx: Option<oneshot::Sender<()>>) {
self.cx.manager.active_mut().preview_reset_image(); self.cx.manager.active_mut().preview.reset(|l| l.is_image());
if state { if state {
self.signals.stop_term(true); self.signals.stop_term(true);
self.term = None; self.term = None;

View file

@ -1,6 +1,6 @@
use core::{emit, files::FilesSorter, input::InputMode, tab::FinderCase, Ctx}; use core::{emit, input::InputMode, tab::FinderCase, Ctx};
use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP}; use config::{keymap::{Control, Exec, Key, KeymapLayer}, KEYMAP};
use shared::{optional_bool, Url}; use shared::{optional_bool, Url};
pub(super) struct Executor; pub(super) struct Executor;
@ -126,13 +126,7 @@ impl Executor {
exec.named.contains_key("block"), exec.named.contains_key("block"),
exec.named.contains_key("confirm"), exec.named.contains_key("confirm"),
), ),
"hidden" => { "hidden" => cx.manager.active_mut().hidden(exec),
cx.manager.active_mut().set_show_hidden(match exec.args.get(0).map(|s| s.as_str()) {
Some("show") => Some(true),
Some("hide") => Some(false),
_ => None,
})
}
"search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") { "search" => match exec.args.get(0).map(|s| s.as_str()).unwrap_or("") {
"rg" => cx.manager.active_mut().search(true), "rg" => cx.manager.active_mut().search(true),
"fd" => cx.manager.active_mut().search(false), "fd" => cx.manager.active_mut().search(false),
@ -160,13 +154,7 @@ impl Executor {
// Sorting // Sorting
"sort" => { "sort" => {
let b = cx.manager.active_mut().set_sorter(FilesSorter { let b = cx.manager.active_mut().sort(exec);
by: SortBy::try_from(exec.args.get(0).cloned().unwrap_or_default())
.unwrap_or_default(),
sensitive: exec.named.contains_key("sensitive"),
reverse: exec.named.contains_key("reverse"),
dir_first: exec.named.contains_key("dir_first"),
});
cx.tasks.precache_size(&cx.manager.current().files); cx.tasks.precache_size(&cx.manager.current().files);
b b
} }

View file

@ -4,6 +4,7 @@ sort_by = "modified"
sort_sensitive = true sort_sensitive = true
sort_reverse = true sort_reverse = true
sort_dir_first = true sort_dir_first = true
linemode = "none"
show_hidden = false show_hidden = false
show_symlink = true show_symlink = true

View file

@ -0,0 +1,26 @@
use anyhow::bail;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(try_from = "String")]
pub enum Linemode {
#[default]
None,
Size,
Mtime,
Permissions,
}
impl TryFrom<String> for Linemode {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> {
Ok(match s.as_str() {
"none" => Self::None,
"size" => Self::Size,
"mtime" => Self::Mtime,
"permissions" => Self::Permissions,
_ => bail!("invalid linemode value: {s}"),
})
}
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{ManagerLayout, SortBy}; use super::{Linemode, ManagerLayout, SortBy};
use crate::MERGED_YAZI; use crate::MERGED_YAZI;
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
@ -14,6 +14,7 @@ pub struct Manager {
pub sort_dir_first: bool, pub sort_dir_first: bool,
// Display // Display
pub linemode: Linemode,
pub show_hidden: bool, pub show_hidden: bool,
pub show_symlink: bool, pub show_symlink: bool,
} }

View file

@ -1,7 +1,9 @@
mod layout; mod layout;
mod linemode;
mod manager; mod manager;
mod sorting; mod sorting;
pub use layout::*; pub use layout::*;
pub use linemode::*;
pub use manager::*; pub use manager::*;
pub use sorting::*; pub use sorting::*;

View file

@ -1,3 +1,5 @@
use std::str::FromStr;
use anyhow::bail; use anyhow::bail;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -5,6 +7,7 @@ use serde::{Deserialize, Serialize};
#[serde(try_from = "String")] #[serde(try_from = "String")]
pub enum SortBy { pub enum SortBy {
#[default] #[default]
None,
Alphabetical, Alphabetical,
Created, Created,
Modified, Modified,
@ -12,11 +15,12 @@ pub enum SortBy {
Size, Size,
} }
impl TryFrom<String> for SortBy { impl FromStr for SortBy {
type Error = anyhow::Error; type Err = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.as_str() { Ok(match s {
"none" => Self::None,
"alphabetical" => Self::Alphabetical, "alphabetical" => Self::Alphabetical,
"created" => Self::Created, "created" => Self::Created,
"modified" => Self::Modified, "modified" => Self::Modified,
@ -26,3 +30,9 @@ impl TryFrom<String> for SortBy {
}) })
} }
} }
impl TryFrom<String> for SortBy {
type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
}

View file

@ -18,7 +18,7 @@ impl FromStr for Color {
impl TryFrom<String> for Color { impl TryFrom<String> for Color {
type Error = anyhow::Error; type Error = anyhow::Error;
fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(s.as_str()) } fn try_from(s: String) -> Result<Self, Self::Error> { Self::from_str(&s) }
} }
impl From<Color> for ratatui::style::Color { impl From<Color> for ratatui::style::Color {

View file

@ -1,11 +1,11 @@
use std::{cmp::Ordering, collections::BTreeMap, mem}; use std::{cmp::Ordering, collections::BTreeMap, mem};
use config::{manager::SortBy, MANAGER}; use config::manager::SortBy;
use shared::{natsort, Url}; use shared::{natsort, Url};
use super::File; use super::File;
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, Default, PartialEq)]
pub struct FilesSorter { pub struct FilesSorter {
pub by: SortBy, pub by: SortBy,
pub sensitive: bool, pub sensitive: bool,
@ -13,17 +13,6 @@ pub struct FilesSorter {
pub dir_first: bool, pub dir_first: bool,
} }
impl Default for FilesSorter {
fn default() -> Self {
Self {
by: MANAGER.sort_by,
sensitive: MANAGER.sort_sensitive,
reverse: MANAGER.sort_reverse,
dir_first: MANAGER.sort_dir_first,
}
}
}
impl FilesSorter { impl FilesSorter {
pub(super) fn sort(&self, items: &mut Vec<File>, sizes: &BTreeMap<Url, u64>) -> bool { pub(super) fn sort(&self, items: &mut Vec<File>, sizes: &BTreeMap<Url, u64>) -> bool {
if items.is_empty() { if items.is_empty() {
@ -31,6 +20,7 @@ impl FilesSorter {
} }
match self.by { match self.by {
SortBy::None => return false,
SortBy::Alphabetical => items.sort_unstable_by(|a, b| { SortBy::Alphabetical => items.sort_unstable_by(|a, b| {
if self.sensitive { if self.sensitive {
return self.cmp(&*a.url, &*b.url, self.promote(a, b)); return self.cmp(&*a.url, &*b.url, self.promote(a, b));
@ -98,7 +88,7 @@ impl FilesSorter {
*items = new; *items = new;
} }
#[inline] #[inline(always)]
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]
fn cmp<T: Ord>(&self, a: T, b: T, promote: Ordering) -> Ordering { fn cmp<T: Ord>(&self, a: T, b: T, promote: Ordering) -> Ordering {
if promote != Ordering::Equal { if promote != Ordering::Equal {
@ -108,7 +98,7 @@ impl FilesSorter {
} }
} }
#[inline] #[inline(always)]
fn promote(&self, a: &File, b: &File) -> Ordering { fn promote(&self, a: &File, b: &File) -> Ordering {
if self.dir_first { b.is_dir().cmp(&a.is_dir()) } else { Ordering::Equal } if self.dir_first { b.is_dir().cmp(&a.is_dir()) } else { Ordering::Equal }
} }

View file

@ -3,12 +3,12 @@ use crate::manager::Manager;
impl Manager { impl Manager {
pub fn peek(&mut self, sequent: bool, show_image: bool) -> bool { pub fn peek(&mut self, sequent: bool, show_image: bool) -> bool {
let Some(hovered) = self.hovered().cloned() else { let Some(hovered) = self.hovered().cloned() else {
return self.active_mut().preview_reset(); return self.active_mut().preview.reset(|_| true);
}; };
let url = &hovered.url; let url = &hovered.url;
if !show_image { if !show_image {
self.active_mut().preview_reset_image(); self.active_mut().preview.reset(|l| l.is_image());
} }
if hovered.is_dir() { if hovered.is_dir() {

View file

@ -23,8 +23,8 @@ impl Tabs {
} }
let mut tab = Tab::from(url); let mut tab = Tab::from(url);
tab.set_show_hidden(Some(self.active().show_hidden)); tab.conf = self.active().conf;
tab.set_sorter(self.active().sorter); tab.apply_files_attrs(false);
self.items.insert(self.idx + 1, tab); self.items.insert(self.idx + 1, tab);
self.set_idx(self.idx + 1); self.set_idx(self.idx + 1);
@ -83,7 +83,7 @@ impl Tabs {
#[inline] #[inline]
fn set_idx(&mut self, idx: usize) { fn set_idx(&mut self, idx: usize) {
self.idx = idx; self.idx = idx;
self.active_mut().preview_reset_image(); self.active_mut().preview.reset(|l| l.is_image());
emit!(Refresh); emit!(Refresh);
} }
} }

View file

@ -38,8 +38,8 @@ impl Tab {
fn escape_search(&mut self) -> bool { self.search_stop() } fn escape_search(&mut self) -> bool { self.search_stop() }
pub fn escape(&mut self, opt: impl Into<Opt>) -> bool { pub fn escape(&mut self, opt: impl Into<Opt>) -> bool {
let opt = opt.into(); let opt = opt.into().0;
if opt.0 == 0 { if opt == 0 {
return self.escape_find() return self.escape_find()
|| self.escape_visual() || self.escape_visual()
|| self.escape_select() || self.escape_select()
@ -47,16 +47,16 @@ impl Tab {
} }
let mut b = false; let mut b = false;
if opt.0 & 0b0001 != 0 { if opt & 0b0001 != 0 {
b |= self.escape_find(); b |= self.escape_find();
} }
if opt.0 & 0b0010 != 0 { if opt & 0b0010 != 0 {
b |= self.escape_visual(); b |= self.escape_visual();
} }
if opt.0 & 0b0100 != 0 { if opt & 0b0100 != 0 {
b |= self.escape_select(); b |= self.escape_select();
} }
if opt.0 & 0b1000 != 0 { if opt & 0b1000 != 0 {
b |= self.escape_search(); b |= self.escape_search();
} }
b b

View file

@ -0,0 +1,18 @@
use config::keymap::Exec;
use crate::{emit, tab::Tab};
impl Tab {
pub fn hidden(&mut self, e: &Exec) -> bool {
self.conf.show_hidden = match e.args.get(0).map(|s| s.as_bytes()) {
Some(b"show") => true,
Some(b"hide") => false,
_ => !self.conf.show_hidden,
};
if self.apply_files_attrs(false) {
emit!(Peek);
return true;
}
false
}
}

View file

@ -5,9 +5,11 @@ mod copy;
mod enter; mod enter;
mod escape; mod escape;
mod find; mod find;
mod hidden;
mod jump; mod jump;
mod leave; mod leave;
mod search; mod search;
mod select; mod select;
mod shell; mod shell;
mod sort;
mod visual_mode; mod visual_mode;

View file

@ -13,7 +13,7 @@ impl Tab {
} }
let mut cwd = self.current.cwd.clone(); let mut cwd = self.current.cwd.clone();
let hidden = self.show_hidden; let hidden = self.conf.show_hidden;
self.search = Some(tokio::spawn(async move { self.search = Some(tokio::spawn(async move {
let Some(Ok(subject)) = emit!(Input(InputOpt::top("Search:"))).recv().await else { let Some(Ok(subject)) = emit!(Input(InputOpt::top("Search:"))).recv().await else {
@ -49,7 +49,7 @@ impl Tab {
handle.abort(); handle.abort();
} }
if self.current.cwd.is_search() { if self.current.cwd.is_search() {
self.preview_reset_image(); self.preview.reset(|l| l.is_image());
let rep = self.history_new(&self.current.cwd.to_regular()); let rep = self.history_new(&self.current.cwd.to_regular());
drop(mem::replace(&mut self.current, rep)); drop(mem::replace(&mut self.current, rep));

View file

@ -0,0 +1,18 @@
use std::str::FromStr;
use config::{keymap::Exec, manager::SortBy};
use crate::tab::Tab;
impl Tab {
pub fn sort(&mut self, e: &Exec) -> bool {
if let Some(by) = e.args.get(0) {
self.conf.sort_by = SortBy::from_str(by).unwrap_or_default();
}
self.conf.sort_sensitive = e.named.contains_key("sensitive");
self.conf.sort_reverse = e.named.contains_key("reverse");
self.conf.sort_dir_first = e.named.contains_key("dir_first");
self.apply_files_attrs(false)
}
}

50
core/src/tab/config.rs Normal file
View file

@ -0,0 +1,50 @@
use config::{manager::{Linemode, SortBy}, MANAGER};
use crate::files::FilesSorter;
#[derive(Clone, Copy, PartialEq)]
pub struct Config {
// Sorting
pub sort_by: SortBy,
pub sort_sensitive: bool,
pub sort_reverse: bool,
pub sort_dir_first: bool,
// Display
pub linemode: Linemode,
pub show_hidden: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
// Sorting
sort_by: MANAGER.sort_by,
sort_sensitive: MANAGER.sort_sensitive,
sort_reverse: MANAGER.sort_reverse,
sort_dir_first: MANAGER.sort_dir_first,
// Display
linemode: MANAGER.linemode,
show_hidden: MANAGER.show_hidden,
}
}
}
impl Config {
pub(super) fn patch<F: FnOnce(&mut Self)>(&mut self, f: F) -> bool {
let old = *self;
f(self);
*self != old
}
#[inline]
pub(super) fn sorter(&self) -> FilesSorter {
FilesSorter {
by: self.sort_by,
sensitive: self.sort_sensitive,
reverse: self.sort_reverse,
dir_first: self.sort_dir_first,
}
}
}

View file

@ -1,11 +1,13 @@
mod backstack; mod backstack;
mod commands; mod commands;
mod config;
mod finder; mod finder;
mod folder; mod folder;
mod mode; mod mode;
mod tab; mod tab;
pub use backstack::*; pub use backstack::*;
pub use config::*;
pub use finder::*; pub use finder::*;
pub use folder::*; pub use folder::*;
pub use mode::*; pub use mode::*;

View file

@ -1,26 +1,24 @@
use std::{borrow::Cow, collections::BTreeMap}; use std::{borrow::Cow, collections::BTreeMap};
use anyhow::Result; use anyhow::Result;
use config::MANAGER;
use shared::Url; use shared::Url;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use super::{Backstack, Finder, Folder, Mode}; use super::{Backstack, Config, Finder, Folder, Mode};
use crate::{emit, files::{File, FilesSorter}, preview::{Preview, PreviewLock}}; use crate::{files::File, preview::{Preview, PreviewLock}};
pub struct Tab { pub struct Tab {
pub mode: Mode, pub mode: Mode,
pub conf: Config,
pub current: Folder, pub current: Folder,
pub parent: Option<Folder>, pub parent: Option<Folder>,
pub backstack: Backstack<Url>, pub backstack: Backstack<Url>,
pub history: BTreeMap<Url, Folder>, pub history: BTreeMap<Url, Folder>,
pub preview: Preview,
pub preview: Preview,
pub finder: Option<Finder>, pub finder: Option<Finder>,
pub(super) search: Option<JoinHandle<Result<()>>>, pub(super) search: Option<JoinHandle<Result<()>>>,
pub sorter: FilesSorter,
pub show_hidden: bool,
} }
impl From<Url> for Tab { impl From<Url> for Tab {
@ -34,12 +32,12 @@ impl From<Url> for Tab {
backstack: Backstack::new(url), backstack: Backstack::new(url),
history: Default::default(), history: Default::default(),
preview: Default::default(),
preview: Default::default(),
finder: None, finder: None,
search: None, search: None,
sorter: Default::default(),
show_hidden: MANAGER.show_hidden, conf: Default::default(),
} }
} }
} }
@ -63,7 +61,7 @@ impl Tab {
pub fn update_preview(&mut self, lock: PreviewLock) -> bool { pub fn update_preview(&mut self, lock: PreviewLock) -> bool {
let Some(hovered) = self.current.hovered().map(|h| &h.url) else { let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
return self.preview_reset(); return self.preview.reset(|_| true);
}; };
if lock.url != *hovered { if lock.url != *hovered {
@ -101,45 +99,13 @@ impl Tab {
self.history.remove(url).unwrap_or_else(|| Folder::from(url)) self.history.remove(url).unwrap_or_else(|| Folder::from(url))
} }
// --- Preview
#[inline]
pub fn preview_reset(&mut self) -> bool { self.preview.reset(|_| true) }
#[inline]
pub fn preview_reset_image(&mut self) -> bool { self.preview.reset(|l| l.is_image()) }
// --- Sorter
pub fn set_sorter(&mut self, sorter: FilesSorter) -> bool {
if sorter == self.sorter {
return false;
}
self.sorter = sorter;
self.apply_files_attrs(false)
}
// --- Show hidden
pub fn set_show_hidden(&mut self, state: Option<bool>) -> bool {
let state = state.unwrap_or(!self.show_hidden);
if state == self.show_hidden {
return false;
}
self.show_hidden = state;
if self.apply_files_attrs(false) {
emit!(Peek);
return true;
}
false
}
pub fn apply_files_attrs(&mut self, just_preview: bool) -> bool { pub fn apply_files_attrs(&mut self, just_preview: bool) -> bool {
let mut b = false; let mut b = false;
if let Some(f) = if let Some(f) =
self.current.hovered().filter(|h| h.is_dir()).and_then(|h| self.history.get_mut(&h.url)) self.current.hovered().filter(|h| h.is_dir()).and_then(|h| self.history.get_mut(&h.url))
{ {
b |= f.files.set_show_hidden(self.show_hidden); b |= f.files.set_show_hidden(self.conf.show_hidden);
b |= f.files.set_sorter(self.sorter); b |= f.files.set_sorter(self.conf.sorter());
} }
if just_preview { if just_preview {
@ -147,12 +113,12 @@ impl Tab {
} }
let hovered = self.current.hovered().map(|h| h.url()); let hovered = self.current.hovered().map(|h| h.url());
b |= self.current.files.set_show_hidden(self.show_hidden); b |= self.current.files.set_show_hidden(self.conf.show_hidden);
b |= self.current.files.set_sorter(self.sorter); b |= self.current.files.set_sorter(self.conf.sorter());
if let Some(parent) = self.parent.as_mut() { if let Some(parent) = self.parent.as_mut() {
b |= parent.files.set_show_hidden(self.show_hidden); b |= parent.files.set_show_hidden(self.conf.show_hidden);
b |= parent.files.set_sorter(self.sorter); b |= parent.files.set_sorter(self.conf.sorter());
} }
self.current.repos(hovered); self.current.repos(hovered);

View file

@ -1 +1 @@
{"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","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan"]} {"language":"en","flagWords":[],"version":"0.2","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","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","linemode"]}

View file

@ -21,9 +21,8 @@ impl Base {
} }
pub fn render(self, cx: &core::Ctx, buf: &mut ratatui::buffer::Buffer) { pub fn render(self, cx: &core::Ctx, buf: &mut ratatui::buffer::Buffer) {
match self.kind { if self.kind == 0 {
0 => super::Preview::new(cx).render(self.area, buf), super::Preview::new(cx).render(self.area, buf)
_ => {}
} }
} }
} }