This commit is contained in:
sxyazi 2023-09-22 12:20:00 +08:00
parent befcd555f9
commit 03b1e8728b
No known key found for this signature in database
13 changed files with 409 additions and 156 deletions

86
Cargo.lock generated
View file

@ -133,6 +133,7 @@ dependencies = [
"crossterm", "crossterm",
"futures", "futures",
"libc", "libc",
"plugin",
"ratatui", "ratatui",
"shared", "shared",
"signal-hook-tokio", "signal-hook-tokio",
@ -217,6 +218,16 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bstr"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019"
dependencies = [
"memchr",
"serde",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.14.0" version = "3.14.0"
@ -544,6 +555,15 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "erased-serde"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "error-code" name = "error-code"
version = "2.3.1" version = "2.3.1"
@ -1073,6 +1093,33 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "mlua"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c3a7a7ff4481ec91b951a733390211a8ace1caba57266ccb5f4d4966704e560"
dependencies = [
"bstr",
"erased-serde",
"mlua-sys",
"num-traits",
"once_cell",
"rustc-hash",
"serde",
"serde-value",
]
[[package]]
name = "mlua-sys"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ec8b54eddb76093069cce9eeffb4c7b3a1a0fe66962d7bd44c4867928149ca3"
dependencies = [
"cc",
"cfg-if",
"pkg-config",
]
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1203,6 +1250,15 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-float"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1276,6 +1332,19 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "plugin"
version = "0.1.0"
dependencies = [
"anyhow",
"config",
"core",
"mlua",
"ratatui",
"shared",
"tracing",
]
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.10" version = "0.17.10"
@ -1450,6 +1519,12 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.14" version = "1.0.14"
@ -1492,6 +1567,16 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-value"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
dependencies = [
"ordered-float",
"serde",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.188" version = "1.0.188"
@ -1542,7 +1627,6 @@ dependencies = [
"libc", "libc",
"parking_lot", "parking_lot",
"ratatui", "ratatui",
"regex",
"tokio", "tokio",
] ]

View file

@ -1,15 +1,16 @@
use anyhow::Result;
use config::theme::Color; use config::theme::Color;
use ratatui::{style::{Modifier, Style}, text::{Line, Span}, widgets::Paragraph}; use ratatui::{prelude::{Buffer, Rect}, style::{Modifier, Style}, text::{Line, Span}, widgets::{Paragraph, Widget}};
pub struct Parser; pub struct Parser;
impl Parser { impl Parser {
pub fn span(s: &str) -> Span<'static> { fn span(s: &str) -> Span<'static> {
let Some((args, content)) = s.split_once(';') else { let Some((args, content)) = s.split_once(';') else {
return Span::raw(s.to_string()); return Span::raw(s.to_string());
}; };
let args = args.split(',').collect::<Vec<_>>(); let args: Vec<_> = args.split(',').collect();
if args.len() != 4 { if args.len() != 4 {
return Span::raw(s.to_string()); return Span::raw(s.to_string());
} }
@ -30,7 +31,7 @@ impl Parser {
Span::styled(content.to_string(), style) Span::styled(content.to_string(), style)
} }
pub fn line(s: &str) -> Line<'static> { fn line(s: &str) -> Line<'static> {
let mut last = '\0'; let mut last = '\0';
let mut spans: Vec<String> = vec![String::new()]; let mut spans: Vec<String> = vec![String::new()];
@ -49,7 +50,7 @@ impl Parser {
Line::from(spans.into_iter().map(|s| Self::span(&s)).collect::<Vec<_>>()) Line::from(spans.into_iter().map(|s| Self::span(&s)).collect::<Vec<_>>())
} }
pub fn paragraph(s: &str) -> Paragraph { fn paragraph(s: &str) -> Paragraph {
let mut last = '\0'; let mut last = '\0';
let mut lines: Vec<String> = vec![String::new()]; let mut lines: Vec<String> = vec![String::new()];
@ -68,22 +69,49 @@ impl Parser {
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>()) Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
} }
pub fn layout(s: &str) -> Paragraph { fn area(args: Vec<&str>) -> Result<Rect> {
Ok(Rect {
x: args[0].parse()?,
y: args[1].parse()?,
width: args[2].parse()?,
height: args[3].parse()?,
})
}
pub fn render(s: &str, buf: &mut Buffer) {
let Some(s) = s.strip_prefix('R') else {
return;
};
let mut last = '\0'; let mut last = '\0';
let mut lines: Vec<String> = vec![String::new()]; let mut paragraphs: Vec<String> = vec![String::new()];
for c in s.chars() { for c in s.chars() {
if c == '\0' && last == '\\' { if c == '\0' && last == '\\' {
let last = lines.last_mut().unwrap(); let last = paragraphs.last_mut().unwrap();
last.pop(); last.pop();
last.push(c); last.push(c);
} else if c == '\0' { } else if c == '\0' {
lines.push(String::new()); paragraphs.push(String::new());
} else { } else {
lines.last_mut().unwrap().push(c); paragraphs.last_mut().unwrap().push(c);
} }
last = c; last = c;
} }
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
for paragraph in paragraphs {
let Some((args, content)) = paragraph.split_once(';') else {
continue;
};
let args: Vec<_> = args.split(',').collect();
if args.len() != 4 {
continue;
}
if let Ok(area) = Self::area(args) {
Self::paragraph(content).render(area, buf);
}
}
} }
} }

View file

@ -1,9 +1,9 @@
use core::Ctx; use core::Ctx;
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, text::Line, widgets::{Paragraph, Widget}}; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::Widget};
use tracing::info; use tracing::info;
use crate::Parser; use crate::parser::Parser;
pub(crate) struct Layout<'a> { pub(crate) struct Layout<'a> {
cx: &'a Ctx, cx: &'a Ctx,
@ -20,63 +20,14 @@ impl<'a> Widget for Layout<'a> {
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(area); .split(area);
// Left::new(self.cx).render(chunks[0], buf); let x = plugin::Status::layout(self.cx, area);
// Right::new(self.cx).render(chunks[1], buf);
let mut spans = vec![];
if let Ok(mode) = plugin::Status::mode(self.cx) {
spans.extend(Parser::line(&mode).spans);
}
let x = plugin::Status::size(self.cx);
if x.is_err() { if x.is_err() {
info!("Error: {:?}", x); info!("{:?}", x);
return; return;
} }
if let Ok(size) = x {
spans.extend(Parser::line(&size).spans);
}
let x = plugin::Status::name(self.cx); if let Ok(s) = x {
if x.is_err() { Parser::render(&s, buf);
info!("Error: {:?}", x);
return;
} }
if let Ok(name) = x {
spans.extend(Parser::line(&name).spans);
}
Paragraph::new(Line::from(spans)).render(chunks[0], buf);
// Right
let mut spans = vec![];
let x = plugin::Status::permissions(self.cx);
if x.is_err() {
info!("Error: {:?}", x);
return;
}
if let Ok(name) = x {
spans.extend(Parser::line(&name).spans);
}
let x = plugin::Status::percentage(self.cx);
if x.is_err() {
info!("Error: {:?}", x);
return;
}
if let Ok(name) = x {
spans.extend(Parser::line(&name).spans);
}
let x = plugin::Status::position(self.cx);
if x.is_err() {
info!("Error: {:?}", x);
return;
}
if let Ok(name) = x {
spans.extend(Parser::line(&name).spans);
}
Paragraph::new(Line::from(spans)).render(chunks[1], buf);
} }
} }

View file

@ -1 +1 @@
{"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","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","backstack","natsort","natsort","USERPROFILE"],"language":"en","version":"0.2"} {"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"],"language":"en","version":"0.2"}

View file

@ -12,3 +12,4 @@ shared = { path = "../shared" }
anyhow = "^1" anyhow = "^1"
mlua = { version = "^0", features = [ "lua54", "serialize" ] } mlua = { version = "^0", features = [ "lua54", "serialize" ] }
tracing = "^0" tracing = "^0"
ratatui = "^0"

View file

@ -1,4 +1,15 @@
function layout() end function layout(area)
local chunks = yazi
.Layout()
:direction(false)
:constraints({ yazi.Constraint.Percentage(50), yazi.Constraint.Percentage(50) })
:split(area)
return yazi.Paragraph.render(
yazi.Paragraph(mode(), size(), name()):area(chunks[1]),
yazi.Paragraph(permissions(), percentage(), position()):area(chunks[2])
)
end
function mode() function mode()
local mode = cx.manager.mode:upper() local mode = cx.manager.mode:upper()
@ -6,45 +17,41 @@ function mode()
mode = "UN-SET" mode = "UN-SET"
end end
return yazi return yazi.Line(
.Line( yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg),
yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg), yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal)
yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal) )
)
:to_string()
end end
function size() function size()
local hovered = cx.manager.current_hovered local hovered = cx.manager.current_hovered
if hovered == nil then if hovered == nil then
return "" return yazi.Span("")
end end
return yazi return yazi.Line(
.Line( yazi.Span(" " .. hovered.length .. " "):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg),
yazi.Span(" " .. hovered.length .. " "):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg), yazi.Span(THEME.status.separator.closing):fg(THEME.status.fancy.bg)
yazi.Span(THEME.status.separator.closing):fg(THEME.status.fancy.bg) )
)
:to_string()
end end
function name() function name()
local hovered = cx.manager.current_hovered local hovered = cx.manager.current_hovered
if hovered == nil then if hovered == nil then
return "" return yazi.Span("")
end end
return yazi.Span(" " .. utils.basename(hovered.url)):to_string() return yazi.Span(" " .. utils.basename(hovered.url))
end end
function permissions() function permissions()
local hovered = cx.manager.current_hovered local hovered = cx.manager.current_hovered
if hovered == nil then if hovered == nil then
return "" return yazi.Span("")
end end
if hovered.permissions == nil then if hovered.permissions == nil then
return "" return yazi.Span("")
end end
local spans = {} local spans = {}
@ -60,7 +67,7 @@ function permissions()
end end
spans[i] = yazi.Span(c):style(style) spans[i] = yazi.Span(c):style(style)
end end
return yazi.Line:from(spans):to_string() return yazi.Line:from(spans)
end end
function percentage() function percentage()
@ -77,16 +84,14 @@ function percentage()
percent = string.format(" %3d%% ", percent) percent = string.format(" %3d%% ", percent)
end end
return yazi return yazi.Line(
.Line( yazi.Span(THEME.status.separator.opening):fg(THEME.status.fancy.bg),
yazi.Span(THEME.status.separator.opening):fg(THEME.status.fancy.bg), yazi.Span(percent):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg)
yazi.Span(percent):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg) )
)
:to_string()
end end
function position() function position()
local cursor = cx.manager.current_cursor local cursor = cx.manager.current_cursor
local length = cx.manager.current_length local length = cx.manager.current_length
return string.format(" %d/%d ", cursor + 1, length) return yazi.Span(string.format(" %d/%d ", cursor + 1, length))
end end

