mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
46c54f6bad
commit
4718c4ddee
15 changed files with 272 additions and 92 deletions
|
|
@ -76,7 +76,7 @@ impl App {
|
||||||
fn dispatch_render(&mut self) {
|
fn dispatch_render(&mut self) {
|
||||||
if let Some(term) = &mut self.term {
|
if let Some(term) = &mut self.term {
|
||||||
let _ = term.draw(|f| {
|
let _ = term.draw(|f| {
|
||||||
plugin::scope(&self.cx, || {
|
plugin::scope(&self.cx, |_| {
|
||||||
f.render_widget(Root::new(&self.cx), f.size());
|
f.render_widget(Root::new(&self.cx), f.size());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,36 +16,14 @@ impl Folder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Folder {
|
impl Folder {
|
||||||
// fn highlighted_item<'b>(&'b self, file: &'b File) -> Vec<Span> {
|
|
||||||
// let short = short_path(file.url(), &self.folder.cwd);
|
// let short = short_path(file.url(), &self.folder.cwd);
|
||||||
//
|
|
||||||
// let v = self.is_find.then_some(()).and_then(|_| {
|
|
||||||
// let finder = self.cx.manager.active().finder()?;
|
|
||||||
// let (head, body, tail) = finder.explode(short.name)?;
|
|
||||||
//
|
|
||||||
// // TODO: to be configured by THEME?
|
|
||||||
// let style = Style::new().fg(Color::Rgb(255, 255,
|
|
||||||
// 50)).add_modifier(Modifier::ITALIC); Some(vec![
|
|
||||||
// Span::raw(short.prefix.join(head.as_ref()).display().to_string()),
|
|
||||||
// Span::styled(body, style),
|
|
||||||
// Span::raw(tail),
|
|
||||||
// ])
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// v.unwrap_or_else(|| vec![Span::raw(format!("{}", short))])
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for Folder {
|
impl Widget for Folder {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let x = plugin::Folder { kind: self.kind as u8 }.render(area);
|
let x = plugin::Folder { kind: self.kind as u8 }.render(area, buf);
|
||||||
if x.is_err() {
|
if x.is_err() {
|
||||||
info!("{:?}", x);
|
info!("{:?}", x);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for x in x.unwrap() {
|
|
||||||
x.render(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// let items: Vec<_> = window
|
// let items: Vec<_> = window
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,10 @@ pub(crate) struct Layout;
|
||||||
|
|
||||||
impl Widget for Layout {
|
impl Widget for Layout {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let x = plugin::Status::render(area);
|
let x = plugin::Status.render(area, buf);
|
||||||
if x.is_err() {
|
if x.is_err() {
|
||||||
info!("{:?}", x);
|
info!("{:?}", x);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in x.unwrap() {
|
|
||||||
x.render(buf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::BTreeMap, ffi::OsStr};
|
use std::{collections::BTreeMap, ffi::OsStr, ops::Range};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use regex::bytes::{Regex, RegexBuilder};
|
use regex::bytes::{Regex, RegexBuilder};
|
||||||
|
|
@ -91,13 +91,17 @@ impl Finder {
|
||||||
|
|
||||||
/// Explode the name into three parts: head, body, tail.
|
/// Explode the name into three parts: head, body, tail.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn explode(&self, name: &[u8]) -> Option<(String, String, String)> {
|
pub fn highlighted(&self, name: &OsStr) -> Vec<Range<usize>> {
|
||||||
let range = self.query.find(name).map(|m| m.range())?;
|
#[cfg(target_os = "windows")]
|
||||||
Some((
|
let found = self.query.find(name.to_string_lossy().as_bytes());
|
||||||
String::from_utf8_lossy(&name[..range.start]).to_string(),
|
|
||||||
String::from_utf8_lossy(&name[range.start..range.end]).to_string(),
|
#[cfg(not(target_os = "windows"))]
|
||||||
String::from_utf8_lossy(&name[range.end..]).to_string(),
|
let found = {
|
||||||
))
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
self.query.find(name.as_bytes())
|
||||||
|
};
|
||||||
|
|
||||||
|
found.map(|m| vec![m.range()]).unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,6 @@ function Folder:by_kind(kind)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:window(kind) return (self:by_kind(kind) or {}).window end
|
|
||||||
|
|
||||||
function Folder:hovered(kind) return (self:by_kind(kind) or {}).hovered end
|
|
||||||
|
|
||||||
function Folder:markers(area, markers)
|
function Folder:markers(area, markers)
|
||||||
if #markers == 0 then
|
if #markers == 0 then
|
||||||
return {}
|
return {}
|
||||||
|
|
@ -51,50 +47,47 @@ function Folder:markers(area, markers)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:parent(area)
|
function Folder:parent(area)
|
||||||
local window = self:window(self.Kind.Parent)
|
local folder = self:by_kind(self.Kind.Parent)
|
||||||
if window == nil then
|
if folder == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local hovered = (self:hovered(self.Kind.Parent) or {}).url
|
local items = {}
|
||||||
local lines = {}
|
for _, f in ipairs(folder.window) do
|
||||||
for _, f in ipairs(window) do
|
local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ")
|
||||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
if f.hovered then
|
||||||
if f.url == hovered then
|
item = item:style(THEME.files.hovered)
|
||||||
line = line:style(THEME.files.hovered)
|
|
||||||
else
|
else
|
||||||
line = line:style(f:style())
|
item = item:style(f:style())
|
||||||
end
|
end
|
||||||
|
|
||||||
lines[#lines + 1] = line
|
items[#items + 1] = item
|
||||||
end
|
end
|
||||||
|
|
||||||
return { ui.Paragraph(area, lines) }
|
return { ui.List(area, items) }
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:current(area)
|
function Folder:current(area)
|
||||||
local hovered = (self:hovered(self.Kind.Current) or {}).url
|
|
||||||
local markers = {}
|
local markers = {}
|
||||||
local lines = {}
|
local items = {}
|
||||||
for i, f in ipairs(self:window(self.Kind.Current)) do
|
for i, f in ipairs(self:by_kind(self.Kind.Current).window) do
|
||||||
local name = f.name
|
local name = ui.highlight_ranges(f.name, f:highlights())
|
||||||
|
|
||||||
-- Show symlink target
|
-- Show symlink target
|
||||||
if MANAGER.show_symlink then
|
if MANAGER.show_symlink then
|
||||||
local link_to = f.link_to
|
if f.link_to ~= nil then
|
||||||
if link_to ~= nil then
|
name[#name + 1] = ui.Span(" -> " .. tostring(f.link_to)):italic()
|
||||||
name = name .. " -> " .. tostring(link_to)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Highlight hovered file
|
-- Highlight hovered file
|
||||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. name .. " ") }
|
local item = ui.ListItem(ui.Line { ui.Span(" " .. f:icon() .. " "), table.unpack(name) })
|
||||||
if f.url == hovered then
|
if f.hovered then
|
||||||
line = line:style(THEME.files.hovered)
|
item = item:style(THEME.files.hovered)
|
||||||
else
|
else
|
||||||
line = line:style(f:style())
|
item = item:style(f:style())
|
||||||
end
|
end
|
||||||
lines[#lines + 1] = line
|
items[#items + 1] = item
|
||||||
|
|
||||||
-- Mark selected/yanked files
|
-- Mark selected/yanked files
|
||||||
if f:selected() then
|
if f:selected() then
|
||||||
|
|
@ -102,28 +95,27 @@ function Folder:current(area)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return { ui.Paragraph(area, lines), table.unpack(self:markers(area, markers)) }
|
return { ui.List(area, items), table.unpack(self:markers(area, markers)) }
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:preview(area)
|
function Folder:preview(area)
|
||||||
local window = self:window(self.Kind.Preview)
|
local folder = self:by_kind(self.Kind.Preview)
|
||||||
if window == nil then
|
if folder == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local hovered = (self:hovered(self.Kind.Preview) or {}).url
|
local items = {}
|
||||||
local lines = {}
|
for _, f in ipairs(folder.window) do
|
||||||
for _, f in ipairs(window) do
|
local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ")
|
||||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
if f.hovered then
|
||||||
if f.url == hovered then
|
item = item:style(THEME.preview.hovered)
|
||||||
line = line:style(THEME.preview.hovered)
|
|
||||||
else
|
else
|
||||||
line = line:style(f:style())
|
item = item:style(f:style())
|
||||||
end
|
end
|
||||||
lines[#lines + 1] = line
|
items[#items + 1] = item
|
||||||
end
|
end
|
||||||
|
|
||||||
return { ui.Paragraph(area, lines) }
|
return { ui.List(area, items) }
|
||||||
end
|
end
|
||||||
|
|
||||||
function Folder:render(area, args)
|
function Folder:render(area, args)
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,24 @@ ui = {
|
||||||
VERTICAL = true,
|
VERTICAL = true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ui.highlight_ranges(s, ranges)
|
||||||
|
if ranges == nil or #ranges == 0 then
|
||||||
|
return { ui.Span(s) }
|
||||||
|
end
|
||||||
|
|
||||||
|
local spans = {}
|
||||||
|
local last = 0
|
||||||
|
for _, r in ipairs(ranges) do
|
||||||
|
if r[1] > last then
|
||||||
|
spans[#spans + 1] = ui.Span(s:sub(last + 1, r[1]))
|
||||||
|
end
|
||||||
|
-- TODO: use a customable style
|
||||||
|
spans[#spans + 1] = ui.Span(s:sub(r[1] + 1, r[2])):fg("yellow"):italic()
|
||||||
|
last = r[2]
|
||||||
|
end
|
||||||
|
if last < #s then
|
||||||
|
spans[#spans + 1] = ui.Span(s:sub(last + 1))
|
||||||
|
end
|
||||||
|
return spans
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,23 @@
|
||||||
use mlua::{MetaMethod, UserData, UserDataRef};
|
use mlua::{IntoLua, MetaMethod, UserData, UserDataRef};
|
||||||
|
|
||||||
|
// --- Range
|
||||||
|
pub struct Range<T>(std::ops::Range<T>);
|
||||||
|
|
||||||
|
impl<T> From<std::ops::Range<T>> for Range<T> {
|
||||||
|
fn from(value: std::ops::Range<T>) -> Self { Self(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T> IntoLua<'lua> for Range<T>
|
||||||
|
where
|
||||||
|
T: IntoLua<'lua>,
|
||||||
|
{
|
||||||
|
fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||||
|
let tbl = lua.create_sequence_from([self.0.start, self.0.end])?;
|
||||||
|
tbl.into_lua(lua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Url
|
||||||
pub struct Url(shared::Url);
|
pub struct Url(shared::Url);
|
||||||
|
|
||||||
impl From<&shared::Url> for Url {
|
impl From<&shared::Url> for Url {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use core::Ctx;
|
||||||
use config::{MANAGER, THEME};
|
use config::{MANAGER, THEME};
|
||||||
use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value};
|
use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value};
|
||||||
|
|
||||||
use super::Url;
|
use super::{Range, Url};
|
||||||
use crate::{layout::Style, LUA};
|
use crate::{layout::Style, LUA};
|
||||||
|
|
||||||
struct File(core::files::File);
|
struct File(core::files::File);
|
||||||
|
|
@ -89,9 +89,13 @@ impl<'a, 'b> Tab<'a, 'b> {
|
||||||
reg.add_function("style", |_, me: AnyUserData| {
|
reg.add_function("style", |_, me: AnyUserData| {
|
||||||
me.named_user_value::<Function>("style")?.call::<_, Style>(())
|
me.named_user_value::<Function>("style")?.call::<_, Style>(())
|
||||||
});
|
});
|
||||||
|
reg.add_field_function_get("hovered", |_, me| me.named_user_value::<bool>("hovered"));
|
||||||
reg.add_function("selected", |_, me: AnyUserData| {
|
reg.add_function("selected", |_, me: AnyUserData| {
|
||||||
me.named_user_value::<Function>("selected")?.call::<_, bool>(me)
|
me.named_user_value::<Function>("selected")?.call::<_, bool>(me)
|
||||||
});
|
});
|
||||||
|
reg.add_function("highlights", |_, me: AnyUserData| {
|
||||||
|
me.named_user_value::<Function>("highlights")?.call::<_, Value>(())
|
||||||
|
});
|
||||||
|
|
||||||
reg.add_field_method_get("url", |_, me| Ok(Url::from(me.url())));
|
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("length", |_, me| Ok(me.length()));
|
||||||
|
|
@ -203,6 +207,11 @@ impl<'a, 'b> Tab<'a, 'b> {
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
ud.set_named_user_value(
|
||||||
|
"hovered",
|
||||||
|
matches!(&folder.hovered, Some(f) if f.url() == inner.url()),
|
||||||
|
)?;
|
||||||
|
|
||||||
ud.set_named_user_value(
|
ud.set_named_user_value(
|
||||||
"selected",
|
"selected",
|
||||||
self.scope.create_function(|_, me: AnyUserData| {
|
self.scope.create_function(|_, me: AnyUserData| {
|
||||||
|
|
@ -217,6 +226,20 @@ impl<'a, 'b> Tab<'a, 'b> {
|
||||||
})?,
|
})?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
ud.set_named_user_value(
|
||||||
|
"highlights",
|
||||||
|
self.scope.create_function(|_, ()| {
|
||||||
|
let Some(finder) = self.inner.finder() else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(
|
||||||
|
inner
|
||||||
|
.name()
|
||||||
|
.map(|n| finder.highlighted(n).into_iter().map(Range::from).collect::<Vec<_>>()),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(ud)
|
Ok(ud)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,56 @@
|
||||||
use mlua::{Result, Table, TableExt};
|
use mlua::{AnyUserData, Table, TableExt};
|
||||||
use ratatui::layout;
|
|
||||||
|
|
||||||
use crate::{layout::{Paragraph, Rect}, GLOBALS, LUA};
|
use crate::{layout::{List, Paragraph, Rect}, GLOBALS, LUA};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn layout(values: Vec<AnyUserData>, buf: &mut ratatui::prelude::Buffer) -> mlua::Result<()> {
|
||||||
|
for value in values {
|
||||||
|
if let Ok(c) = value.take::<Paragraph>() {
|
||||||
|
c.render(buf)
|
||||||
|
} else if let Ok(c) = value.take::<List>() {
|
||||||
|
c.render(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Status
|
||||||
pub struct Status;
|
pub struct Status;
|
||||||
|
|
||||||
impl Status {
|
impl Status {
|
||||||
pub fn render(area: layout::Rect) -> Result<Vec<Paragraph>> {
|
pub fn render(
|
||||||
|
self,
|
||||||
|
area: ratatui::layout::Rect,
|
||||||
|
buf: &mut ratatui::prelude::Buffer,
|
||||||
|
) -> mlua::Result<()> {
|
||||||
let comp: Table = GLOBALS.get("Status")?;
|
let comp: Table = GLOBALS.get("Status")?;
|
||||||
comp.call_method::<_, _>("render", Rect(area))
|
let values: Vec<AnyUserData> = comp.call_method::<_, _>("render", Rect(area))?;
|
||||||
|
|
||||||
|
layout(values, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Folder
|
||||||
pub struct Folder {
|
pub struct Folder {
|
||||||
pub kind: u8,
|
pub kind: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Folder {
|
impl Folder {
|
||||||
fn args(&self) -> Result<Table> {
|
fn args(&self) -> mlua::Result<Table> {
|
||||||
let tbl = LUA.create_table()?;
|
let tbl = LUA.create_table()?;
|
||||||
tbl.set("kind", self.kind)?;
|
tbl.set("kind", self.kind)?;
|
||||||
Ok(tbl)
|
Ok(tbl)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self, area: layout::Rect) -> Result<Vec<Paragraph>> {
|
pub fn render(
|
||||||
|
self,
|
||||||
|
area: ratatui::layout::Rect,
|
||||||
|
buf: &mut ratatui::prelude::Buffer,
|
||||||
|
) -> mlua::Result<()> {
|
||||||
let comp: Table = GLOBALS.get("Folder")?;
|
let comp: Table = GLOBALS.get("Folder")?;
|
||||||
comp.call_method::<_, _>("render", (Rect(area), self.args()?))
|
let values: Vec<AnyUserData> =
|
||||||
|
comp.call_method::<_, _>("render", (Rect(area), self.args()?))?;
|
||||||
|
|
||||||
|
layout(values, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
117
plugin/src/layout/list.rs
Normal file
117
plugin/src/layout/list.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, Value};
|
||||||
|
use ratatui::widgets::Widget;
|
||||||
|
|
||||||
|
use super::{Line, Rect, Span, Style};
|
||||||
|
use crate::{GLOBALS, LUA};
|
||||||
|
|
||||||
|
// --- List
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct List {
|
||||||
|
area: ratatui::layout::Rect,
|
||||||
|
|
||||||
|
inner: ratatui::widgets::List<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
pub(crate) fn install() -> mlua::Result<()> {
|
||||||
|
let ui: Table = GLOBALS.get("ui")?;
|
||||||
|
ui.set(
|
||||||
|
"List",
|
||||||
|
LUA.create_function(|_, (area, items): (Rect, Vec<ListItem>)| {
|
||||||
|
let items: Vec<_> = items.into_iter().map(|x| x.into()).collect();
|
||||||
|
Ok(Self { area: area.0, inner: ratatui::widgets::List::new(items) })
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
||||||
|
self.inner.render(self.area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for List {
|
||||||
|
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: "List",
|
||||||
|
message: Some("expected a List".to_string()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for List {}
|
||||||
|
|
||||||
|
// --- ListItem
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct ListItem {
|
||||||
|
content: ratatui::text::Text<'static>,
|
||||||
|
style: Option<ratatui::style::Style>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListItem {
|
||||||
|
pub(crate) fn install() -> mlua::Result<()> {
|
||||||
|
let ui: Table = GLOBALS.get("ui")?;
|
||||||
|
ui.set(
|
||||||
|
"ListItem",
|
||||||
|
LUA.create_function(|_, value: Value| {
|
||||||
|
match value {
|
||||||
|
Value::UserData(ud) => {
|
||||||
|
let content: ratatui::text::Text = if let Ok(line) = ud.take::<Line>() {
|
||||||
|
line.0.into()
|
||||||
|
} else if let Ok(span) = ud.take::<Span>() {
|
||||||
|
span.0.into()
|
||||||
|
} else {
|
||||||
|
return Err(mlua::Error::external("expected a String, Line or Span"));
|
||||||
|
};
|
||||||
|
return Ok(Self { content, style: None });
|
||||||
|
}
|
||||||
|
Value::String(s) => {
|
||||||
|
return Ok(Self { content: s.to_str()?.to_string().into(), style: None });
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Err(mlua::Error::external("expected a String, Line or Span"))
|
||||||
|
})?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ListItem> for ratatui::widgets::ListItem<'static> {
|
||||||
|
fn from(value: ListItem) -> Self {
|
||||||
|
let mut item = Self::new(value.content);
|
||||||
|
if let Some(style) = value.style {
|
||||||
|
item = item.style(style)
|
||||||
|
}
|
||||||
|
item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for ListItem {
|
||||||
|
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: "ListItem",
|
||||||
|
message: Some("expected a ListItem".to_string()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for ListItem {
|
||||||
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
|
||||||
|
ud.borrow_mut::<Self>()?.style = match value {
|
||||||
|
Value::Nil => None,
|
||||||
|
Value::Table(tbl) => Some(Style::from(tbl).0),
|
||||||
|
Value::UserData(ud) => Some(ud.borrow::<Style>()?.0),
|
||||||
|
_ => return Err(mlua::Error::external("expected a Style or Table or nil")),
|
||||||
|
};
|
||||||
|
Ok(ud)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
mod constraint;
|
mod constraint;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod line;
|
mod line;
|
||||||
|
mod list;
|
||||||
mod paragraph;
|
mod paragraph;
|
||||||
mod rect;
|
mod rect;
|
||||||
mod span;
|
mod span;
|
||||||
|
|
@ -11,6 +12,7 @@ mod style;
|
||||||
pub(super) use constraint::*;
|
pub(super) use constraint::*;
|
||||||
pub(super) use layout::*;
|
pub(super) use layout::*;
|
||||||
pub(super) use line::*;
|
pub(super) use line::*;
|
||||||
|
pub(super) use list::*;
|
||||||
pub(super) use paragraph::*;
|
pub(super) use paragraph::*;
|
||||||
pub(super) use rect::*;
|
pub(super) use rect::*;
|
||||||
pub(super) use span::*;
|
pub(super) use span::*;
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl Paragraph {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
pub(crate) fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
||||||
let mut p = ratatui::widgets::Paragraph::new(self.text);
|
let mut p = ratatui::widgets::Paragraph::new(self.text);
|
||||||
if let Some(style) = self.style {
|
if let Some(style) = self.style {
|
||||||
p = p.style(style);
|
p = p.style(style);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ pub fn init() {
|
||||||
layout::Constraint::install()?;
|
layout::Constraint::install()?;
|
||||||
layout::Layout::install()?;
|
layout::Layout::install()?;
|
||||||
layout::Line::install()?;
|
layout::Line::install()?;
|
||||||
|
layout::List::install()?;
|
||||||
|
layout::ListItem::install()?;
|
||||||
layout::Paragraph::install()?;
|
layout::Paragraph::install()?;
|
||||||
layout::Rect::install()?;
|
layout::Rect::install()?;
|
||||||
layout::Span::install()?;
|
layout::Span::install()?;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
use core::Ctx;
|
use core::Ctx;
|
||||||
|
|
||||||
|
pub use mlua::Scope;
|
||||||
|
|
||||||
use crate::{bindings, GLOBALS, LUA};
|
use crate::{bindings, GLOBALS, LUA};
|
||||||
|
|
||||||
pub fn scope<F: FnOnce()>(cx: &Ctx, f: F) {
|
pub fn scope<'a>(cx: &'a Ctx, f: impl FnOnce(&Scope<'a, 'a>)) {
|
||||||
let _ = LUA.scope(|scope| {
|
let _ = LUA.scope(|scope| {
|
||||||
let tbl = LUA.create_table()?;
|
let tbl = LUA.create_table()?;
|
||||||
tbl.set("active", bindings::Tab::new(scope, cx, cx.manager.active()).make()?)?;
|
tbl.set("active", bindings::Tab::new(scope, cx, cx.manager.active()).make()?)?;
|
||||||
tbl.set("tasks", bindings::Tasks::make(scope, &cx.tasks)?)?;
|
tbl.set("tasks", bindings::Tasks::make(scope, &cx.tasks)?)?;
|
||||||
GLOBALS.set("cx", tbl)?;
|
GLOBALS.set("cx", tbl)?;
|
||||||
|
|
||||||
Ok(f())
|
Ok(f(scope))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,9 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
|
||||||
#[allow(clippy::collapsible_else_if)]
|
#[allow(clippy::collapsible_else_if)]
|
||||||
pub fn permissions(permissions: Permissions) -> Option<String> {
|
pub fn permissions(permissions: Permissions) -> Option<String> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
return None;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
Some({
|
Some({
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
use std::os::unix::prelude::PermissionsExt;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue