From 03b1e8728b0ebc49b657bd0f3b7e4b224110f8e5 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 22 Sep 2023 12:20:00 +0800 Subject: [PATCH] .. --- Cargo.lock | 86 ++++++++++++- app/src/parser.rs | 50 ++++++-- app/src/status/layout.rs | 61 +-------- cspell.json | 2 +- plugin/Cargo.toml | 1 + plugin/preset/components/status.lua | 57 +++++---- plugin/preset/elements/paragraph.lua | 26 ++++ plugin/src/bindings.rs | 28 ++--- plugin/src/layout.rs | 178 +++++++++++++++++++++++++++ plugin/src/lib.rs | 2 + plugin/src/plugin.rs | 23 +++- plugin/src/status.rs | 48 ++------ shared/Cargo.toml | 3 +- 13 files changed, 409 insertions(+), 156 deletions(-) create mode 100644 plugin/src/layout.rs diff --git a/Cargo.lock b/Cargo.lock index 68b627e4..61b00410 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -133,6 +133,7 @@ dependencies = [ "crossterm", "futures", "libc", + "plugin", "ratatui", "shared", "signal-hook-tokio", @@ -217,6 +218,16 @@ dependencies = [ "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]] name = "bumpalo" version = "3.14.0" @@ -544,6 +555,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "error-code" version = "2.3.1" @@ -1073,6 +1093,33 @@ dependencies = [ "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]] name = "nom" version = "7.1.3" @@ -1203,6 +1250,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "overload" version = "0.1.1" @@ -1276,6 +1332,19 @@ dependencies = [ "time", ] +[[package]] +name = "plugin" +version = "0.1.0" +dependencies = [ + "anyhow", + "config", + "core", + "mlua", + "ratatui", + "shared", + "tracing", +] + [[package]] name = "png" version = "0.17.10" @@ -1450,6 +1519,12 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustversion" version = "1.0.14" @@ -1492,6 +1567,16 @@ dependencies = [ "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]] name = "serde_derive" version = "1.0.188" @@ -1542,7 +1627,6 @@ dependencies = [ "libc", "parking_lot", "ratatui", - "regex", "tokio", ] diff --git a/app/src/parser.rs b/app/src/parser.rs index a7929f28..5bce2cd4 100644 --- a/app/src/parser.rs +++ b/app/src/parser.rs @@ -1,15 +1,16 @@ +use anyhow::Result; 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; impl Parser { - pub fn span(s: &str) -> Span<'static> { + fn span(s: &str) -> Span<'static> { let Some((args, content)) = s.split_once(';') else { return Span::raw(s.to_string()); }; - let args = args.split(',').collect::>(); + let args: Vec<_> = args.split(',').collect(); if args.len() != 4 { return Span::raw(s.to_string()); } @@ -30,7 +31,7 @@ impl Parser { 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 spans: Vec = vec![String::new()]; @@ -49,7 +50,7 @@ impl Parser { Line::from(spans.into_iter().map(|s| Self::span(&s)).collect::>()) } - pub fn paragraph(s: &str) -> Paragraph { + fn paragraph(s: &str) -> Paragraph { let mut last = '\0'; let mut lines: Vec = vec![String::new()]; @@ -68,22 +69,49 @@ impl Parser { Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::>()) } - pub fn layout(s: &str) -> Paragraph { + fn area(args: Vec<&str>) -> Result { + 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 lines: Vec = vec![String::new()]; + let mut paragraphs: Vec = vec![String::new()]; for c in s.chars() { if c == '\0' && last == '\\' { - let last = lines.last_mut().unwrap(); + let last = paragraphs.last_mut().unwrap(); last.pop(); last.push(c); } else if c == '\0' { - lines.push(String::new()); + paragraphs.push(String::new()); } else { - lines.last_mut().unwrap().push(c); + paragraphs.last_mut().unwrap().push(c); } last = c; } - Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::>()) + + 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); + } + } } } diff --git a/app/src/status/layout.rs b/app/src/status/layout.rs index fec9cb2d..4b2becb4 100644 --- a/app/src/status/layout.rs +++ b/app/src/status/layout.rs @@ -1,9 +1,9 @@ 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 crate::Parser; +use crate::parser::Parser; pub(crate) struct Layout<'a> { cx: &'a Ctx, @@ -20,63 +20,14 @@ impl<'a> Widget for Layout<'a> { .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(area); - // Left::new(self.cx).render(chunks[0], buf); - // 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); + let x = plugin::Status::layout(self.cx, area); if x.is_err() { - info!("Error: {:?}", x); + info!("{:?}", x); return; } - if let Ok(size) = x { - spans.extend(Parser::line(&size).spans); - } - let x = plugin::Status::name(self.cx); - if x.is_err() { - info!("Error: {:?}", x); - return; + if let Ok(s) = x { + Parser::render(&s, buf); } - 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); } } diff --git a/cspell.json b/cspell.json index b19e1b92..19ff1c11 100644 --- a/cspell.json +++ b/cspell.json @@ -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"} diff --git a/plugin/Cargo.toml b/plugin/Cargo.toml index 021ee291..892c929f 100644 --- a/plugin/Cargo.toml +++ b/plugin/Cargo.toml @@ -12,3 +12,4 @@ shared = { path = "../shared" } anyhow = "^1" mlua = { version = "^0", features = [ "lua54", "serialize" ] } tracing = "^0" +ratatui = "^0" diff --git a/plugin/preset/components/status.lua b/plugin/preset/components/status.lua index 8b3ce368..b3ec2943 100644 --- a/plugin/preset/components/status.lua +++ b/plugin/preset/components/status.lua @@ -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() local mode = cx.manager.mode:upper() @@ -6,45 +17,41 @@ function mode() mode = "UN-SET" end - return yazi - .Line( - yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg), - yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal) - ) - :to_string() + return yazi.Line( + yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg), + yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal) + ) end function size() local hovered = cx.manager.current_hovered if hovered == nil then - return "" + return yazi.Span("") end - return yazi - .Line( - 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) - ) - :to_string() + return yazi.Line( + 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) + ) end function name() local hovered = cx.manager.current_hovered if hovered == nil then - return "" + return yazi.Span("") end - return yazi.Span(" " .. utils.basename(hovered.url)):to_string() + return yazi.Span(" " .. utils.basename(hovered.url)) end function permissions() local hovered = cx.manager.current_hovered if hovered == nil then - return "" + return yazi.Span("") end if hovered.permissions == nil then - return "" + return yazi.Span("") end local spans = {} @@ -60,7 +67,7 @@ function permissions() end spans[i] = yazi.Span(c):style(style) end - return yazi.Line:from(spans):to_string() + return yazi.Line:from(spans) end function percentage() @@ -77,16 +84,14 @@ function percentage() percent = string.format(" %3d%% ", percent) end - return yazi - .Line( - 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) - ) - :to_string() + return yazi.Line( + 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) + ) end function position() local cursor = cx.manager.current_cursor 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 diff --git a/plugin/preset/elements/paragraph.lua b/plugin/preset/elements/paragraph.lua index 04f532fd..5682b478 100644 --- a/plugin/preset/elements/paragraph.lua +++ b/plugin/preset/elements/paragraph.lua @@ -2,6 +2,7 @@ local Paragraph = {} function Paragraph:new(...) local o = { + position = nil, lines = { ... }, } setmetatable(o, self) @@ -11,6 +12,11 @@ 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() local s = "" for _, line in ipairs(self.lines) do @@ -19,6 +25,26 @@ function Paragraph:to_string() return s.sub(s, 1, -2) 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, { __call = function(self, ...) return self:new(...) end, __tostring = function(self) return self:to_string() end, diff --git a/plugin/src/bindings.rs b/plugin/src/bindings.rs index fe61c9df..4cb9018a 100644 --- a/plugin/src/bindings.rs +++ b/plugin/src/bindings.rs @@ -10,12 +10,12 @@ impl<'a> Manager<'a> { impl<'a> UserData for Manager<'a> { 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_length", |_, this| Ok(this.0.current().files.len())); - fields.add_field_method_get("current_hovered", |_, this| { - Ok(this.0.current().hovered.as_ref().map(File::from)) + fields.add_field_method_get("current_cursor", |_, me| Ok(me.0.current().cursor())); + fields.add_field_method_get("current_length", |_, me| Ok(me.0.current().files.len())); + fields.add_field_method_get("current_hovered", |_, me| { + Ok(me.0.current().hovered.as_ref().map(File::from)) }); } } @@ -30,7 +30,7 @@ impl<'a> Tasks<'a> { impl<'a> UserData for Tasks<'a> { 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 { 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("length", |_, this| Ok(this.0.length())); - fields.add_field_method_get("link_to", |_, this| { - Ok(this.0.link_to().map(|l| l.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", |_, me| Ok(me.0.length())); + fields.add_field_method_get("link_to", |_, me| { + 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_hidden", |_, this| Ok(this.0.is_hidden())); + fields.add_field_method_get("is_link", |_, me| Ok(me.0.is_link())); + fields.add_field_method_get("is_hidden", |_, me| Ok(me.0.is_hidden())); // Meta - fields.add_field_method_get("permissions", |_, this| { - Ok(shared::permissions(this.0.meta().permissions())) + fields.add_field_method_get("permissions", |_, me| { + Ok(shared::permissions(me.0.meta().permissions())) }); } } diff --git a/plugin/src/layout.rs b/plugin/src/layout.rs new file mode 100644 index 00000000..c14a37bb --- /dev/null +++ b/plugin/src/layout.rs @@ -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 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 { + match value { + Value::UserData(ud) => Ok(*ud.borrow::()?), + _ => 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 { + match value { + Value::UserData(ud) => Ok(*ud.borrow::()?), + _ => 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, + constraints: Vec, +} + +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 { + match value { + Value::UserData(ud) => Ok(ud.borrow::()?.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::()?; + me.direction = value; + } + Ok(ud) + }); + methods.add_function("margin", |_, (ud, value): (AnyUserData, u16)| { + { + let mut me = ud.borrow_mut::()?; + 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::()?; + 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::()?; + 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)| { + { + let mut me = ud.borrow_mut::()?; + me.constraints = value.into_iter().map(|c| c.0).collect(); + } + Ok(ud) + }); + methods.add_function("split", |_, (ud, value): (AnyUserData, Rect)| { + let me = ud.borrow::()?; + + 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 = layout.split(value.0).iter().copied().map(Rect).collect(); + Ok(chunks) + }); + } +} diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index b0120294..7db0e939 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -1,9 +1,11 @@ #![allow(clippy::unit_arg)] mod bindings; +mod layout; mod plugin; mod status; pub use bindings::*; +pub use layout::*; pub use plugin::*; pub use status::*; diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index e5fa888a..cb2bb95b 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -3,21 +3,34 @@ use config::THEME; use mlua::{Lua, LuaSerdeExt}; use shared::RoCell; +use crate::layout; + pub(crate) static LUA: RoCell = RoCell::new(); pub fn init() { fn inner() -> Result<()> { let lua = Lua::new(); + + // Base lua.load(include_str!("../preset/utils.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)?)?; - Ok(LUA.init(lua)) + LUA.init(lua); + + layout::Layout::install()?; + layout::Constraint::install()?; + + Ok(()) } inner().expect("failed to initialize Lua"); diff --git a/plugin/src/status.rs b/plugin/src/status.rs index 47f06ace..52965aa0 100644 --- a/plugin/src/status.rs +++ b/plugin/src/status.rs @@ -1,13 +1,14 @@ use core::Ctx; use mlua::{Function, Result}; +use ratatui::layout; -use crate::{bindings, LUA}; +use crate::{bindings, Rect, LUA}; pub struct Status; impl Status { - fn scoped Result>(cx: &Ctx, f: F) -> Result { + fn scope Result>(cx: &Ctx, f: F) -> Result { LUA.scope(|scope| { let manager = scope.create_nonstatic_userdata(bindings::Manager::new(&cx.manager))?; let tasks = scope.create_nonstatic_userdata(bindings::Tasks::new(&cx.tasks))?; @@ -21,45 +22,10 @@ impl Status { }) } - pub fn mode(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let mode: Function = LUA.globals().get("mode")?; - mode.call::<_, String>(()) - }) - } - - pub fn size(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let size: Function = LUA.globals().get("size")?; - size.call::<_, String>(()) - }) - } - - pub fn name(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let size: Function = LUA.globals().get("name")?; - size.call::<_, String>(()) - }) - } - - pub fn permissions(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let size: Function = LUA.globals().get("permissions")?; - size.call::<_, String>(()) - }) - } - - pub fn percentage(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let size: Function = LUA.globals().get("percentage")?; - size.call::<_, String>(()) - }) - } - - pub fn position(cx: &Ctx) -> Result { - Self::scoped(cx, || { - let size: Function = LUA.globals().get("position")?; - size.call::<_, String>(()) + pub fn layout(cx: &Ctx, area: layout::Rect) -> Result { + Self::scope(cx, || { + let layout: Function = LUA.globals().get("layout")?; + layout.call::<_, String>(Rect::from(area)) }) } } diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 85f147f1..d631eda0 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -9,6 +9,5 @@ crossterm = "^0" futures = "^0" libc = "^0" parking_lot = "^0" -ratatui = { version = "^0" } -regex = "^1" +ratatui = "^0" tokio = { version = "^1", features = [ "parking_lot", "macros", "rt-multi-thread", "sync", "time", "fs" ] }