Create mount manager

This commit is contained in:
Lutsay Aleksandr Valeryevich 2024-12-01 01:43:54 +03:00
parent 4194befb88
commit 854f92446a
16 changed files with 279 additions and 11 deletions

View file

@ -151,6 +151,9 @@ keymap = [
# Tasks
{ on = "w", run = "tasks_show", desc = "Show task manager" },
# Mount manager
{ on = "M", run = "mount_show", desc = "Show mount manager" },
# Help
{ on = "~", run = "help", desc = "Open help" },
{ on = "<F1>", run = "help", desc = "Open help" },
@ -178,6 +181,25 @@ keymap = [
{ on = "<F1>", run = "help", desc = "Open help" },
]
[mount]
keymap = [
{ on = "<Esc>", run = "close", desc = "Close mount manager" },
{ on = "<C-[>", run = "close", desc = "Close mount manager" },
{ on = "<C-c>", run = "close", desc = "Close mount manager" },
{ on = "M", run = "close", desc = "Close mount manager" },
{ on = "k", run = "arrow -1", desc = "Move cursor up" },
{ on = "j", run = "arrow 1", desc = "Move cursor down" },
{ on = "<Up>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<Down>", run = "arrow 1", desc = "Move cursor down" },
# Help
{ on = "~", run = "help", desc = "Open help" },
{ on = "<F1>", run = "help", desc = "Open help" },
]
[spot]
keymap = [

View file

@ -18,6 +18,7 @@ pub struct Keymap {
pub confirm: Vec<Chord>,
pub help: Vec<Chord>,
pub completion: Vec<Chord>,
pub mount: Vec<Chord>,
}
impl Keymap {
@ -34,6 +35,7 @@ impl Keymap {
Layer::Help => &self.help,
Layer::Completion => &self.completion,
Layer::Which => unreachable!(),
Layer::Mount => &self.mount,
}
}
}
@ -61,6 +63,7 @@ impl<'de> Deserialize<'de> for Keymap {
confirm: Inner,
help: Inner,
completion: Inner,
mount: Inner,
}
#[derive(Deserialize)]
struct Inner {
@ -104,6 +107,8 @@ impl<'de> Deserialize<'de> for Keymap {
help: mix(shadow.help.prepend_keymap, shadow.help.keymap, shadow.help.append_keymap),
#[rustfmt::skip]
completion: mix(shadow.completion.prepend_keymap, shadow.completion.keymap, shadow.completion.append_keymap),
#[rustfmt::skip]
mount: mix(shadow.mount.prepend_keymap, shadow.mount.keymap, shadow.mount.append_keymap),
})
}
}

View file

