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
|
# Tasks
|
||||||
{ on = "w", run = "tasks_show", desc = "Show task manager" },
|
{ on = "w", run = "tasks_show", desc = "Show task manager" },
|
||||||
|
|
||||||
|
# Mount manager
|
||||||
|
{ on = "M", run = "mount_show", desc = "Show mount manager" },
|
||||||
|
|
||||||
# Help
|
# Help
|
||||||
{ on = "~", run = "help", desc = "Open help" },
|
{ on = "~", run = "help", desc = "Open help" },
|
||||||
{ on = "<F1>", run = "help", desc = "Open help" },
|
{ on = "<F1>", run = "help", desc = "Open help" },
|
||||||
|
|
@ -178,6 +181,25 @@ keymap = [
|
||||||
{ on = "<F1>", run = "help", desc = "Open help" },
|
{ 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]
|
[spot]
|
||||||
|
|
||||||
keymap = [
|
keymap = [
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ pub struct Keymap {
|
||||||
pub confirm: Vec<Chord>,
|
pub confirm: Vec<Chord>,
|
||||||
pub help: Vec<Chord>,
|
pub help: Vec<Chord>,
|
||||||
pub completion: Vec<Chord>,
|
pub completion: Vec<Chord>,
|
||||||
|
pub mount: Vec<Chord>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keymap {
|
impl Keymap {
|
||||||
|
|
@ -34,6 +35,7 @@ impl Keymap {
|
||||||
Layer::Help => &self.help,
|
Layer::Help => &self.help,
|
||||||
Layer::Completion => &self.completion,
|
Layer::Completion => &self.completion,
|
||||||
Layer::Which => unreachable!(),
|
Layer::Which => unreachable!(),
|
||||||
|
Layer::Mount => &self.mount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +63,7 @@ impl<'de> Deserialize<'de> for Keymap {
|
||||||
confirm: Inner,
|
confirm: Inner,
|
||||||
help: Inner,
|
help: Inner,
|
||||||
completion: Inner,
|
completion: Inner,
|
||||||
|
mount: Inner,
|
||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Inner {
|
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),
|
help: mix(shadow.help.prepend_keymap, shadow.help.keymap, shadow.help.append_keymap),
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
completion: mix(shadow.completion.prepend_keymap, shadow.completion.keymap, shadow.completion.append_keymap),
|
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
|
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() {
|
pub fn init() {
|
||||||
manager::WATCHED.with(<_>::default);
|
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 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;
|
use yazi_fs::Folder;
|
||||||
|
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
|
|
@ -12,20 +12,22 @@ pub struct Ctx {
|
||||||
pub completion: Completion,
|
pub completion: Completion,
|
||||||
pub which: Which,
|
pub which: Which,
|
||||||
pub notify: Notify,
|
pub notify: Notify,
|
||||||
|
pub mount: Mount,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ctx {
|
impl Ctx {
|
||||||
pub fn make() -> Self {
|
pub fn make() -> Self {
|
||||||
Self {
|
Self {
|
||||||
manager: Manager::make(),
|
manager: Manager::make(),
|
||||||
tasks: Tasks::serve(),
|
tasks: Tasks::serve(),
|
||||||
pick: Default::default(),
|
pick: Default::default(),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
confirm: Default::default(),
|
confirm: Default::default(),
|
||||||
help: Default::default(),
|
help: Default::default(),
|
||||||
completion: Default::default(),
|
completion: Default::default(),
|
||||||
which: Default::default(),
|
which: Default::default(),
|
||||||
notify: Default::default(),
|
notify: Default::default(),
|
||||||
|
mount: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ impl<'a> Executor<'a> {
|
||||||
Layer::Help => self.help(cmd),
|
Layer::Help => self.help(cmd),
|
||||||
Layer::Completion => self.completion(cmd),
|
Layer::Completion => self.completion(cmd),
|
||||||
Layer::Which => self.which(cmd),
|
Layer::Which => self.which(cmd),
|
||||||
|
Layer::Mount => self.mount(cmd),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,6 +146,8 @@ impl<'a> Executor<'a> {
|
||||||
match cmd.name.as_str() {
|
match cmd.name.as_str() {
|
||||||
// Tasks
|
// Tasks
|
||||||
"tasks_show" => self.app.cx.tasks.toggle(()),
|
"tasks_show" => self.app.cx.tasks.toggle(()),
|
||||||
|
// Mount
|
||||||
|
"mount_show" => self.app.cx.mount.toggle(()),
|
||||||
// Help
|
// Help
|
||||||
"help" => self.app.cx.help.toggle(Layer::Manager),
|
"help" => self.app.cx.help.toggle(Layer::Manager),
|
||||||
// Plugin
|
// Plugin
|
||||||
|
|
@ -355,4 +358,31 @@ impl<'a> Executor<'a> {
|
||||||
on!(show);
|
on!(show);
|
||||||
on!(callback);
|
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);
|
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() {
|
if self.cx.active().spot.visible() {
|
||||||
spot::Spot::new(self.cx).render(area, buf);
|
spot::Spot::new(self.cx).render(area, buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ impl<'a> Router<'a> {
|
||||||
self.matches(Layer::Spot, key)
|
self.matches(Layer::Spot, key)
|
||||||
} else if cx.tasks.visible {
|
} else if cx.tasks.visible {
|
||||||
self.matches(Layer::Tasks, key)
|
self.matches(Layer::Tasks, key)
|
||||||
|
} else if cx.mount.visible {
|
||||||
|
self.matches(Layer::Mount, key)
|
||||||
} else {
|
} else {
|
||||||
self.matches(Layer::Manager, key)
|
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,
|
Help,
|
||||||
Completion,
|
Completion,
|
||||||
Which,
|
Which,
|
||||||
|
Mount,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Layer {
|
impl Display for Layer {
|
||||||
|
|
@ -30,6 +31,7 @@ impl Display for Layer {
|
||||||
Self::Help => "help",
|
Self::Help => "help",
|
||||||
Self::Completion => "completion",
|
Self::Completion => "completion",
|
||||||
Self::Which => "which",
|
Self::Which => "which",
|
||||||
|
Self::Mount => "mount",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +51,7 @@ impl FromStr for Layer {
|
||||||
"help" => Self::Help,
|
"help" => Self::Help,
|
||||||
"completion" => Self::Completion,
|
"completion" => Self::Completion,
|
||||||
"which" => Self::Which,
|
"which" => Self::Which,
|
||||||
|
"mount" => Self::Mount,
|
||||||
_ => bail!("invalid layer: {s}"),
|
_ => bail!("invalid layer: {s}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue