mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Create mount manager
This commit is contained in:
parent
4194befb88
commit
854f92446a
16 changed files with 279 additions and 11 deletions
|
|
@ -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 = [
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
37
yazi-core/src/mount/commands/arrow.rs
Normal file
37
yazi-core/src/mount/commands/arrow.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
1
yazi-core/src/mount/commands/mod.rs
Normal file
1
yazi-core/src/mount/commands/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
yazi_macro::mod_flat!(arrow toggle mountpoint_cd);
|
||||
9
yazi-core/src/mount/commands/mountpoint_cd.rs
Normal file
9
yazi-core/src/mount/commands/mountpoint_cd.rs
Normal 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() {}
|
||||
}
|
||||
}
|
||||
26
yazi-core/src/mount/commands/toggle.rs
Normal file
26
yazi-core/src/mount/commands/toggle.rs
Normal 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!();
|
||||
}
|
||||
}
|
||||
7
yazi-core/src/mount/mod.rs
Normal file
7
yazi-core/src/mount/mod.rs
Normal 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;
|
||||
53
yazi-core/src/mount/mount.rs
Normal file
53
yazi-core/src/mount/mount.rs
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,20 +12,22 @@ pub struct Ctx {
|
|||
pub completion: Completion,
|
||||
pub which: Which,
|
||||
pub notify: Notify,
|
||||
pub mount: Mount,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
pub fn make() -> Self {
|
||||
Self {
|
||||
manager: Manager::make(),
|
||||
tasks: Tasks::serve(),
|
||||
pick: Default::default(),
|
||||
input: Default::default(),
|
||||
confirm: Default::default(),
|
||||
help: Default::default(),
|
||||
manager: Manager::make(),
|
||||
tasks: Tasks::serve(),
|
||||
pick: Default::default(),
|
||||
input: Default::default(),
|
||||
confirm: Default::default(),
|
||||
help: Default::default(),
|
||||
completion: Default::default(),
|
||||
which: Default::default(),
|
||||
notify: Default::default(),
|
||||
which: Default::default(),
|
||||
notify: Default::default(),
|
||||
mount: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
yazi_macro::mod_flat!(progress tasks);
|
||||
yazi_macro::mod_flat!(progress tasks mount);
|
||||
|
|
|
|||
67
yazi-fm/src/tasks/mount.rs
Normal file
67
yazi-fm/src/tasks/mount.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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}"),
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue