This commit is contained in:
sxyazi 2023-09-19 09:24:58 +08:00
parent 9ff11b2dbc
commit befcd555f9
No known key found for this signature in database
18 changed files with 299 additions and 262 deletions

View file

@ -53,6 +53,25 @@ impl Parser {
let mut last = '\0'; let mut last = '\0';
let mut lines: Vec<String> = vec![String::new()]; let mut lines: Vec<String> = vec![String::new()];
for c in s.chars() {
if c == '\r' && last == '\\' {
let last = lines.last_mut().unwrap();
last.pop();
last.push(c);
} else if c == '\r' {
lines.push(String::new());
} else {
lines.last_mut().unwrap().push(c);
}
last = c;
}
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
}
pub fn layout(s: &str) -> Paragraph {
let mut last = '\0';
let mut lines: 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 = lines.last_mut().unwrap();

View file

@ -47,5 +47,36 @@ impl<'a> Widget for Layout<'a> {
} }
Paragraph::new(Line::from(spans)).render(chunks[0], buf); 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,46 +0,0 @@
use core::Ctx;
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
pub(super) struct Left<'a> {
cx: &'a Ctx,
}
impl<'a> Left<'a> {
pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
}
impl<'a> Widget for Left<'a> {
fn render(self, area: Rect, buf: &mut Buffer) {
// let folder = self.cx.manager.current();
// let mode = self.cx.manager.active().mode();
//
// // Separator
// let separator = &THEME.status.separator;
//
// // Mode
// let mut spans = Vec::with_capacity(5);
// spans.push(Span::styled(&separator.opening, primary.fg()));
// spans.push(Span::styled(
// format!(" {mode} "),
// primary.bg().fg(**secondary).add_modifier(Modifier::BOLD),
// ));
//
// if let Some(h) = &folder.hovered {
// // Length
// {
// let size = if h.is_dir() { folder.files.size(h.url()) } else { None };
// spans.push(Span::styled(
// format!(" {} ", readable_size(size.unwrap_or(h.length()))),
// body.bg().fg(**primary),
// ));
// spans.push(Span::styled(&separator.closing, body.fg()));
// }
//
// // Filename
// spans.push(Span::raw(format!(" {} ", h.name_display().unwrap())));
// }
//
// Paragraph::new(Line::from(spans)).render(area, buf);
}
}

View file

@ -1,9 +1,4 @@
mod layout; mod layout;
mod left;
mod progress; mod progress;
mod right;
pub(super) use layout::*; pub(super) use layout::*;
use left::*;
use progress::*;
use right::*;

View file

@ -1,96 +0,0 @@
use core::Ctx;
use config::THEME;
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
use super::Progress;
pub(super) struct Right<'a> {
cx: &'a Ctx,
}
// impl<'a> Right<'a> {
// pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
//
// #[cfg(not(target_os = "windows"))]
// fn permissions(&self, s: &str) -> Vec<Span> {
// // Colors
// let mode = self.cx.manager.active().mode();
// let tertiary = mode.color(&THEME.status.tertiary);
// let info = mode.color(&THEME.status.info);
// let success = mode.color(&THEME.status.success);
// let warning = mode.color(&THEME.status.warning);
// let danger = mode.color(&THEME.status.danger);
//
// s.chars()
// .map(|c| match c {
// '-' => Span::styled("-", tertiary.fg()),
// 'r' => Span::styled("r", warning.fg()),
// 'w' => Span::styled("w", danger.fg()),
// 'x' | 's' | 'S' | 't' | 'T' => Span::styled(c.to_string(), info.fg()),
// _ => Span::styled(c.to_string(), success.fg()),
// })
// .collect()
// }
//
// fn position(&self) -> Vec<Span> {
// // Colors
// let mode = self.cx.manager.active().mode();
// let primary = mode.color(&THEME.status.primary);
// let secondary = mode.color(&THEME.status.secondary);
// let body = mode.color(&THEME.status.body);
//
// // Separator
// let separator = &THEME.status.separator;
//
// let cursor = self.cx.manager.current().cursor();
// let length = self.cx.manager.current().files.len();
// let percent = if cursor == 0 || length == 0 { 0 } else { (cursor + 1) * 100 /
// length };
//
// vec![
// Span::raw(" "),
// Span::styled(&separator.opening, body.fg()),
// Span::styled(
// if percent == 0 { " Top ".to_string() } else { format!(" {:>3}% ", percent)
// }, body.bg().fg(**primary),
// ),
// Span::styled(
// format!(" {:>2}/{:<2} ", (cursor + 1).min(length), length),
// primary.bg().fg(**secondary),
// ),
// Span::styled(&separator.closing, primary.fg()),
// ]
// }
// }
impl Widget for Right<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
// let manager = self.cx.manager.current();
// let mut spans = Vec::with_capacity(20);
//
// // Permissions
// #[cfg(not(target_os = "windows"))]
// if let Some(h) = &manager.hovered {
// use std::os::unix::prelude::PermissionsExt;
// spans.extend(self.permissions(&shared::file_mode(h.meta().permissions().
// mode()))) }
//
// // Position
// spans.extend(self.position());
//
// // Progress
// let line = Line::from(spans);
// Progress::new(self.cx).render(
// Rect {
// x: area.x + area.width.saturating_sub(21 + line.width() as u16),
// y: area.y,
// width: 20.min(area.width),
// height: 1,
// },
// buf,
// );
//
// Paragraph::new(line).alignment(Alignment::Right).render(area, buf);
}
}

