mirror of
https://github.com/sayanarijit/xplr.git
synced 2024-10-05 14:58:20 +03:00
Merge bb7053baa9
into c1bb251fef
This commit is contained in:
commit
4e13555e91
@ -47,6 +47,20 @@ that can be overwritten.
|
||||
|
||||
Tries to auto complete the path in the input buffer
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_selection_item
|
||||
|
||||
Formats each node in the selection
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_preview_renderer
|
||||
|
||||
Renders the focused node in preview pane
|
||||
|
||||
See: [xplr.util.preview](https://xplr.dev/en/xplr.util#xplrutilpreview)
|
||||
|
||||
The focused node is passed as the node value, and layout_hight is passed
|
||||
dynamically.
|
||||
When there is no item under focus, the node value will be nil.
|
||||
|
||||
#### xplr.fn.builtin.fmt_general_table_row_cols_0
|
||||
|
||||
Renders the first column in the table
|
||||
|
@ -199,6 +199,18 @@ Style for each item in the selection list.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.preview.renderer.format
|
||||
|
||||
Preview renderer for the path under focus.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.preview.renderer.style
|
||||
|
||||
Style for preview panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.search.algorithm
|
||||
|
||||
The default search algorithm
|
||||
@ -548,6 +560,41 @@ Style of the selection panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.title.format
|
||||
|
||||
The content for the preview panel title.
|
||||
|
||||
Type: nullable string
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.title.style
|
||||
|
||||
Style of the preview panel title.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.borders
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.style
|
||||
|
||||
Style of the preview panel.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
Defines where to show borders for the preview panel.
|
||||
|
||||
Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.border_type
|
||||
|
||||
Type of the borders for preview panel.
|
||||
|
||||
Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
|
||||
#### xplr.config.general.panel_ui.preview.border_style
|
||||
|
||||
Style of the preview panel borders.
|
||||
|
||||
Type: [Style](https://xplr.dev/en/style)
|
||||
|
||||
#### xplr.config.general.panel_ui.sort_and_filter.title.format
|
||||
|
||||
The content for the sort & filter panel title.
|
||||
|
@ -127,12 +127,12 @@ xplr.util.path_split(".././foo")
|
||||
|
||||
### xplr.util.node
|
||||
|
||||
Get [Node][5] information of a given path.
|
||||
Get [Node][2] information of a given path.
|
||||
Doesn't check if the path exists.
|
||||
Returns nil if the path is "/".
|
||||
Errors out if absolute path can't be obtained.
|
||||
|
||||
Type: function( path:string ) -> [Node][5]|nil
|
||||
Type: function( path:string ) -> [Node][2]|nil
|
||||
|
||||
Example:
|
||||
|
||||
@ -146,9 +146,9 @@ xplr.util.node("/")
|
||||
|
||||
### xplr.util.node_type
|
||||
|
||||
Get the configured [Node Type][6] of a given [Node][5].
|
||||
Get the configured [Node Type][6] of a given [Node][2].
|
||||
|
||||
Type: function( [Node][5], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
Type: function( [Node][2], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
|
||||
If the second argument is missing, global config `xplr.config.node_types`
|
||||
will be used.
|
||||
@ -518,11 +518,33 @@ xplr.util.permissions_octal(app.focused_node.permission)
|
||||
-- { 0, 7, 5, 4 }
|
||||
```
|
||||
|
||||
### xplr.util.preview
|
||||
|
||||
Renders a preview of the given node as string.
|
||||
|
||||
You probably want to use it inside the function mentioned in
|
||||
[xplr.config.general.preview.renderer.format][9], or inside a
|
||||
[custom dynamic layout][10].
|
||||
|
||||
Type: function( { node:[Node][2]|nil, layout_size:[Size][5] } ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.preview({
|
||||
node = xplr.util.node("/foo"),
|
||||
layout_size = { x = 0, y = 0, height = 10, width = 10 },
|
||||
})
|
||||
-- "Preview of /foo"
|
||||
```
|
||||
|
||||
[1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
[2]: https://xplr.dev/en/lua-function-calls#node
|
||||
[3]: https://xplr.dev/en/style
|
||||
[4]: https://xplr.dev/en/layout
|
||||
[5]: https://xplr.dev/en/lua-function-calls#node
|
||||
[5]: https://xplr.dev/en/layout#size
|
||||
[6]: https://xplr.dev/en/node-type
|
||||
[7]: https://xplr.dev/en/node_types
|
||||
[8]: https://xplr.dev/en/column-renderer#permission
|
||||
[9]: https://xplr.dev/en/general-config#xplrconfiggeneralpreviewrendererformat
|
||||
[10]: https://xplr.dev/en/layout#dynamic
|
||||
|
@ -189,6 +189,13 @@ pub struct SelectionConfig {
|
||||
pub item: UiElement,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PreviewConfig {
|
||||
#[serde(default)]
|
||||
pub renderer: UiElement,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SearchConfig {
|
||||
@ -275,6 +282,9 @@ pub struct PanelUi {
|
||||
#[serde(default)]
|
||||
pub selection: PanelUiConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub preview: PanelUiConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub input_and_logs: PanelUiConfig,
|
||||
|
||||
@ -318,6 +328,9 @@ pub struct GeneralConfig {
|
||||
#[serde(default)]
|
||||
pub selection: SelectionConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub preview: PreviewConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub search: SearchConfig,
|
||||
|
||||
|
57
src/init.lua
57
src/init.lua
@ -261,6 +261,16 @@ xplr.config.general.selection.item.format = "builtin.fmt_general_selection_item"
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.selection.item.style = {}
|
||||
|
||||
-- Preview renderer for the path under focus.
|
||||
--
|
||||
-- Type: nullable string
|
||||
xplr.config.general.preview.renderer.format = "builtin.fmt_general_preview_renderer"
|
||||
|
||||
-- Style for preview panel.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.preview.renderer.style = { add_modifiers = { "Dim" } }
|
||||
|
||||
-- The default search algorithm
|
||||
--
|
||||
-- Type: [Search Algorithm](https://xplr.dev/en/searching#algorithm)
|
||||
@ -343,9 +353,7 @@ xplr.config.general.sort_and_filter_ui.separator.format = " › "
|
||||
-- The style of the separator for the Sort & filter panel.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.sort_and_filter_ui.separator.style = {
|
||||
add_modifiers = { "Dim" },
|
||||
}
|
||||
xplr.config.general.sort_and_filter_ui.separator.style = { add_modifiers = { "Dim" } }
|
||||
|
||||
-- The content of the default identifier in Sort & filter panel.
|
||||
--
|
||||
@ -639,12 +647,41 @@ xplr.config.general.panel_ui.selection.borders = nil
|
||||
-- Type of the borders for selection panel.
|
||||
--
|
||||
-- Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
xplr.config.general.panel_ui.selection.border_type = nil
|
||||
xplr.config.general.panel_ui.selection.border_type = "Double"
|
||||
|
||||
-- Style of the selection panel borders.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.panel_ui.selection.border_style = {}
|
||||
xplr.config.general.panel_ui.selection.border_style = { fg = "Red" }
|
||||
|
||||
-- The content for the preview panel title.
|
||||
--
|
||||
-- Type: nullable string
|
||||
xplr.config.general.panel_ui.preview.title.format = nil
|
||||
|
||||
-- Style of the preview panel title.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.panel_ui.preview.title.style = {}
|
||||
|
||||
-- Style of the preview panel.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.panel_ui.preview.style = {}
|
||||
-- Defines where to show borders for the preview panel.
|
||||
--
|
||||
-- Type: nullable list of [Border](https://xplr.dev/en/borders#border)
|
||||
xplr.config.general.panel_ui.preview.borders = nil
|
||||
|
||||
-- Type of the borders for preview panel.
|
||||
--
|
||||
-- Type: nullable [Border Type](https://xplr.dev/en/borders#border-type)
|
||||
xplr.config.general.panel_ui.preview.border_type = nil
|
||||
|
||||
-- Style of the preview panel borders.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.general.panel_ui.preview.border_style = {}
|
||||
|
||||
-- The content for the sort & filter panel title.
|
||||
--
|
||||
@ -3111,6 +3148,7 @@ xplr.fn.builtin.try_complete_path = function(m)
|
||||
end
|
||||
end
|
||||
|
||||
-- Formats each node in the selection
|
||||
xplr.fn.builtin.fmt_general_selection_item = function(n)
|
||||
local nl = xplr.util.paint("\\n", { add_modifiers = { "Italic", "Dim" } })
|
||||
local sh_config = { with_prefix_dots = true, without_suffix_dots = true }
|
||||
@ -3124,6 +3162,15 @@ xplr.fn.builtin.fmt_general_selection_item = function(n)
|
||||
return xplr.util.paint(shortened:gsub("\n", nl), style)
|
||||
end
|
||||
|
||||
-- Renders the focused node in preview pane
|
||||
--
|
||||
-- See: [xplr.util.preview](https://xplr.dev/en/xplr.util#xplrutilpreview)
|
||||
--
|
||||
-- The focused node is passed as the node value, and layout_hight is passed
|
||||
-- dynamically.
|
||||
-- When there is no item under focus, the node value will be nil.
|
||||
xplr.fn.builtin.fmt_general_preview_renderer = xplr.util.preview
|
||||
|
||||
-- Renders the first column in the table
|
||||
xplr.fn.builtin.fmt_general_table_row_cols_0 = function(m)
|
||||
local r = ""
|
||||
|
130
src/lua/util.rs
130
src/lua/util.rs
@ -10,6 +10,7 @@ use crate::permissions::Octal;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::ui;
|
||||
use crate::ui::Layout;
|
||||
use crate::ui::PreviewRendererArgs;
|
||||
use crate::ui::Style;
|
||||
use crate::ui::WrapOptions;
|
||||
use anyhow::Result;
|
||||
@ -26,8 +27,11 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_json as json;
|
||||
use serde_yaml as yaml;
|
||||
use std::borrow::Cow;
|
||||
use std::io::BufRead;
|
||||
use std::iter;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::{fs, io};
|
||||
|
||||
lazy_static! {
|
||||
static ref LS_COLORS: LsColors = LsColors::from_env().unwrap_or_default();
|
||||
@ -224,12 +228,12 @@ pub fn path_split<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get [Node][5] information of a given path.
|
||||
/// Get [Node][2] information of a given path.
|
||||
/// Doesn't check if the path exists.
|
||||
/// Returns nil if the path is "/".
|
||||
/// Errors out if absolute path can't be obtained.
|
||||
///
|
||||
/// Type: function( path:string ) -> [Node][5]|nil
|
||||
/// Type: function( path:string ) -> [Node][2]|nil
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
@ -259,9 +263,9 @@ pub fn node<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the configured [Node Type][6] of a given [Node][5].
|
||||
/// Get the configured [Node Type][6] of a given [Node][2].
|
||||
///
|
||||
/// Type: function( [Node][5], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
/// Type: function( [Node][2], [xplr.config.node_types][7]|nil ) -> [Node Type][6]
|
||||
///
|
||||
/// If the second argument is missing, global config `xplr.config.node_types`
|
||||
/// will be used.
|
||||
@ -852,15 +856,130 @@ pub fn permissions_octal<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Renders a preview of the given node as string.
|
||||
///
|
||||
/// You probably want to use it inside the function mentioned in
|
||||
/// [xplr.config.general.preview.renderer.format][9], or inside a
|
||||
/// [custom dynamic layout][10].
|
||||
///
|
||||
/// Type: function( { node:[Node][2]|nil, layout_size:[Size][5] } ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.preview({
|
||||
/// node = xplr.util.node("/foo"),
|
||||
/// layout_size = { x = 0, y = 0, height = 10, width = 10 },
|
||||
/// })
|
||||
/// -- "Preview of /foo"
|
||||
/// ```
|
||||
pub fn preview<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
fn format_node(node: &Node) -> String {
|
||||
format!(
|
||||
"• T: {}\n• P: {}\n• O: {}:{}\n• S: {}",
|
||||
node.mime_essence,
|
||||
node.permissions.to_string(),
|
||||
node.uid,
|
||||
node.gid,
|
||||
node.human_size,
|
||||
)
|
||||
}
|
||||
|
||||
let func = lua.create_function(|lua, args: Table| {
|
||||
let args: PreviewRendererArgs = lua.from_value(Value::Table(args))?;
|
||||
let Some(node) = args.node else {
|
||||
return Ok("".into());
|
||||
};
|
||||
|
||||
let size = args.layout_size;
|
||||
|
||||
let preview = if node
|
||||
.canonical
|
||||
.as_ref()
|
||||
.map(|c| c.is_file)
|
||||
.unwrap_or(node.is_file)
|
||||
{
|
||||
let path = node
|
||||
.canonical
|
||||
.as_ref()
|
||||
.map(|c| &c.absolute_path)
|
||||
.unwrap_or(&node.absolute_path);
|
||||
|
||||
let file = fs::File::open(path)?;
|
||||
let reader = io::BufReader::new(file);
|
||||
let mut lines = vec![];
|
||||
for line in reader.lines() {
|
||||
let Ok(mut line) = line else {
|
||||
return Ok(format_node(&node));
|
||||
};
|
||||
line.truncate(size.width.into());
|
||||
lines.push(line);
|
||||
if lines.len() >= size.height.into() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
lines.join("\n")
|
||||
} else if node
|
||||
.canonical
|
||||
.as_ref()
|
||||
.map(|c| c.is_dir)
|
||||
.unwrap_or(node.is_dir)
|
||||
{
|
||||
let path = node
|
||||
.symlink
|
||||
.as_ref()
|
||||
.map(|c| &c.absolute_path)
|
||||
.unwrap_or(&node.relative_path);
|
||||
|
||||
match fs::read_dir(path) {
|
||||
Ok(nodes) => iter::once(format!("▼ {}/", path))
|
||||
.chain(
|
||||
nodes
|
||||
.filter_map(|d| d.ok())
|
||||
.map(|d| {
|
||||
if d.file_type()
|
||||
.ok()
|
||||
.map(|t| t.is_dir())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
format!(" ▷ {}/", d.file_name().to_string_lossy())
|
||||
} else {
|
||||
format!(" {}", d.file_name().to_string_lossy())
|
||||
}
|
||||
})
|
||||
.take(size.height.into()),
|
||||
)
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n"),
|
||||
Err(err) => err.to_string(),
|
||||
}
|
||||
} else if node.is_symlink && node.is_broken {
|
||||
"-> ×".into()
|
||||
} else if node.is_symlink {
|
||||
node.symlink
|
||||
.map(|s| format!("-> {}", s.absolute_path))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
format_node(&node)
|
||||
};
|
||||
|
||||
Ok(preview)
|
||||
})?;
|
||||
util.set("preview", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
///
|
||||
/// [1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
/// [2]: https://xplr.dev/en/lua-function-calls#node
|
||||
/// [3]: https://xplr.dev/en/style
|
||||
/// [4]: https://xplr.dev/en/layout
|
||||
/// [5]: https://xplr.dev/en/lua-function-calls#node
|
||||
/// [5]: https://xplr.dev/en/layout#size
|
||||
/// [6]: https://xplr.dev/en/node-type
|
||||
/// [7]: https://xplr.dev/en/node_types
|
||||
/// [8]: https://xplr.dev/en/column-renderer#permission
|
||||
/// [9]: https://xplr.dev/en/general-config#xplrconfiggeneralpreviewrendererformat
|
||||
/// [10]: https://xplr.dev/en/layout#dynamic
|
||||
|
||||
pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
|
||||
let mut util = lua.create_table()?;
|
||||
@ -896,6 +1015,7 @@ pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
|
||||
util = layout_replace(util, lua)?;
|
||||
util = permissions_rwx(util, lua)?;
|
||||
util = permissions_octal(util, lua)?;
|
||||
util = preview(util, lua)?;
|
||||
|
||||
Ok(util)
|
||||
}
|
||||
|
55
src/ui.rs
55
src/ui.rs
@ -915,6 +915,53 @@ fn draw_table(
|
||||
f.render_widget(table, layout_size);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PreviewRendererArgs {
|
||||
pub node: Option<Node>,
|
||||
pub layout_size: TuiRect,
|
||||
}
|
||||
|
||||
fn draw_preview(
|
||||
f: &mut Frame,
|
||||
_screen_size: TuiRect,
|
||||
layout_size: TuiRect,
|
||||
app: &app::App,
|
||||
lua: &Lua,
|
||||
) {
|
||||
let panel_config = &app.config.general.panel_ui;
|
||||
let config = panel_config
|
||||
.default
|
||||
.to_owned()
|
||||
.extend(&panel_config.preview);
|
||||
|
||||
let renderer = app
|
||||
.config
|
||||
.general
|
||||
.preview
|
||||
.renderer
|
||||
.format
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
|
||||
let preview = if let Some(node) = app.focused_node() {
|
||||
let args = PreviewRendererArgs {
|
||||
node: Some(node.to_owned()),
|
||||
layout_size,
|
||||
};
|
||||
lua::serialize::<PreviewRendererArgs>(lua, &args)
|
||||
.and_then(|args| lua::call(lua, &renderer, args))
|
||||
.unwrap_or_else(|e| format!("{e:?}"))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let mut text = string_to_text(preview);
|
||||
text.patch_style(app.config.general.preview.renderer.style.to_owned().into());
|
||||
let block = Paragraph::new(text).block(block(config, " Preview ".into()));
|
||||
f.render_widget(block, layout_size);
|
||||
}
|
||||
|
||||
fn draw_selection(
|
||||
f: &mut Frame,
|
||||
_screen_size: TuiRect,
|
||||
@ -1421,7 +1468,13 @@ pub fn draw_layout(
|
||||
draw_sort_n_filter(f, screen_size, layout_size, app, lua)
|
||||
}
|
||||
Layout::HelpMenu => draw_help_menu(f, screen_size, layout_size, app, lua),
|
||||
Layout::Selection => draw_selection(f, screen_size, layout_size, app, lua),
|
||||
Layout::Selection => {
|
||||
if app.selection.is_empty() {
|
||||
draw_preview(f, screen_size, layout_size, app, lua);
|
||||
} else {
|
||||
draw_selection(f, screen_size, layout_size, app, lua);
|
||||
}
|
||||
}
|
||||
Layout::InputAndLogs => {
|
||||
if app.input.buffer.is_some() {
|
||||
draw_input_buffer(f, screen_size, layout_size, app, lua);
|
||||
|
Loading…
Reference in New Issue
Block a user