View file

@ -2,6 +2,7 @@ local Paragraph = {}
function Paragraph:new(...) function Paragraph:new(...)
local o = { local o = {
position = nil,
lines = { ... }, lines = { ... },
} }
setmetatable(o, self) setmetatable(o, self)
@ -11,6 +12,11 @@ end
function Paragraph:from(lines) return self:new(table.unpack(lines)) end function Paragraph:from(lines) return self:new(table.unpack(lines)) end
function Paragraph:area(rect)
self.position = rect
return self
end
function Paragraph:to_string() function Paragraph:to_string()
local s = "" local s = ""
for _, line in ipairs(self.lines) do for _, line in ipairs(self.lines) do
@ -19,6 +25,26 @@ function Paragraph:to_string()
return s.sub(s, 1, -2) return s.sub(s, 1, -2)
end end
function Paragraph:aaa() return self:to_string() end
function Paragraph.render(...)
local s = "R"
for _, paragraph in ipairs { ... } do
s = s
.. paragraph.position.x
.. ","
.. paragraph.position.y
.. ","
.. paragraph.position.width
.. ","
.. paragraph.position.height
.. ";"
.. paragraph:to_string():gsub("\0", "\\\0")
.. "\0"
end
return s.sub(s, 1, -2)
end
setmetatable(Paragraph, { setmetatable(Paragraph, {
__call = function(self, ...) return self:new(...) end, __call = function(self, ...) return self:new(...) end,
__tostring = function(self) return self:to_string() end, __tostring = function(self) return self:to_string() end,

View file

@ -10,12 +10,12 @@ impl<'a> Manager<'a> {
impl<'a> UserData for Manager<'a> { impl<'a> UserData for Manager<'a> {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("mode", |_, this| Ok(this.0.active().mode().to_string())); fields.add_field_method_get("mode", |_, me| Ok(me.0.active().mode().to_string()));
fields.add_field_method_get("current_cursor", |_, this| Ok(this.0.current().cursor())); fields.add_field_method_get("current_cursor", |_, me| Ok(me.0.current().cursor()));
fields.add_field_method_get("current_length", |_, this| Ok(this.0.current().files.len())); fields.add_field_method_get("current_length", |_, me| Ok(me.0.current().files.len()));
fields.add_field_method_get("current_hovered", |_, this| { fields.add_field_method_get("current_hovered", |_, me| {
Ok(this.0.current().hovered.as_ref().map(File::from)) Ok(me.0.current().hovered.as_ref().map(File::from))
}); });
} }
} }
@ -30,7 +30,7 @@ impl<'a> Tasks<'a> {
impl<'a> UserData for Tasks<'a> { impl<'a> UserData for Tasks<'a> {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("progress", |lua, this| lua.to_value(&this.0.progress)) fields.add_field_method_get("progress", |lua, me| lua.to_value(&me.0.progress))
} }
} }
@ -44,17 +44,17 @@ impl From<&core::files::File> for File {
impl UserData for File { impl UserData for File {
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("url", |_, this| Ok(this.0.url().to_string_lossy().to_string())); fields.add_field_method_get("url", |_, me| Ok(me.0.url().to_string_lossy().to_string()));
fields.add_field_method_get("length", |_, this| Ok(this.0.length())); fields.add_field_method_get("length", |_, me| Ok(me.0.length()));
fields.add_field_method_get("link_to", |_, this| { fields.add_field_method_get("link_to", |_, me| {
Ok(this.0.link_to().map(|l| l.to_string_lossy().to_string())) Ok(me.0.link_to().map(|l| l.to_string_lossy().to_string()))
}); });
fields.add_field_method_get("is_link", |_, this| Ok(this.0.is_link())); fields.add_field_method_get("is_link", |_, me| Ok(me.0.is_link()));
fields.add_field_method_get("is_hidden", |_, this| Ok(this.0.is_hidden())); fields.add_field_method_get("is_hidden", |_, me| Ok(me.0.is_hidden()));
// Meta // Meta
fields.add_field_method_get("permissions", |_, this| { fields.add_field_method_get("permissions", |_, me| {
Ok(shared::permissions(this.0.meta().permissions())) Ok(shared::permissions(me.0.meta().permissions()))
}); });
} }
} }

178
plugin/src/layout.rs Normal file
View file

@ -0,0 +1,178 @@
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, UserDataMethods, Value};
use ratatui::layout;
use crate::LUA;
// --- Rect
#[derive(Clone, Copy)]
pub struct Rect(layout::Rect);
impl From<layout::Rect> for Rect {
fn from(value: layout::Rect) -> Self { Self(value) }
}
impl<'lua> FromLua<'lua> for Rect {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Rect",
message: Some("expected a Rect".to_string()),
}),
}
}
}
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));
}
}
// --- Constraint
#[derive(Clone, Copy)]
pub struct Constraint(layout::Constraint);
impl Constraint {
pub(super) fn install() -> mlua::Result<()> {
let globals = LUA.globals();
let yazi = globals.get::<_, Table>("yazi")?;
let constraint = LUA.create_table()?;
constraint.set(
"Percentage",
LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Percentage(n))))?,
)?;
constraint.set(
"Ratio",
LUA
.create_function(|_, (a, b): (u32, u32)| Ok(Constraint(layout::Constraint::Ratio(a, b))))?,
)?;
constraint.set(
"Length",
LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Length(n))))?,
)?;
constraint
.set("Max", LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Max(n))))?)?;
constraint
.set("Min", LUA.create_function(|_, n: u16| Ok(Constraint(layout::Constraint::Min(n))))?)?;
yazi.set("Constraint", constraint)
}
}
impl<'lua> FromLua<'lua> for Constraint {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Constraint",
message: Some("expected a Constraint".to_string()),
}),
}
}
}
impl UserData for Constraint {}
// --- Layout
#[derive(Clone, Default)]
pub struct Layout {
direction: bool,
margin: Option<layout::Margin>,
constraints: Vec<layout::Constraint>,
}
impl Layout {
pub(super) fn install() -> mlua::Result<()> {
let globals = LUA.globals();
let yazi = globals.get::<_, Table>("yazi")?;
yazi.set("Layout", LUA.create_function(|_, ()| Ok(Self::default()))?)
}
}
impl<'lua> FromLua<'lua> for Layout {
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => Err(mlua::Error::FromLuaConversionError {
from: value.type_name(),
to: "Layout",
message: Some("expected a Layout".to_string()),
}),
}
}
}
impl UserData for Layout {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("direction", |_, (ud, value): (AnyUserData, bool)| {
{
let mut me = ud.borrow_mut::<Self>()?;
me.direction = value;
}
Ok(ud)
});
methods.add_function("margin", |_, (ud, value): (AnyUserData, u16)| {
{
let mut me = ud.borrow_mut::<Self>()?;
me.margin = Some(layout::Margin::new(value, value));
}
Ok(ud)
});
methods.add_function("margin_h", |_, (ud, value): (AnyUserData, u16)| {
{
let mut me = ud.borrow_mut::<Self>()?;
if let Some(margin) = &mut me.margin {
margin.horizontal = value;
} else {
me.margin = Some(layout::Margin::new(value, 0));
}
}
Ok(ud)
});
methods.add_function("margin_v", |_, (ud, value): (AnyUserData, u16)| {
{
let mut me = ud.borrow_mut::<Self>()?;
if let Some(margin) = &mut me.margin {
margin.vertical = value;
} else {
me.margin = Some(layout::Margin::new(0, value));
}
}
Ok(ud)
});
methods.add_function("constraints", |_, (ud, value): (AnyUserData, Vec<Constraint>)| {
{
let mut me = ud.borrow_mut::<Self>()?;
me.constraints = value.into_iter().map(|c| c.0).collect();
}
Ok(ud)
});
methods.add_function("split", |_, (ud, value): (AnyUserData, Rect)| {
let me = ud.borrow::<Self>()?;
let mut layout = layout::Layout::new()
.direction(if me.direction {
layout::Direction::Horizontal
} else {
layout::Direction::Vertical
})
.constraints(me.constraints.as_slice());
if let Some(margin) = me.margin {
layout = layout.horizontal_margin(margin.horizontal);
layout = layout.vertical_margin(margin.vertical);
}
let chunks: Vec<Rect> = layout.split(value.0).iter().copied().map(Rect).collect();
Ok(chunks)
});
}
}