@ -6,7 +6,7 @@
clippy::unit_arg
)]
yazi_macro::mod_pub!(completion confirm help input manager notify pick spot tab tasks which);
yazi_macro::mod_pub!(completion confirm help input manager notify pick spot tab tasks which mount);
pub fn init() {
manager::WATCHED.with(<_>::default);

View file

@ -0,0 +1,37 @@
use yazi_macro::render;
use yazi_shared::event::{CmdCow, Data};
use crate::mount::Mount;
struct Opt {
step: isize,
}
impl From<CmdCow> for Opt {
fn from(c: CmdCow) -> Self {
Self { step: c.first().and_then(Data::as_isize).unwrap_or(0) }
}
}
impl From<isize> for Opt {
fn from(step: isize) -> Self {
Self { step }
}
}
impl Mount {
#[yazi_codegen::command]
pub fn arrow(&mut self, opt: Opt) {
self.update();
let old = self.cursor;
if opt.step > 0 {
self.cursor += 1;
} else {
self.cursor = self.cursor.saturating_sub(1);
}
let max = Self::limit().min(self.points.len());
self.cursor = self.cursor.min(max.saturating_sub(1));
render!(self.cursor != old);
}
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(arrow toggle mountpoint_cd);

View file

@ -0,0 +1,9 @@
use yazi_proxy::options::ProcessExecOpt;
use crate::mount::Mount;
impl Mount {
pub fn mountpoint_cd(&mut self, opt: impl TryInto<ProcessExecOpt>) {
if let Ok(opt) = opt.try_into() {}
}
}

View file

@ -0,0 +1,26 @@
use yazi_macro::render;
use yazi_shared::event::CmdCow;
use crate::mount::Mount;
struct Opt;
impl From<CmdCow> for Opt {
fn from(_: CmdCow) -> Self { Self }
}
impl From<()> for Opt {
fn from(_: ()) -> Self { Self }
}
impl Mount {
#[yazi_codegen::command]
pub fn toggle(&mut self, _: Opt) {
self.visible = !self.visible;
if self.visible {
self.arrow(0);
}
render!();
}
}

View file

@ -0,0 +1,7 @@
yazi_macro::mod_pub!(commands);
yazi_macro::mod_flat!(mount);
pub const MOUNT_BORDER: u16 = 2;
pub const MOUNT_PADDING: u16 = 2;
pub const MOUNT_PERCENT: u16 = 80;

View file

@ -0,0 +1,53 @@
use std::{io::BufRead, path::PathBuf, sync::Arc, time::Duration};
use parking_lot::Mutex;
use yazi_adapter::Dimension;
use yazi_scheduler::{Ongoing, TaskSummary};
use super::{MOUNT_BORDER, MOUNT_PADDING, MOUNT_PERCENT};
#[derive(Debug)]
pub struct MountPoint {
pub dev: String,
pub path: PathBuf,
pub fs: String,
pub opts: String,
}
#[derive(Default)]
pub struct Mount {
pub visible: bool,
pub cursor: usize,
pub points: Vec<MountPoint>,
}
impl Mount {
pub fn update(&mut self) {
let points =
std::io::BufReader::new(std::fs::File::open(PathBuf::from("/proc/mounts")).unwrap())
.lines()
.map_while(Result::ok)
.filter_map(|l| {
let mut parts = l.trim_end_matches(" 0 0").split(' ');
Some(MountPoint {
dev: parts.next()?.into(),
path: parts.next()?.into(),
fs: parts.next()?.into(),
opts: parts.next()?.into(),
})
})
.filter(|p| !p.path.starts_with("/sys"))
.filter(|p| !p.path.starts_with("/tmp"))
.filter(|p| !p.path.starts_with("/run"))
.filter(|p| !p.path.starts_with("/dev"))
.filter(|p| !p.path.starts_with("/proc"));
self.points = points.collect();
}
#[inline]
pub fn limit() -> usize {
(Dimension::available().rows * MOUNT_PERCENT / 100).saturating_sub(MOUNT_BORDER + MOUNT_PADDING)
as usize
}
}

View file

@ -1,5 +1,5 @@
use ratatui::layout::Rect;
use yazi_core::{completion::Completion, confirm::Confirm, help::Help, input::Input, manager::Manager, notify::Notify, pick::Pick, tab::Tab, tasks::Tasks, which::Which};
use yazi_core::{completion::Completion, confirm::Confirm, help::Help, input::Input, manager::Manager, notify::Notify, pick::Pick, tab::Tab, tasks::Tasks, which::Which, mount::Mount};
use yazi_fs::Folder;
pub struct Ctx {
@ -12,6 +12,7 @@ pub struct Ctx {
pub completion: Completion,
pub which: Which,
pub notify: Notify,
pub mount: Mount,
}
impl Ctx {
@ -26,6 +27,7 @@ impl Ctx {
completion: Default::default(),
which: Default::default(),
notify: Default::default(),
mount: Default::default(),
}
}

View file

@ -24,6 +24,7 @@ impl<'a> Executor<'a> {
Layer::Help => self.help(cmd),
Layer::Completion => self.completion(cmd),
Layer::Which => self.which(cmd),
Layer::Mount => self.mount(cmd),
}
}
@ -145,6 +146,8 @@ impl<'a> Executor<'a> {
match cmd.name.as_str() {
// Tasks
"tasks_show" => self.app.cx.tasks.toggle(()),
// Mount
"mount_show" => self.app.cx.mount.toggle(()),
// Help
"help" => self.app.cx.help.toggle(Layer::Manager),
// Plugin
@ -355,4 +358,31 @@ impl<'a> Executor<'a> {
on!(show);
on!(callback);
}
fn mount(&mut self, cmd: CmdCow) {
macro_rules! on {
($name:ident) => {
if cmd.name == stringify!($name) {
return self.app.cx.mount.$name(cmd);
}
};
($name:ident, $alias:literal) => {
if cmd.name == $alias {
return self.app.cx.mount.$name(cmd);
}
};
}
on!(toggle, "close");
on!(arrow);
on!(mountpoint_cd);
match cmd.name.as_str() {
// Help
"help" => self.app.cx.help.toggle(Layer::Mount),
// Plugin
"plugin" => self.app.plugin(cmd),
_ => {}
}
}
}

View file

@ -39,6 +39,10 @@ impl Widget for Root<'_> {
tasks::Tasks::new(self.cx).render(area, buf);
}
if self.cx.mount.visible {
tasks::Mount::new(self.cx).render(area, buf);
}
if self.cx.active().spot.visible() {
spot::Spot::new(self.cx).render(area, buf);
}

View file

@ -40,6 +40,8 @@ impl<'a> Router<'a> {
self.matches(Layer::Spot, key)
} else if cx.tasks.visible {
self.matches(Layer::Tasks, key)
} else if cx.mount.visible {
self.matches(Layer::Mount, key)
} else {
self.matches(Layer::Manager, key)
}

View file

@ -1 +1 @@
yazi_macro::mod_flat!(progress tasks);
yazi_macro::mod_flat!(progress tasks mount);

View file

@ -0,0 +1,67 @@
use ratatui::{
buffer::Buffer,
layout::{self, Alignment, Constraint, Rect},
text::Line,
widgets::{Block, BorderType, List, ListItem, Padding, Widget},
};
use yazi_config::THEME;
use yazi_core::tasks::TASKS_PERCENT;
use crate::Ctx;
pub(crate) struct Mount<'a> {
cx: &'a Ctx,
}
impl<'a> Mount<'a> {
pub(crate) fn new(cx: &'a Ctx) -> Self {
Self { cx }
}
pub(super) fn area(area: Rect) -> Rect {
let chunk = layout::Layout::vertical([
Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage(TASKS_PERCENT),
Constraint::Percentage((100 - TASKS_PERCENT) / 2),
])
.split(area)[1];
layout::Layout::horizontal([
Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage(TASKS_PERCENT),
Constraint::Percentage((100 - TASKS_PERCENT) / 2),
])
.split(chunk)[1]
}
}
impl Widget for Mount<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let area = Self::area(area);
yazi_plugin::elements::Clear::default().render(area, buf);
let block = Block::bordered()
.title(Line::styled("Mount", THEME.tasks.title))
.title_alignment(Alignment::Center)
.padding(Padding::symmetric(1, 1))
.border_type(BorderType::Rounded)
.border_style(THEME.tasks.border);
block.clone().render(area, buf);
let mnt = &self.cx.mount;
let items = mnt
.points
.iter()
.enumerate()
.map(|(i, p)| {
let mut item = ListItem::new(format!("{} {}", p.dev, p.path.to_string_lossy()));
if i == mnt.cursor {
item = item.style(THEME.tasks.hovered);
}
item
})
.collect::<Vec<_>>();
List::new(items).render(block.inner(area), buf);
}
}

View file

@ -15,6 +15,7 @@ pub enum Layer {
Help,
Completion,
Which,
Mount,
}
impl Display for Layer {
@ -30,6 +31,7 @@ impl Display for Layer {
Self::Help => "help",
Self::Completion => "completion",
Self::Which => "which",
Self::Mount => "mount",
})
}
}
@ -49,6 +51,7 @@ impl FromStr for Layer {
"help" => Self::Help,
"completion" => Self::Completion,
"which" => Self::Which,
"mount" => Self::Mount,
_ => bail!("invalid layer: {s}"),
})
}