View file

@ -17,6 +17,12 @@ mode_unset = { fg = "#1E1E30", bg = "#FFAF80", bold = true }
progress_label = { fg = "#FFFFFF", bold = true } progress_label = { fg = "#FFFFFF", bold = true }
progress_gauge = { fg = "#FFA577", bg = "#484D66" } progress_gauge = { fg = "#FFA577", bg = "#484D66" }
# Permissions
permissions_t = { fg = "#6D738F" }
permissions_r = { fg = "#F3D398" }
permissions_w = { fg = "#FA7F94" }
permissions_x = { fg = "#7AD9E5" }
[selection] [selection]
hovered = { fg = "#1E2031", bg = "#80AEFA" } hovered = { fg = "#1E2031", bg = "#80AEFA" }

View file

@ -16,6 +16,12 @@ pub struct Status {
// Progress // Progress
pub progress_label: Style, pub progress_label: Style,
pub progress_gauge: Style, pub progress_gauge: Style,
// Permissions
pub permissions_t: Style,
pub permissions_r: Style,
pub permissions_w: Style,
pub permissions_x: Style,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]

View file

@ -81,4 +81,8 @@ impl File {
#[inline] #[inline]
pub fn is_link(&self) -> bool { self.is_link } pub fn is_link(&self) -> bool { self.is_link }
// -- Is hidden
#[inline]
pub fn is_hidden(&self) -> bool { self.is_hidden }
} }

View file

@ -0,0 +1,92 @@
function layout() end
function mode()
local mode = cx.manager.mode:upper()
if mode == "UNSET" then
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()
end
function size()
local hovered = cx.manager.current_hovered
if hovered == nil then
return ""
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()
end
function name()
local hovered = cx.manager.current_hovered
if hovered == nil then
return ""
end
return yazi.Span(" " .. utils.basename(hovered.url)):to_string()
end
function permissions()
local hovered = cx.manager.current_hovered
if hovered == nil then
return ""
end
if hovered.permissions == nil then
return ""
end
local spans = {}
for i = 1, #hovered.permissions do
local c = hovered.permissions:sub(i, i)
local style = THEME.status.permissions_t
if c == "r" then
style = THEME.status.permissions_r
elseif c == "w" then
style = THEME.status.permissions_w
elseif c == "x" or c == "s" or c == "S" or c == "t" or c == "T" then
style = THEME.status.permissions_x
end
spans[i] = yazi.Span(c):style(style)
end
return yazi.Line:from(spans):to_string()
end
function percentage()
local percent = 0
local cursor = cx.manager.current_cursor
local length = cx.manager.current_length
if cursor ~= 0 and length ~= 0 then
percent = math.floor((cursor + 1) * 100 / length)
end
if percent == 0 then
percent = " Top "
else
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()
end
function position()
local cursor = cx.manager.current_cursor
local length = cx.manager.current_length
return string.format(" %d/%d ", cursor + 1, length)
end