View file

@ -1,9 +1,11 @@
#![allow(clippy::unit_arg)] #![allow(clippy::unit_arg)]
mod bindings; mod bindings;
mod layout;
mod plugin; mod plugin;
mod status; mod status;
pub use bindings::*; pub use bindings::*;
pub use layout::*;
pub use plugin::*; pub use plugin::*;
pub use status::*; pub use status::*;

View file

@ -3,21 +3,34 @@ use config::THEME;
use mlua::{Lua, LuaSerdeExt}; use mlua::{Lua, LuaSerdeExt};
use shared::RoCell; use shared::RoCell;
use crate::layout;
pub(crate) static LUA: RoCell<Lua> = RoCell::new(); pub(crate) static LUA: RoCell<Lua> = RoCell::new();
pub fn init() { pub fn init() {
fn inner() -> Result<()> { fn inner() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
// Base
lua.load(include_str!("../preset/utils.lua")).exec()?; lua.load(include_str!("../preset/utils.lua")).exec()?;
lua.load(include_str!("../preset/inspect/inspect.lua")).exec()?; lua.load(include_str!("../preset/inspect/inspect.lua")).exec()?;
lua.load(include_str!("../preset/span.lua")).exec()?;
lua.load(include_str!("../preset/line.lua")).exec()?;
lua.load(include_str!("../preset/paragraph.lua")).exec()?;
lua.load(include_str!("../preset/status.lua")).exec()?; // Elements
lua.load(include_str!("../preset/elements/span.lua")).exec()?;
lua.load(include_str!("../preset/elements/line.lua")).exec()?;
lua.load(include_str!("../preset/elements/paragraph.lua")).exec()?;
// Components
lua.load(include_str!("../preset/components/status.lua")).exec()?;
// Initialize
lua.globals().set("THEME", lua.to_value(&*THEME)?)?; lua.globals().set("THEME", lua.to_value(&*THEME)?)?;
Ok(LUA.init(lua)) LUA.init(lua);
layout::Layout::install()?;
layout::Constraint::install()?;
Ok(())
} }
inner().expect("failed to initialize Lua"); inner().expect("failed to initialize Lua");

View file

@ -1,13 +1,14 @@
use core::Ctx; use core::Ctx;
use mlua::{Function, Result}; use mlua::{Function, Result};
use ratatui::layout;
use crate::{bindings, LUA}; use crate::{bindings, Rect, LUA};
pub struct Status; pub struct Status;
impl Status { impl Status {
fn scoped<T, F: FnOnce() -> Result<T>>(cx: &Ctx, f: F) -> Result<T> { fn scope<T, F: FnOnce() -> Result<T>>(cx: &Ctx, f: F) -> Result<T> {
LUA.scope(|scope| { LUA.scope(|scope| {
let manager = scope.create_nonstatic_userdata(bindings::Manager::new(&cx.manager))?; let manager = scope.create_nonstatic_userdata(bindings::Manager::new(&cx.manager))?;
let tasks = scope.create_nonstatic_userdata(bindings::Tasks::new(&cx.tasks))?; let tasks = scope.create_nonstatic_userdata(bindings::Tasks::new(&cx.tasks))?;
@ -21,45 +22,10 @@ impl Status {
}) })
} }
pub fn mode(cx: &Ctx) -> Result<String> { pub fn layout(cx: &Ctx, area: layout::Rect) -> Result<String> {
Self::scoped(cx, || { Self::scope(cx, || {
let mode: Function = LUA.globals().get("mode")?; let layout: Function = LUA.globals().get("layout")?;
mode.call::<_, String>(()) layout.call::<_, String>(Rect::from(area))
})
}
pub fn size(cx: &Ctx) -> Result<String> {
Self::scoped(cx, || {
let size: Function = LUA.globals().get("size")?;
size.call::<_, String>(())
})
}
pub fn name(cx: &Ctx) -> Result<String> {
Self::scoped(cx, || {
let size: Function = LUA.globals().get("name")?;
size.call::<_, String>(())
})
}
pub fn permissions(cx: &Ctx) -> Result<String> {
Self::scoped(cx, || {
let size: Function = LUA.globals().get("permissions")?;
size.call::<_, String>(())
})
}
pub fn percentage(cx: &Ctx) -> Result<String> {
Self::scoped(cx, || {
let size: Function = LUA.globals().get("percentage")?;
size.call::<_, String>(())
})
}
pub fn position(cx: &Ctx) -> Result<String> {
Self::scoped(cx, || {
let size: Function = LUA.globals().get("position")?;
size.call::<_, String>(())
}) })
} }
} }

View file

@ -9,6 +9,5 @@ crossterm = "^0"
futures = "^0" futures = "^0"
libc = "^0" libc = "^0"
parking_lot = "^0" parking_lot = "^0"
ratatui = { version = "^0" } ratatui = "^0"
regex = "^1"
tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs" ] } tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs" ] }