mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
2616419858
commit
2c2e1668fc
11 changed files with 177 additions and 78 deletions
|
|
@ -69,24 +69,6 @@ impl<'a> Widget for Folder<'a> {
|
|||
// );
|
||||
// }
|
||||
//
|
||||
// let hovered = matches!(self.folder.hovered, Some(ref h) if h.url() ==
|
||||
// f.url()); let style = if self.is_preview && hovered {
|
||||
// THEME.preview.hovered.get()
|
||||
// } else if hovered {
|
||||
// THEME.selection.hovered.get()
|
||||
// } else {
|
||||
// self.item_style(f)
|
||||
// };
|
||||
//
|
||||
// let mut spans = Vec::with_capacity(10);
|
||||
// spans.push(Span::raw(format!(" {} ", Self::icon(f))));
|
||||
// spans.extend(self.highlighted_item(f));
|
||||
//
|
||||
// if let Some(link_to) = f.link_to() {
|
||||
// if MANAGER.show_symlink {
|
||||
// spans.push(Span::raw(format!(" -> {}", link_to.display())));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if let Some(idx) = active
|
||||
// .finder()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use anyhow::bail;
|
||||
use crossterm::terminal::WindowSize;
|
||||
use ratatui::prelude::Rect;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared::Term;
|
||||
|
||||
use super::{FOLDER_MARGIN, PREVIEW_BORDER, PREVIEW_MARGIN};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(try_from = "Vec<u32>")]
|
||||
pub struct ManagerLayout {
|
||||
pub parent: u32,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{ManagerLayout, SortBy};
|
||||
use crate::MERGED_YAZI;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Manager {
|
||||
pub layout: ManagerLayout,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::bail;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(try_from = "String")]
|
||||
pub enum SortBy {
|
||||
#[default]
|
||||
|
|
|
|||
|
|
@ -1,25 +1,47 @@
|
|||
Folder = {}
|
||||
Folder = {
|
||||
Kind = {
|
||||
Parent = 0,
|
||||
Current = 1,
|
||||
Preview = 2,
|
||||
},
|
||||
}
|
||||
|
||||
function Folder:by_kind(kind)
|
||||
if kind == self.Kind.Parent then
|
||||
return cx.manager.parent
|
||||
elseif kind == self.Kind.Current then
|
||||
return cx.manager.current
|
||||
elseif kind == self.Kind.Preview then
|
||||
return cx.manager.preview.folder
|
||||
end
|
||||
end
|
||||
|
||||
function Folder:window(kind)
|
||||
local folder = self:by_kind(kind)
|
||||
if folder ~= nil then
|
||||
return folder.files:slice(folder.offset, MANAGER.layout.folder_height())
|
||||
end
|
||||
end
|
||||
|
||||
function Folder:hovered(kind)
|
||||
local folder = self:by_kind(kind)
|
||||
return folder and folder.hovered
|
||||
end
|
||||
|
||||
function Folder:parent(area)
|
||||
local parent = cx.manager.parent
|
||||
if parent == nil then
|
||||
return ui.Paragraph(area, ui.Line {})
|
||||
end
|
||||
|
||||
local hovered = nil
|
||||
if parent.hovered ~= nil then
|
||||
hovered = parent.hovered.url
|
||||
local window = self:window(self.Kind.Parent)
|
||||
if window == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
local hovered = (self:hovered(self.Kind.Parent) or {}).url
|
||||
local lines = {}
|
||||
for _, f in pairs(parent.files) do
|
||||
local line = ui.Line { ui.Span(" " .. f.icon .. " " .. f.name .. " ") }
|
||||
|
||||
-- TODO: preview hovered
|
||||
for _, f in pairs(window) do
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.selection.hovered)
|
||||
else
|
||||
line = line:style(f.style)
|
||||
line = line:style(f:style())
|
||||
end
|
||||
|
||||
lines[#lines + 1] = line
|
||||
|
|
@ -29,20 +51,25 @@ function Folder:parent(area)
|
|||
end
|
||||
|
||||
function Folder:current(area)
|
||||
local hovered = nil
|
||||
if cx.manager.current.hovered ~= nil then
|
||||
hovered = cx.manager.current.hovered.url
|
||||
local hovered = (self:hovered(self.Kind.Current) or {}).url
|
||||
local lines = {}
|
||||
for _, f in pairs(self:window(self.Kind.Current)) do
|
||||
local name = f.name
|
||||
|
||||
-- Show symlink target
|
||||
if MANAGER.show_symlink then
|
||||
local link_to = f.link_to
|
||||
if link_to ~= nil then
|
||||
name = name .. " -> " .. tostring(link_to)
|
||||
end
|
||||
end
|
||||
|
||||
local lines = {}
|
||||
for _, f in pairs(cx.manager.current.files) do
|
||||
local line = ui.Line { ui.Span(" " .. f.icon .. " " .. f.name .. " ") }
|
||||
|
||||
-- TODO: preview hovered
|
||||
-- Highlight hovered file
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.selection.hovered)
|
||||
else
|
||||
line = line:style(f.style)
|
||||
line = line:style(f:style())
|
||||
end
|
||||
|
||||
lines[#lines + 1] = line
|
||||
|
|
@ -52,25 +79,32 @@ function Folder:current(area)
|
|||
end
|
||||
|
||||
function Folder:preview(area)
|
||||
local target = cx.manager.preview.folder
|
||||
if target == nil then
|
||||
return ui.Paragraph(area, ui.Line {})
|
||||
local window = self:window(self.Kind.Preview)
|
||||
if window == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
local hovered = (self:hovered(self.Kind.Preview) or {}).url
|
||||
local lines = {}
|
||||
for _, f in pairs(target.files) do
|
||||
lines[#lines + 1] = ui.Line { ui.Span(f.name) }
|
||||
for _, f in pairs(window) do
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.preview.hovered)
|
||||
else
|
||||
line = line:style(f:style())
|
||||
end
|
||||
lines[#lines + 1] = line
|
||||
end
|
||||
|
||||
return { ui.Paragraph(area, lines) }
|
||||
end
|
||||
|
||||
function Folder:render(area, args)
|
||||
if args.kind == 0 then
|
||||
if args.kind == self.Kind.Parent then
|
||||
return self:parent(area)
|
||||
elseif args.kind == 1 then
|
||||
elseif args.kind == self.Kind.Current then
|
||||
return self:current(area)
|
||||
elseif args.kind == 2 then
|
||||
elseif args.kind == self.Kind.Preview then
|
||||
return self:preview(area)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
Status = {}
|
||||
|
||||
function Status.style()
|
||||
local mode = cx.manager.mode:upper()
|
||||
if mode == "SELECT" then
|
||||
if cx.manager.mode.is_select then
|
||||
return THEME.status.mode_select
|
||||
elseif mode == "UNSET" then
|
||||
elseif cx.manager.mode.is_unset then
|
||||
return THEME.status.mode_unset
|
||||
else
|
||||
return THEME.status.mode_normal
|
||||
|
|
@ -12,7 +11,7 @@ function Status.style()
|
|||
end
|
||||
|
||||
function Status:mode()
|
||||
local mode = cx.manager.mode:upper()
|
||||
local mode = tostring(cx.manager.mode):upper()
|
||||
if mode == "UNSET" then
|
||||
mode = "UN-SET"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use config::THEME;
|
||||
use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserDataFields, UserDataMethods};
|
||||
use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserDataFields, UserDataMethods, Value};
|
||||
|
||||
use super::Url;
|
||||
use crate::{layout::Style, LUA};
|
||||
|
|
@ -9,19 +9,28 @@ pub struct Manager;
|
|||
impl Manager {
|
||||
pub(crate) fn init() -> mlua::Result<()> {
|
||||
LUA.register_userdata_type::<core::manager::Manager>(|reg| {
|
||||
reg.add_field_method_get("mode", |_, me| Ok(me.active().mode().to_string()));
|
||||
reg.add_field_function_get("parent", |_, me| me.named_user_value::<AnyUserData>("parent"));
|
||||
reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode"));
|
||||
reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent"));
|
||||
reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current"));
|
||||
reg.add_field_function_get("preview", |_, me| me.named_user_value::<AnyUserData>("preview"));
|
||||
})?;
|
||||
|
||||
LUA.register_userdata_type::<core::manager::Mode>(|reg| {
|
||||
reg.add_field_method_get("is_select", |_, me| Ok(me.is_select()));
|
||||
reg.add_field_method_get("is_unset", |_, me| Ok(me.is_unset()));
|
||||
reg.add_field_method_get("is_visual", |_, me| Ok(me.is_visual()));
|
||||
reg.add_method("pending", |_, me, (idx, state): (usize, bool)| Ok(me.pending(idx, state)));
|
||||
|
||||
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string()));
|
||||
})?;
|
||||
|
||||
LUA.register_userdata_type::<core::manager::Folder>(|reg| {
|
||||
reg.add_field_method_get("cwd", |_, me| Ok(Url::from(&me.cwd)));
|
||||
reg.add_field_method_get("offset", |_, me| Ok(me.offset()));
|
||||
reg.add_field_method_get("cursor", |_, me| Ok(me.cursor()));
|
||||
|
||||
reg.add_field_function_get("files", |_, me| me.named_user_value::<AnyUserData>("files"));
|
||||
reg.add_field_function_get("hovered", |_, me| me.named_user_value::<AnyUserData>("hovered"));
|
||||
reg.add_field_function_get("hovered", |_, me| me.named_user_value::<Value>("hovered"));
|
||||
})?;
|
||||
|
||||
LUA.register_userdata_type::<core::files::Files>(|reg| {
|
||||
|
|
@ -41,24 +50,27 @@ impl Manager {
|
|||
})?;
|
||||
Ok((iter, me, 0))
|
||||
});
|
||||
|
||||
reg.add_function("slice", |_, (me, skip, take): (AnyUserData, usize, usize)| {
|
||||
let items = me.named_user_value::<Vec<AnyUserData>>("items")?;
|
||||
Ok(items.iter().skip(skip).take(take).cloned().collect::<Vec<_>>())
|
||||
});
|
||||
})?;
|
||||
|
||||
LUA.register_userdata_type::<core::files::File>(|reg| {
|
||||
reg.add_field_method_get("name", |_, me| {
|
||||
Ok(me.url().file_name().map(|n| n.to_string_lossy().to_string()))
|
||||
});
|
||||
reg.add_field_function_get("icon", |_, me| {
|
||||
reg.add_function("icon", |_, me: AnyUserData| {
|
||||
me.named_user_value::<Function>("icon")?.call::<_, String>(())
|
||||
});
|
||||
reg.add_field_function_get("style", |_, me| {
|
||||
reg.add_function("style", |_, me: AnyUserData| {
|
||||
me.named_user_value::<Function>("style")?.call::<_, Style>(())
|
||||
});
|
||||
|
||||
reg.add_field_method_get("url", |_, me| Ok(Url::from(me.url())));
|
||||
reg.add_field_method_get("length", |_, me| Ok(me.length()));
|
||||
reg.add_field_method_get("link_to", |_, me| {
|
||||
Ok(me.link_to().map(|l| l.to_string_lossy().to_string()))
|
||||
});
|
||||
reg.add_field_method_get("link_to", |_, me| Ok(me.link_to().map(Url::from)));
|
||||
reg.add_field_method_get("is_link", |_, me| Ok(me.is_link()));
|
||||
reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden()));
|
||||
|
||||
|
|
@ -69,7 +81,7 @@ impl Manager {
|
|||
})?;
|
||||
|
||||
LUA.register_userdata_type::<core::manager::Preview>(|reg| {
|
||||
reg.add_field_function_get("folder", |_, me| me.named_user_value::<AnyUserData>("folder"));
|
||||
reg.add_field_function_get("folder", |_, me| me.named_user_value::<Value>("folder"));
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -80,6 +92,7 @@ impl Manager {
|
|||
inner: &'a core::manager::Manager,
|
||||
) -> mlua::Result<AnyUserData<'a>> {
|
||||
let ud = scope.create_any_userdata_ref(inner)?;
|
||||
ud.set_named_user_value("mode", scope.create_any_userdata_ref(inner.active().mode())?)?;
|
||||
ud.set_named_user_value(
|
||||
"parent",
|
||||
inner.parent().and_then(|p| Self::folder(scope, inner, p).ok()),
|
||||
|
|
|
|||
47
plugin/src/config.rs
Normal file
47
plugin/src/config.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use mlua::{LuaSerdeExt, SerializeOptions, Table};
|
||||
|
||||
use crate::{layout::Rect, GLOBALS, LUA};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct Config;
|
||||
|
||||
impl Config {
|
||||
pub(super) fn install(self) -> mlua::Result<()> {
|
||||
let options =
|
||||
SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false);
|
||||
|
||||
self.theme(options)?;
|
||||
self.manager(options)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn theme(self, options: SerializeOptions) -> mlua::Result<()> {
|
||||
GLOBALS.set("THEME", LUA.to_value_with(&*config::THEME, options)?)
|
||||
}
|
||||
|
||||
fn manager(self, options: SerializeOptions) -> mlua::Result<()> {
|
||||
let manager = LUA.to_value_with(&*config::MANAGER, options)?;
|
||||
{
|
||||
let layout: Table = manager.as_table().unwrap().get("layout")?;
|
||||
|
||||
layout.set(
|
||||
"preview_rect",
|
||||
LUA.create_function(|_, ()| Ok(Rect(config::MANAGER.layout.preview_rect())))?,
|
||||
)?;
|
||||
layout.set(
|
||||
"preview_height",
|
||||
LUA.create_function(|_, ()| Ok(config::MANAGER.layout.preview_height()))?,
|
||||
)?;
|
||||
layout.set(
|
||||
"folder_rect",
|
||||
LUA.create_function(|_, ()| Ok(Rect(config::MANAGER.layout.folder_rect())))?,
|
||||
)?;
|
||||
layout.set(
|
||||
"folder_height",
|
||||
LUA.create_function(|_, ()| Ok(config::MANAGER.layout.folder_height()))?,
|
||||
)?;
|
||||
}
|
||||
|
||||
GLOBALS.set("MANAGER", manager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,27 @@
|
|||
use mlua::{FromLua, Lua, UserData, Value};
|
||||
use mlua::{FromLua, Lua, Table, UserData, Value};
|
||||
|
||||
use crate::{GLOBALS, LUA};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct Rect(pub(crate) ratatui::layout::Rect);
|
||||
|
||||
impl Rect {
|
||||
pub(crate) fn install() -> mlua::Result<()> {
|
||||
let ui: Table = GLOBALS.get("ui")?;
|
||||
ui.set(
|
||||
"Rect",
|
||||
LUA.create_function(|_, args: Table| {
|
||||
Ok(Self(ratatui::layout::Rect {
|
||||
x: args.get("x")?,
|
||||
y: args.get("y")?,
|
||||
width: args.get("w")?,
|
||||
height: args.get("h")?,
|
||||
}))
|
||||
})?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Rect {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
|
|
@ -20,7 +39,12 @@ impl UserData for Rect {
|
|||
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("x", |_, me| Ok(me.0.x));
|
||||
fields.add_field_method_get("y", |_, me| Ok(me.0.y));
|
||||
fields.add_field_method_get("width", |_, me| Ok(me.0.width));
|
||||
fields.add_field_method_get("height", |_, me| Ok(me.0.height));
|
||||
fields.add_field_method_get("w", |_, me| Ok(me.0.width));
|
||||
fields.add_field_method_get("h", |_, me| Ok(me.0.height));
|
||||
|
||||
fields.add_field_method_get("left", |_, me| Ok(me.0.left()));
|
||||
fields.add_field_method_get("right", |_, me| Ok(me.0.right()));
|
||||
fields.add_field_method_get("top", |_, me| Ok(me.0.top()));
|
||||
fields.add_field_method_get("bottom", |_, me| Ok(me.0.bottom()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
mod bindings;
|
||||
mod components;
|
||||
mod config;
|
||||
mod layout;
|
||||
mod plugin;
|
||||
|
||||
pub use components::*;
|
||||
use config::*;
|
||||
pub use plugin::*;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use config::THEME;
|
||||
use mlua::{Lua, LuaSerdeExt, SerializeOptions, Table};
|
||||
use mlua::{Lua, Table};
|
||||
use shared::RoCell;
|
||||
|
||||
use crate::{bindings, layout};
|
||||
|
|
@ -27,17 +26,16 @@ pub fn init() {
|
|||
bindings::init()?;
|
||||
|
||||
// Install
|
||||
crate::Config.install()?;
|
||||
|
||||
layout::Constraint::install()?;
|
||||
layout::Layout::install()?;
|
||||
layout::Line::install()?;
|
||||
layout::Paragraph::install()?;
|
||||
layout::Rect::install()?;
|
||||
layout::Span::install()?;
|
||||
layout::Style::install()?;
|
||||
|
||||
let options =
|
||||
SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false);
|
||||
GLOBALS.set("THEME", LUA.to_value_with(&*THEME, options)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue