This commit is contained in:
sxyazi 2023-10-05 18:20:35 +08:00
parent a3971fc4f5
commit 46c54f6bad
No known key found for this signature in database
26 changed files with 175 additions and 139 deletions

View file

@ -1,4 +1,4 @@
use core::{emit, files::FilesSorter, input::InputMode, manager::FinderCase}; use core::{emit, files::FilesSorter, input::InputMode, manager::FinderCase, Ctx};
use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP}; use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP};
use shared::{optional_bool, Url}; use shared::{optional_bool, Url};

View file

@ -17,7 +17,7 @@ impl<'a> Widget for Layout<'a> {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let chunks = layout::Layout::new() let chunks = layout::Layout::new()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(area); .split(area);
let cwd = &self.cx.manager.current().cwd; let cwd = &self.cx.manager.current().cwd;

View file

@ -16,7 +16,7 @@ impl<'a> Widget for Layout<'a> {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let chunks = layout::Layout::new() let chunks = layout::Layout::new()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(1)].as_ref()) .constraints([Constraint::Min(0), Constraint::Length(1)])
.split(area); .split(area);
Clear.render(area, buf); Clear.render(area, buf);

View file

@ -1,10 +1,7 @@
use core::Ctx;
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
use tracing::info; use tracing::info;
pub(super) struct Folder<'a> { pub(super) struct Folder {
cx: &'a Ctx,
kind: FolderKind, kind: FolderKind,
} }
@ -14,11 +11,11 @@ pub(super) enum FolderKind {
Preview = 2, Preview = 2,
} }
impl<'a> Folder<'a> { impl Folder {
pub(super) fn new(cx: &'a Ctx, kind: FolderKind) -> Self { Self { cx, kind } } pub(super) fn new(kind: FolderKind) -> Self { Self { kind } }
} }
impl<'a> Folder<'a> { impl Folder {
// fn highlighted_item<'b>(&'b self, file: &'b File) -> Vec<Span> { // 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);
// //
@ -39,7 +36,7 @@ impl<'a> Folder<'a> {
// } // }
} }
impl<'a> Widget for Folder<'a> { 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);
if x.is_err() { if x.is_err() {
@ -55,21 +52,6 @@ impl<'a> Widget for Folder<'a> {
// .iter() // .iter()
// .enumerate() // .enumerate()
// .map(|(i, f)| { // .map(|(i, f)| {
// let is_selected = self.folder.files.is_selected(f.url());
// if (!self.is_selection && is_selected)
// || (self.is_selection && mode.pending(self.folder.offset() + i, is_selected))
// {
// buf.set_style(
// Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1
// }, if self.is_selection {
// THEME.marker.selecting.get()
// } else {
// THEME.marker.selected.get()
// },
// );
// }
//
//
// if let Some(idx) = active // if let Some(idx) = active
// .finder() // .finder()
// .filter(|&f| hovered && self.is_find && f.has_matched()) // .filter(|&f| hovered && self.is_find && f.has_matched())

View file

@ -20,25 +20,22 @@ impl<'a> Widget for Layout<'a> {
let chunks = layout::Layout::new() let chunks = layout::Layout::new()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints( .constraints([
[ Constraint::Ratio(layout.parent, layout.all),
Constraint::Ratio(layout.parent, layout.all), Constraint::Ratio(layout.current, layout.all),
Constraint::Ratio(layout.current, layout.all), Constraint::Ratio(layout.preview, layout.all),
Constraint::Ratio(layout.preview, layout.all), ])
]
.as_ref(),
)
.split(area); .split(area);
// Parent // Parent
let block = Block::new().borders(Borders::RIGHT).padding(Padding::new(1, 0, 0, 0)); let block = Block::new().borders(Borders::RIGHT).padding(Padding::new(1, 0, 0, 0));
if manager.parent().is_some() { if manager.parent().is_some() {
Folder::new(self.cx, FolderKind::Parent).render(block.inner(chunks[0]), buf); Folder::new(FolderKind::Parent).render(block.inner(chunks[0]), buf);
} }
block.render(chunks[0], buf); block.render(chunks[0], buf);
// Current // Current
Folder::new(self.cx, FolderKind::Current).render(chunks[1], buf); Folder::new(FolderKind::Current).render(chunks[1], buf);
// Preview // Preview
let block = Block::new().borders(Borders::LEFT).padding(Padding::new(0, 1, 0, 0)); let block = Block::new().borders(Borders::LEFT).padding(Padding::new(0, 1, 0, 0));

View file

@ -27,7 +27,7 @@ impl<'a> Widget for Preview<'a> {
match &preview.lock.as_ref().unwrap().data { match &preview.lock.as_ref().unwrap().data {
PreviewData::Folder => { PreviewData::Folder => {
Folder::new(self.cx, FolderKind::Preview).render(area, buf); Folder::new(FolderKind::Preview).render(area, buf);
} }
PreviewData::Text(s) => { PreviewData::Text(s) => {
let p = Paragraph::new(s.as_bytes().into_text().unwrap()); let p = Paragraph::new(s.as_bytes().into_text().unwrap());

View file

@ -17,12 +17,12 @@ impl<'a> Widget for Root<'a> {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
let chunks = Layout::new() let chunks = Layout::new()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Length(1), Constraint::Min(0), Constraint::Length(1)].as_ref()) .constraints([Constraint::Length(1), Constraint::Min(0), Constraint::Length(1)])
.split(area); .split(area);
header::Layout::new(self.cx).render(chunks[0], buf); header::Layout::new(self.cx).render(chunks[0], buf);
manager::Layout::new(self.cx).render(chunks[1], buf); manager::Layout::new(self.cx).render(chunks[1], buf);
status::Layout::new(self.cx).render(chunks[2], buf); status::Layout.render(chunks[2], buf);
if self.cx.tasks.visible { if self.cx.tasks.visible {
tasks::Layout::new(self.cx).render(area, buf); tasks::Layout::new(self.cx).render(area, buf);

View file

@ -1,17 +1,9 @@
use core::Ctx;
use ratatui::{buffer::Buffer, prelude::Rect, widgets::Widget}; use ratatui::{buffer::Buffer, prelude::Rect, widgets::Widget};
use tracing::info; use tracing::info;
pub(crate) struct Layout<'a> { pub(crate) struct Layout;
cx: &'a Ctx,
}
impl<'a> Layout<'a> { impl Widget for Layout {
pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } }
}
impl<'a> Widget for Layout<'a> {
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);
if x.is_err() { if x.is_err() {

View file

@ -14,29 +14,21 @@ impl<'a> Layout<'a> {
pub(super) fn area(area: Rect) -> Rect { pub(super) fn area(area: Rect) -> Rect {
let chunk = layout::Layout::new() let chunk = layout::Layout::new()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints( .constraints([
[ Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage((100 - TASKS_PERCENT) / 2), Constraint::Percentage(TASKS_PERCENT),
Constraint::Percentage(TASKS_PERCENT), Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage((100 - TASKS_PERCENT) / 2), ])
]
.as_ref(),
)
.split(area)[1]; .split(area)[1];
let chunk = layout::Layout::new() layout::Layout::new()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints( .constraints([
[ Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage((100 - TASKS_PERCENT) / 2), Constraint::Percentage(TASKS_PERCENT),
Constraint::Percentage(TASKS_PERCENT), Constraint::Percentage((100 - TASKS_PERCENT) / 2),
Constraint::Percentage((100 - TASKS_PERCENT) / 2), ])
] .split(chunk)[1]
.as_ref(),
)
.split(chunk)[1];
chunk
} }
} }

View file

@ -35,9 +35,7 @@ impl Widget for Which<'_> {
let chunks = layout::Layout::new() let chunks = layout::Layout::new()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints( .constraints([Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3)])
[Constraint::Ratio(1, 3), Constraint::Ratio(1, 3), Constraint::Ratio(1, 3)].as_ref(),
)
.split(area); .split(area);
Clear.render(area, buf); Clear.render(area, buf);

View file

@ -23,12 +23,13 @@ permissions_r = { fg = "#F3D398" }
permissions_w = { fg = "#FA7F94" } permissions_w = { fg = "#FA7F94" }
permissions_x = { fg = "#7AD9E5" } permissions_x = { fg = "#7AD9E5" }
[selection] [files]
hovered = { fg = "#1E2031", bg = "#80AEFA" } hovered = { fg = "#1E2031", bg = "#80AEFA" }
[marker] [marker]
selecting = { fg = "#97DC8D", bg = "#97DC8D" } selected = { fg = "#97DC8D", bg = "#97DC8D" }
selected = { fg = "#F3D398", bg = "#F3D398" } copied = { fg = "#F3D398", bg = "#F3D398" }
cut = { fg = "#FF84A9", bg = "#FF84A9" }
[preview] [preview]
hovered = { underline = true } hovered = { underline = true }

View file

@ -3,12 +3,13 @@ use serde::{Deserialize, Serialize};
use super::Style; use super::Style;
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Selection { pub struct Files {
pub hovered: Style, pub hovered: Style,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct Marker { pub struct Marker {
pub selecting: Style, pub selected: Style,
pub selected: Style, pub copied: Style,
pub cut: Style,
} }

View file

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use shared::expand_path; use shared::expand_path;
use validator::Validate; use validator::Validate;
use super::{Filetype, Icon, Marker, Selection, Status, Style}; use super::{Files, Filetype, Icon, Marker, Status, Style};
use crate::{validation::check_validation, MERGED_THEME}; use crate::{validation::check_validation, MERGED_THEME};
#[derive(Deserialize, Serialize, Validate)] #[derive(Deserialize, Serialize, Validate)]
@ -25,7 +25,7 @@ pub struct Preview {
pub struct Theme { pub struct Theme {
pub tab: Tab, pub tab: Tab,
pub status: Status, pub status: Status,
pub selection: Selection, pub files: Files,
pub marker: Marker, pub marker: Marker,
pub preview: Preview, pub preview: Preview,
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]

View file

@ -10,9 +10,9 @@ use super::{Backstack, Finder, FinderCase, Folder, Mode, Preview, PreviewLock};
use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER}; use crate::{emit, external::{self, FzfOpt, ZoxideOpt}, files::{File, FilesOp, FilesSorter}, input::InputOpt, Event, Step, BLOCKER};
pub struct Tab { pub struct Tab {
pub(super) mode: Mode, pub mode: Mode,
pub(super) current: Folder, pub current: Folder,
pub(super) parent: Option<Folder>, pub parent: Option<Folder>,
pub(super) backstack: Backstack<Url>, pub(super) backstack: Backstack<Url>,
pub(super) history: BTreeMap<Url, Folder>, pub(super) history: BTreeMap<Url, Folder>,
@ -455,14 +455,10 @@ impl Tab {
impl Tab { impl Tab {
// --- Mode // --- Mode
#[inline] #[inline]
pub fn mode(&self) -> &Mode { &self.mode } pub fn in_selecting(&self) -> bool { self.mode.is_visual() || self.current.files.has_selected() }
#[inline]
pub fn in_selecting(&self) -> bool {
self.mode().is_visual() || self.current.files.has_selected()
}
// --- Current // --- Current
// TODO: remove this
#[inline] #[inline]
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
self self
@ -475,10 +471,9 @@ impl Tab {
} }
pub fn selected(&self) -> Vec<&File> { pub fn selected(&self) -> Vec<&File> {
let mode = self.mode(); let pending = self.mode.visual().map(|(_, p)| Cow::Borrowed(p)).unwrap_or_default();
let pending = mode.visual().map(|(_, p)| Cow::Borrowed(p)).unwrap_or_default(); let selected = self.current.files.selected(&pending, self.mode.is_unset());
let selected = self.current.files.selected(&pending, mode.is_unset());
if selected.is_empty() { if selected.is_empty() {
self.current.hovered.as_ref().map(|h| vec![h]).unwrap_or_default() self.current.hovered.as_ref().map(|h| vec![h]).unwrap_or_default()
} else { } else {

View file

@ -1 +1 @@
{"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","metatable","natsort"]} {"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","metatable","natsort","backstack","luajit"],"language":"en","version":"0.2"}

View file

@ -10,6 +10,6 @@ shared = { path = "../shared" }
# External dependencies # External dependencies
anyhow = "^1" anyhow = "^1"
mlua = { version = "^0", features = [ "lua54", "vendored", "serialize" ] } mlua = { version = "^0", features = [ "luajit52", "vendored", "serialize" ] }
tracing = "^0" tracing = "^0"
ratatui = "^0" ratatui = "^0"

View file

@ -8,11 +8,11 @@ Folder = {
function Folder:by_kind(kind) function Folder:by_kind(kind)
if kind == self.Kind.Parent then if kind == self.Kind.Parent then
return cx.manager.parent return cx.active.parent
elseif kind == self.Kind.Current then elseif kind == self.Kind.Current then
return cx.manager.current return cx.active.current
elseif kind == self.Kind.Preview then elseif kind == self.Kind.Preview then
return cx.manager.preview.folder return cx.active.preview.folder
end end
end end
@ -20,6 +20,36 @@ 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:hovered(kind) return (self:by_kind(kind) or {}).hovered end
function Folder:markers(area, markers)
if #markers == 0 then
return {}
end
local elements = {}
local append = function(last)
local rect = ui.Rect {
x = area.x - 1,
y = area.y + last[1] - 1,
w = 1,
h = 1 + last[2] - last[1],
}
elements[#elements + 1] = ui.Paragraph(rect, {}):style(THEME.marker.selected)
end
local last = { markers[1][1], markers[1][1], markers[1][2] } -- start, end, type
for _, m in ipairs(markers) do
if m[1] - last[2] > 1 or last[3] ~= m[2] then
append(last)
last = { m[1], m[1], m[2] }
else
last[2] = m[1]
end
end
append(last)
return elements
end
function Folder:parent(area) function Folder:parent(area)
local window = self:window(self.Kind.Parent) local window = self:window(self.Kind.Parent)
if window == nil then if window == nil then
@ -28,10 +58,10 @@ function Folder:parent(area)
local hovered = (self:hovered(self.Kind.Parent) or {}).url local hovered = (self:hovered(self.Kind.Parent) or {}).url
local lines = {} local lines = {}
for _, f in pairs(window) do for _, f in ipairs(window) do
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") } local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
if f.url == hovered then if f.url == hovered then
line = line:style(THEME.selection.hovered) line = line:style(THEME.files.hovered)
else else
line = line:style(f:style()) line = line:style(f:style())
end end
@ -44,8 +74,9 @@ end
function Folder:current(area) function Folder:current(area)
local hovered = (self:hovered(self.Kind.Current) or {}).url local hovered = (self:hovered(self.Kind.Current) or {}).url
local markers = {}
local lines = {} local lines = {}
for _, f in pairs(self:window(self.Kind.Current)) do for i, f in ipairs(self:window(self.Kind.Current)) do
local name = f.name local name = f.name
-- Show symlink target -- Show symlink target
@ -59,15 +90,19 @@ function Folder:current(area)
-- Highlight hovered file -- Highlight hovered file
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. name .. " ") } local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. name .. " ") }
if f.url == hovered then if f.url == hovered then
line = line:style(THEME.selection.hovered) line = line:style(THEME.files.hovered)
else else
line = line:style(f:style()) line = line:style(f:style())
end end
lines[#lines + 1] = line lines[#lines + 1] = line
-- Mark selected/yanked files
if f:selected() then
markers[#markers + 1] = { i, 1 }
end
end end
return { ui.Paragraph(area, lines) } return { ui.Paragraph(area, lines), table.unpack(self:markers(area, markers)) }
end end
function Folder:preview(area) function Folder:preview(area)
@ -78,7 +113,7 @@ function Folder:preview(area)
local hovered = (self:hovered(self.Kind.Preview) or {}).url local hovered = (self:hovered(self.Kind.Preview) or {}).url
local lines = {} local lines = {}
for _, f in pairs(window) do for _, f in ipairs(window) do
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") } local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
if f.url == hovered then if f.url == hovered then
line = line:style(THEME.preview.hovered) line = line:style(THEME.preview.hovered)

View file

@ -1,9 +1,9 @@
Status = {} Status = {}
function Status.style() function Status.style()
if cx.manager.mode.is_select then if cx.active.mode.is_select then
return THEME.status.mode_select return THEME.status.mode_select
elseif cx.manager.mode.is_unset then elseif cx.active.mode.is_unset then
return THEME.status.mode_unset return THEME.status.mode_unset
else else
return THEME.status.mode_normal return THEME.status.mode_normal
@ -11,7 +11,7 @@ function Status.style()
end end
function Status:mode() function Status:mode()
local mode = tostring(cx.manager.mode):upper() local mode = tostring(cx.active.mode):upper()
if mode == "UNSET" then if mode == "UNSET" then
mode = "UN-SET" mode = "UN-SET"
end end
@ -24,7 +24,7 @@ function Status:mode()
end end
function Status:size() function Status:size()
local h = cx.manager.current.hovered local h = cx.active.current.hovered
if h == nil then if h == nil then
return ui.Span("") return ui.Span("")
end end
@ -37,7 +37,7 @@ function Status:size()
end end
function Status:name() function Status:name()
local h = cx.manager.current.hovered local h = cx.active.current.hovered
if h == nil then if h == nil then
return ui.Span("") return ui.Span("")
end end
@ -46,7 +46,7 @@ function Status:name()
end end
function Status:permissions() function Status:permissions()
local h = cx.manager.current.hovered local h = cx.active.current.hovered
if h == nil or h.permissions == nil then if h == nil or h.permissions == nil then
return ui.Span("") return ui.Span("")
end end
@ -69,8 +69,8 @@ end
function Status:percentage() function Status:percentage()
local percent = 0 local percent = 0
local cursor = cx.manager.current.cursor local cursor = cx.active.current.cursor
local length = #cx.manager.current.files local length = #cx.active.current.files
if cursor ~= 0 and length ~= 0 then if cursor ~= 0 and length ~= 0 then
percent = math.floor((cursor + 1) * 100 / length) percent = math.floor((cursor + 1) * 100 / length)
end end
@ -89,8 +89,8 @@ function Status:percentage()
end end
function Status:position() function Status:position()
local cursor = cx.manager.current.cursor local cursor = cx.active.current.cursor
local length = #cx.manager.current.files local length = #cx.active.current.files
local style = self.style() local style = self.style()
return ui.Line { return ui.Line {

View file

@ -1,5 +1,5 @@
pub fn init() -> mlua::Result<()> { pub fn init() -> mlua::Result<()> {
super::manager::Manager::init()?; super::tab::Tab::init()?;
super::tasks::Tasks::init()?; super::tasks::Tasks::init()?;
Ok(()) Ok(())

View file

@ -1,11 +1,11 @@
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
mod bindings; mod bindings;
mod manager;
mod shared; mod shared;
mod tab;
mod tasks; mod tasks;
pub use bindings::*; pub use bindings::*;
pub use manager::*;
pub use shared::*; pub use shared::*;
pub use tab::*;
pub use tasks::*; pub use tasks::*;

View file

@ -1,3 +1,5 @@
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};
@ -20,14 +22,16 @@ impl UserData for File {
} }
} }
pub struct Manager<'a, 'b> { pub struct Tab<'a, 'b> {
scope: &'b mlua::Scope<'a, 'a>, scope: &'b mlua::Scope<'a, 'a>,
inner: &'a core::manager::Manager,
cx: &'a core::Ctx,
inner: &'a core::manager::Tab,
} }
impl<'a, 'b> Manager<'a, 'b> { impl<'a, 'b> Tab<'a, 'b> {
pub(crate) fn init() -> mlua::Result<()> { pub(crate) fn init() -> mlua::Result<()> {
LUA.register_userdata_type::<core::manager::Manager>(|reg| { LUA.register_userdata_type::<core::manager::Tab>(|reg| {
reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode")); reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode"));
reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent")); reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent"));
reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current")); reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current"));
@ -85,6 +89,9 @@ impl<'a, 'b> Manager<'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_function("selected", |_, me: AnyUserData| {
me.named_user_value::<Function>("selected")?.call::<_, bool>(me)
});
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()));
@ -105,19 +112,24 @@ impl<'a, 'b> Manager<'a, 'b> {
Ok(()) Ok(())
} }
pub(crate) fn new(scope: &'b mlua::Scope<'a, 'a>, inner: &'a core::manager::Manager) -> Self { pub(crate) fn new(
Self { scope, inner } scope: &'b mlua::Scope<'a, 'a>,
cx: &'a Ctx,
inner: &'a core::manager::Tab,
) -> Self {
Self { scope, cx, inner }
} }
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> { pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
let ud = self.scope.create_any_userdata_ref(self.inner)?; let ud = self.scope.create_any_userdata_ref(self.inner)?;
ud.set_named_user_value("mode", self.scope.create_any_userdata_ref(&self.inner.mode)?)?;
ud.set_named_user_value( ud.set_named_user_value(
"mode", "parent",
self.scope.create_any_userdata_ref(self.inner.active().mode())?, self.inner.parent.as_ref().and_then(|p| self.folder(p, None).ok()),
)?; )?;
ud.set_named_user_value("parent", self.inner.parent().and_then(|p| self.folder(p, None).ok()))?; ud.set_named_user_value("current", self.folder(&self.inner.current, None)?)?;
ud.set_named_user_value("current", self.folder(self.inner.current(), None)?)?; ud.set_named_user_value("preview", self.preview(self.inner)?)?;
ud.set_named_user_value("preview", self.preview(self.inner.active())?)?;
Ok(ud) Ok(ud)
} }
@ -137,11 +149,16 @@ impl<'a, 'b> Manager<'a, 'b> {
.iter() .iter()
.skip(window.0) .skip(window.0)
.take(window.1) .take(window.1)
.filter_map(|f| self.file(f).ok()) .enumerate()
.filter_map(|(i, f)| self.file(i, f, inner).ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
)?; )?;
ud.set_named_user_value("files", self.files(&inner.files)?)?; ud.set_named_user_value("files", self.files(&inner.files)?)?;
ud.set_named_user_value("hovered", inner.hovered.as_ref().and_then(|h| self.file(h).ok()))?; // TODO: remove this
ud.set_named_user_value(
"hovered",
inner.hovered.as_ref().and_then(|h| self.file(999, h, inner).ok()),
)?;
Ok(ud) Ok(ud)
} }
@ -150,8 +167,15 @@ impl<'a, 'b> Manager<'a, 'b> {
self.scope.create_any_userdata_ref(inner) self.scope.create_any_userdata_ref(inner)
} }
fn file(&self, inner: &'a core::files::File) -> mlua::Result<AnyUserData<'a>> { fn file(
&self,
idx: usize,
inner: &'a core::files::File,
folder: &'a core::manager::Folder,
) -> mlua::Result<AnyUserData<'a>> {
let ud = self.scope.create_any_userdata_ref(inner)?; let ud = self.scope.create_any_userdata_ref(inner)?;
ud.set_named_user_value("idx", idx)?;
ud.set_named_user_value( ud.set_named_user_value(
"icon", "icon",
self.scope.create_function(|_, ()| { self.scope.create_function(|_, ()| {
@ -168,7 +192,7 @@ impl<'a, 'b> Manager<'a, 'b> {
ud.set_named_user_value( ud.set_named_user_value(
"style", "style",
self.scope.create_function(|_, ()| { self.scope.create_function(|_, ()| {
let mime = self.inner.mimetype.get(inner.url()); let mime = self.cx.manager.mimetype.get(inner.url());
Ok( Ok(
THEME THEME
.filetypes .filetypes
@ -179,6 +203,20 @@ impl<'a, 'b> Manager<'a, 'b> {
})?, })?,
)?; )?;
ud.set_named_user_value(
"selected",
self.scope.create_function(|_, me: AnyUserData| {
let is_visual = self.inner.mode.is_visual();
let selected = folder.files.is_selected(inner.url());
Ok(if !is_visual {
selected
} else {
let idx: usize = me.named_user_value("idx")?;
self.inner.mode.pending(folder.offset() + idx, selected)
})
})?,
)?;
Ok(ud) Ok(ud)
} }

View file

@ -58,7 +58,7 @@ impl UserData for Line {
Value::Table(tbl) => me.0.patch_style(Style::from(tbl).0), Value::Table(tbl) => me.0.patch_style(Style::from(tbl).0),
Value::UserData(ud) => me.0.patch_style(ud.borrow::<Style>()?.0), Value::UserData(ud) => me.0.patch_style(ud.borrow::<Style>()?.0),
_ => return Err(mlua::Error::external("expected a Style or Table or nil")), _ => return Err(mlua::Error::external("expected a Style or Table or nil")),
}; }
} }
Ok(ud) Ok(ud)
}); });

View file

@ -56,8 +56,16 @@ impl<'lua> FromLua<'lua> for Paragraph {
impl UserData for Paragraph { impl UserData for Paragraph {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("style", |_, (ud, style): (AnyUserData, Style)| { methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.style = Some(style.0); {
let mut me = ud.borrow_mut::<Self>()?;
match value {
Value::Nil => me.style = None,
Value::Table(tbl) => me.style = Some(Style::from(tbl).0),
Value::UserData(ud) => me.style = Some(ud.borrow::<Style>()?.0),
_ => return Err(mlua::Error::external("expected a Style or Table or nil")),
}
}
Ok(ud) Ok(ud)
}); });
methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| { methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| {

View file

@ -1,6 +1,5 @@
use config::theme::Color; use config::theme::Color;
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, UserDataMethods, Value}; use mlua::{AnyUserData, FromLua, Lua, Table, UserData, UserDataMethods, Value};
use tracing::info;
use crate::{GLOBALS, LUA}; use crate::{GLOBALS, LUA};
@ -51,7 +50,6 @@ impl UserData for Style {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| { methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.0.fg = Color::try_from(color).ok().map(Into::into); ud.borrow_mut::<Self>()?.0.fg = Color::try_from(color).ok().map(Into::into);
info!("fg: {:?}", ud.borrow::<Self>()?.0.fg);
Ok(ud) Ok(ud)
}); });
methods.add_function("bg", |_, (ud, color): (AnyUserData, String)| { methods.add_function("bg", |_, (ud, color): (AnyUserData, String)| {

View file

@ -5,7 +5,7 @@ use crate::{bindings, GLOBALS, LUA};
pub fn scope<F: FnOnce()>(cx: &Ctx, f: F) { pub fn scope<F: FnOnce()>(cx: &Ctx, f: F) {
let _ = LUA.scope(|scope| { let _ = LUA.scope(|scope| {
let tbl = LUA.create_table()?; let tbl = LUA.create_table()?;
tbl.set("manager", bindings::Manager::new(scope, &cx.manager).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)?;

View file

@ -92,7 +92,6 @@ 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(unix)]
#[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)]