View file

@ -14,7 +14,7 @@ function Paragraph:from(lines) return self:new(table.unpack(lines)) 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
s = s .. line:to_string():gsub("\0", "\\\0") .. "\0" s = s .. line:to_string():gsub("\r", "\\\r") .. "\r"
end end
return s.sub(s, 1, -2) return s.sub(s, 1, -2)
end end

43
plugin/preset/layout.lua Normal file
View file

@ -0,0 +1,43 @@
local Layout = {}
function Layout:new(...)
local o = {
direction = "R",
dimension = {},
elements = { ... },
}
setmetatable(o, self)
self.__index = self
return o
end
function Layout:from(elements) return self:new(table.unpack(elements)) end
function Layout:rows(rows)
self.direction = "R"
self.dimension = rows
return self:to_string()
end
function Layout:cols(cols)
self.direction = "C"
self.dimension = cols
return self:to_string()
end
function Layout:to_string()
local s = ""
for i, element in ipairs(self.elements) do
if i == 1 then
s = s .. self.direction
end
s = s .. self.dimension[i] .. "\0" .. element:to_string():gsub("\0", "\\\0")
end
end
setmetatable(Layout, {
__call = function(self, ...) return self:new(...) end,
})
yazi = yazi or {}
yazi.Layout = Layout

View file

@ -1,42 +0,0 @@
function mode()
local mode = cx.manager.mode:upper()
if mode == "UNSET" then
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()
end
function size()
local hovered = cx.manager.hovered
if hovered == nil then
return ""
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()
end
function name()
local hovered = cx.manager.hovered
if hovered == nil then
return ""
end
return yazi.Span(" " .. yazi.basename(hovered.url)):to_string()
end
function permissions() end
function percentage() end
function position() end

View file

@ -1,3 +1,3 @@
yazi = yazi or {} utils = utils or {}
function yazi.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end function utils.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end

View file

@ -1,5 +1,4 @@
use mlua::{LuaSerdeExt, UserData, Value}; use mlua::{LuaSerdeExt, UserData};
use shared::Url;
// Manager // Manager
@ -13,9 +12,11 @@ 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", |_, this| Ok(this.0.active().mode().to_string()));
fields.add_field_method_get("hovered", |_, this| { 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)) Ok(this.0.current().hovered.as_ref().map(File::from))
}) });
} }
} }
@ -35,31 +36,25 @@ impl<'a> UserData for Tasks<'a> {
// File // File
pub struct File { pub struct File(core::files::File);
pub(super) url: Url,
pub(super) length: u64,
pub(super) link_to: Option<Url>,
pub(super) is_link: bool,
}
impl From<&core::files::File> for File { impl From<&core::files::File> for File {
fn from(value: &core::files::File) -> Self { fn from(value: &core::files::File) -> Self { Self(value.clone()) }
Self {
url: value.url_owned(),
length: value.length(),
link_to: value.link_to().cloned(),
is_link: value.is_link(),
}
}
} }
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.url.to_string_lossy().to_string())); 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.length)); fields.add_field_method_get("length", |_, this| Ok(this.0.length()));
fields.add_field_method_get("link_to", |_, this| { fields.add_field_method_get("link_to", |_, this| {
Ok(this.link_to.as_ref().map(|l| l.to_string_lossy().to_string())) Ok(this.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()));
// Meta
fields.add_field_method_get("permissions", |_, this| {
Ok(shared::permissions(this.0.meta().permissions()))
}); });
fields.add_field_method_get("is_link", |_, this| Ok(this.is_link));
} }
} }

View file

@ -41,4 +41,25 @@ impl Status {
size.call::<_, String>(()) 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

@ -1,4 +1,4 @@
use std::{collections::VecDeque, path::{Path, PathBuf}}; use std::{collections::VecDeque, fs::Permissions, path::{Path, PathBuf}};
use anyhow::Result; use anyhow::Result;
use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time};
@ -92,61 +92,70 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
} }
// Convert a file mode to a string representation // Convert a file mode to a string representation
#[cfg(not(target_os = "windows"))] #[cfg(unix)]
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]
pub fn file_mode(mode: u32) -> String { pub fn permissions(permissions: Permissions) -> Option<String> {
use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR}; #[cfg(windows)]
{
return None;
}
#[cfg(target_os = "macos")] Some({
let m = mode as u16; use std::os::unix::prelude::PermissionsExt;
#[cfg(target_os = "freebsd")]
let m = mode as u16;
#[cfg(target_os = "netbsd")]
let m = mode;
#[cfg(target_os = "linux")]
let m = mode;
let mut s = String::with_capacity(10); use libc::{S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFSOCK, S_IRGRP, S_IROTH, S_IRUSR, S_ISGID, S_ISUID, S_ISVTX, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};
// File type #[cfg(target_os = "macos")]
s.push(match m & S_IFMT { let m = permissions.mode() as u16;
S_IFBLK => 'b', #[cfg(target_os = "freebsd")]
S_IFCHR => 'c', let m = permissions.mode() as u16;
S_IFDIR => 'd', #[cfg(target_os = "netbsd")]
S_IFIFO => 'p', let m = permissions.mode();
S_IFLNK => 'l', #[cfg(target_os = "linux")]
S_IFSOCK => 's', let m = permissions.mode();
_ => '-',
});
// Owner let mut s = String::with_capacity(10);
s.push(if m & S_IRUSR != 0 { 'r' } else { '-' });
s.push(if m & S_IWUSR != 0 { 'w' } else { '-' });
s.push(if m & S_IXUSR != 0 {
if m & S_ISUID != 0 { 's' } else { 'x' }
} else {
if m & S_ISUID != 0 { 'S' } else { '-' }
});
// Group // File type
s.push(if m & S_IRGRP != 0 { 'r' } else { '-' }); s.push(match m & S_IFMT {
s.push(if m & S_IWGRP != 0 { 'w' } else { '-' }); S_IFBLK => 'b',
s.push(if m & S_IXGRP != 0 { S_IFCHR => 'c',
if m & S_ISGID != 0 { 's' } else { 'x' } S_IFDIR => 'd',
} else { S_IFIFO => 'p',
if m & S_ISGID != 0 { 'S' } else { '-' } S_IFLNK => 'l',
}); S_IFSOCK => 's',
_ => '-',
});
// Other // Owner
s.push(if m & S_IROTH != 0 { 'r' } else { '-' }); s.push(if m & S_IRUSR != 0 { 'r' } else { '-' });
s.push(if m & S_IWOTH != 0 { 'w' } else { '-' }); s.push(if m & S_IWUSR != 0 { 'w' } else { '-' });
s.push(if m & S_IXOTH != 0 { s.push(if m & S_IXUSR != 0 {
if m & S_ISVTX != 0 { 't' } else { 'x' } if m & S_ISUID != 0 { 's' } else { 'x' }
} else { } else {
if m & S_ISVTX != 0 { 'T' } else { '-' } if m & S_ISUID != 0 { 'S' } else { '-' }
}); });
s // Group
s.push(if m & S_IRGRP != 0 { 'r' } else { '-' });
s.push(if m & S_IWGRP != 0 { 'w' } else { '-' });
s.push(if m & S_IXGRP != 0 {
if m & S_ISGID != 0 { 's' } else { 'x' }
} else {
if m & S_ISGID != 0 { 'S' } else { '-' }
});
// Other
s.push(if m & S_IROTH != 0 { 'r' } else { '-' });
s.push(if m & S_IWOTH != 0 { 'w' } else { '-' });
s.push(if m & S_IXOTH != 0 {
if m & S_ISVTX != 0 { 't' } else { 'x' }
} else {
if m & S_ISVTX != 0 { 'T' } else { '-' }
});
s
})
} }
// Find the max common root of a list of files // Find the max common root of a list of files