mirror of
https://github.com/sayanarijit/xplr
synced 2024-11-04 18:00:14 +00:00
More UI utilities and improvements (#582)
* More UI utilities and improvements - Apply style only to the file column in the table. - Properly quote paths. - Expose the applicable style from config in the table renderer argument. - Add utility functions: - xplr.util.node_type - xplr.util.style_mix - xplr.util.shell_escape * Make escaping play nice with shorten * Fix tests * Fix doc
This commit is contained in:
parent
adccbae8f5
commit
77b99ae413
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -1071,6 +1071,16 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
||||
|
||||
[[package]]
|
||||
name = "snailquote"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
@ -1220,6 +1230,12 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.5"
|
||||
@ -1469,6 +1485,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"snailquote",
|
||||
"textwrap",
|
||||
"tui",
|
||||
"tui-input",
|
||||
|
@ -38,6 +38,7 @@ path-absolutize = "3.0.14"
|
||||
which = "4.3.0"
|
||||
nu-ansi-term = "0.46.0"
|
||||
textwrap = "0.16"
|
||||
snailquote = "0.3.1"
|
||||
|
||||
[dependencies.lscolors]
|
||||
version = "0.13.0"
|
||||
|
@ -73,6 +73,7 @@ The special argument contains the following fields
|
||||
- [is_selected][25]
|
||||
- [is_focused][26]
|
||||
- [total][27]
|
||||
- [style][38]
|
||||
- [meta][28]
|
||||
|
||||
### parent
|
||||
@ -254,6 +255,12 @@ Type: integer
|
||||
|
||||
The total number of the nodes.
|
||||
|
||||
### style
|
||||
|
||||
Type: [Style][39]
|
||||
|
||||
The applicable [style object][39] for the node.
|
||||
|
||||
### meta
|
||||
|
||||
Type: mapping of string and string
|
||||
@ -333,3 +340,5 @@ It contains the following fields.
|
||||
[35]: #last_modified
|
||||
[36]: #uid
|
||||
[37]: #gid
|
||||
[38]: #style
|
||||
[39]: style.md#style
|
||||
|
@ -127,7 +127,24 @@ xplr.util.node("/")
|
||||
-- nil
|
||||
```
|
||||
|
||||
[5]: https://xplr.dev/en/lua-function-calls#node
|
||||
### xplr.util.node_type
|
||||
|
||||
Get the configured [Node Type][6] of a given [Node][5].
|
||||
|
||||
Type: function( [Node][5], [NodeTypesConfig][7]|nil ) -> [NodeType][5]
|
||||
|
||||
If the second argument is missing, global config `xplr.config.node_types`
|
||||
will be used.
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.node_type(app.focused_node)
|
||||
-- { style = { fg = ..., ... }, meta = { icon = ..., ... } ... }
|
||||
|
||||
xplr.util.node_type(xplr.util.node("/foo/bar"), xplr.config.node_types)
|
||||
-- { style = { fg = ..., ... }, meta = { icon = ..., ... } ... }
|
||||
```
|
||||
|
||||
### xplr.util.dirname
|
||||
|
||||
@ -259,9 +276,6 @@ xplr.util.explore("/tmp", app.explorer_config)
|
||||
-- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
```
|
||||
|
||||
[1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
[2]: https://xplr.dev/en/lua-function-calls#node
|
||||
|
||||
### xplr.util.shell_execute
|
||||
|
||||
Execute shell commands safely.
|
||||
@ -291,6 +305,19 @@ xplr.util.shell_quote("a'b\"c")
|
||||
-- 'a'"'"'b"c'
|
||||
```
|
||||
|
||||
### xplr.util.shell_escape
|
||||
|
||||
Escape commands and paths safely.
|
||||
|
||||
Type: function( string ) -> string
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.shell_escape("a'b\"c")
|
||||
-- "\"a'b\\\"c\""
|
||||
```
|
||||
|
||||
### xplr.util.from_json
|
||||
|
||||
Load JSON string into Lua value.
|
||||
@ -353,7 +380,7 @@ xplr.util.to_yaml({ foo = "bar" })
|
||||
Get a [Style][3] object for the given path based on the LS_COLORS
|
||||
environment variable.
|
||||
|
||||
Type: function( path:string ) -> Style[3]|nil
|
||||
Type: function( path:string ) -> [Style][3]|nil
|
||||
|
||||
Example:
|
||||
|
||||
@ -362,8 +389,6 @@ xplr.util.lscolor("Desktop")
|
||||
-- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
|
||||
```
|
||||
|
||||
[3]: https://xplr.dev/en/style
|
||||
|
||||
### xplr.util.paint
|
||||
|
||||
Apply style (escape sequence) to string using a given [Style][3] object.
|
||||
@ -377,6 +402,19 @@ xplr.util.paint("Desktop", { fg = "Red", bg = nil, add_modifiers = {}, sub_modif
|
||||
-- "\u001b[31mDesktop\u001b[0m"
|
||||
```
|
||||
|
||||
### xplr.util.style_mix
|
||||
|
||||
Mix multiple [Style][3] objects into one.
|
||||
|
||||
Type: function( [Style][3], [Style][3], ... ) -> Style[3]
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
xplr.util.style_mix({ fg = "Red" }, { bg = "Blue" }, { add_modifiers = {"Bold"} })
|
||||
-- { fg = "Red", bg = "Blue", add_modifiers = { "Bold" }, sub_modifiers = {} }
|
||||
```
|
||||
|
||||
### xplr.util.textwrap
|
||||
|
||||
Wrap the given text to fit the specified width.
|
||||
@ -431,4 +469,10 @@ xplr.util.layout_replace(layout, "Table", "Selection")
|
||||
-- }
|
||||
```
|
||||
|
||||
[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
|
||||
[6]: https://xplr.dev/en/node-type
|
||||
[7]: https://xplr.dev/en/node-types
|
||||
|
@ -260,6 +260,9 @@ def gen_xplr_util():
|
||||
print("\n".join(function.doc))
|
||||
print("\n".join(function.doc), file=f)
|
||||
|
||||
if reading:
|
||||
print("\n".join(reading.doc), file=f)
|
||||
|
||||
|
||||
def format_docs():
|
||||
os.system("prettier --write docs/en/src")
|
||||
|
@ -3,6 +3,7 @@ use crate::app::HelpMenuLine;
|
||||
use crate::app::NodeFilter;
|
||||
use crate::app::NodeSorter;
|
||||
use crate::app::NodeSorterApplicable;
|
||||
use crate::node::Node;
|
||||
use crate::ui::Border;
|
||||
use crate::ui::BorderType;
|
||||
use crate::ui::Constraint;
|
||||
@ -80,6 +81,40 @@ pub struct NodeTypesConfig {
|
||||
pub special: HashMap<String, NodeTypeConfig>,
|
||||
}
|
||||
|
||||
impl NodeTypesConfig {
|
||||
pub fn get(&self, node: &Node) -> NodeTypeConfig {
|
||||
let mut node_type = if node.is_symlink {
|
||||
self.symlink.to_owned()
|
||||
} else if node.is_dir {
|
||||
self.directory.to_owned()
|
||||
} else {
|
||||
self.file.to_owned()
|
||||
};
|
||||
|
||||
let mut me = node.mime_essence.splitn(2, '/');
|
||||
let mimetype: String = me.next().map(|s| s.into()).unwrap_or_default();
|
||||
let mimesub: String = me.next().map(|s| s.into()).unwrap_or_default();
|
||||
|
||||
if let Some(conf) = self
|
||||
.mime_essence
|
||||
.get(&mimetype)
|
||||
.and_then(|t| t.get(&mimesub).or_else(|| t.get("*")))
|
||||
{
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
|
||||
if let Some(conf) = self.extension.get(&node.extension) {
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
|
||||
if let Some(conf) = self.special.get(&node.relative_path) {
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
|
||||
node_type
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct UiConfig {
|
||||
|
40
src/init.lua
40
src/init.lua
@ -710,7 +710,9 @@ xplr.config.general.global_key_bindings = {
|
||||
-- The style for the directory nodes
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.node_types.directory.style = {}
|
||||
xplr.config.node_types.directory.style = {
|
||||
fg = "Blue",
|
||||
}
|
||||
|
||||
-- Metadata for the directory nodes.
|
||||
-- You can set as many metadata as you want.
|
||||
@ -746,7 +748,10 @@ xplr.config.node_types.file.meta.icon = "ƒ"
|
||||
-- The style for the symlink nodes.
|
||||
--
|
||||
-- Type: [Style](https://xplr.dev/en/style)
|
||||
xplr.config.node_types.symlink.style = {}
|
||||
xplr.config.node_types.symlink.style = {
|
||||
fg = "Magenta",
|
||||
add_modifiers = { "Italic" },
|
||||
}
|
||||
|
||||
-- Metadata for the symlink nodes.
|
||||
-- You can set as many metadata as you want.
|
||||
@ -2615,9 +2620,16 @@ xplr.fn.builtin.try_complete_path = function(m)
|
||||
end
|
||||
|
||||
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 }
|
||||
local shortened = xplr.util.shorten(n.absolute_path, sh_config)
|
||||
return xplr.util.paint(shortened, xplr.util.lscolor(n.absolute_path))
|
||||
if n.is_dir then
|
||||
shortened = shortened .. "/"
|
||||
end
|
||||
local ls_style = xplr.util.lscolor(n.absolute_path)
|
||||
local meta_style = xplr.util.node_type(n).style
|
||||
local style = xplr.util.style_mix({ meta_style, ls_style })
|
||||
return xplr.util.paint(shortened:gsub("\n", nl), style)
|
||||
end
|
||||
|
||||
-- Renders the first column in the table
|
||||
@ -2636,11 +2648,10 @@ end
|
||||
|
||||
-- Renders the second column in the table
|
||||
xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
|
||||
local nl = xplr.util.paint("\\n", { add_modifiers = { "Italic", "Dim" } })
|
||||
local r = m.tree .. m.prefix
|
||||
|
||||
local function path_escape(path)
|
||||
return string.gsub(string.gsub(path, "\\", "\\\\"), "\n", "\\n")
|
||||
end
|
||||
local style = xplr.util.lscolor(m.absolute_path)
|
||||
style = xplr.util.style_mix({ m.style, style })
|
||||
|
||||
if m.meta.icon == nil then
|
||||
r = r .. ""
|
||||
@ -2648,11 +2659,11 @@ xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
|
||||
r = r .. m.meta.icon .. " "
|
||||
end
|
||||
|
||||
r = r .. path_escape(m.relative_path)
|
||||
|
||||
local rel = m.relative_path
|
||||
if m.is_dir then
|
||||
r = r .. "/"
|
||||
rel = rel .. "/"
|
||||
end
|
||||
r = r .. xplr.util.paint(xplr.util.shell_escape(rel), style)
|
||||
|
||||
r = r .. m.suffix .. " "
|
||||
|
||||
@ -2663,15 +2674,14 @@ xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
|
||||
r = r .. "×"
|
||||
else
|
||||
local symlink_path = xplr.util.shorten(m.symlink.absolute_path)
|
||||
r = r .. path_escape(symlink_path)
|
||||
|
||||
if m.symlink.is_dir then
|
||||
r = r .. "/"
|
||||
symlink_path = symlink_path .. "/"
|
||||
end
|
||||
r = r .. symlink_path:gsub("\n", nl)
|
||||
end
|
||||
end
|
||||
local style = xplr.util.lscolor(m.absolute_path)
|
||||
return xplr.util.paint(r, style)
|
||||
|
||||
return r
|
||||
end
|
||||
|
||||
-- Renders the third column in the table
|
||||
|
172
src/lua/util.rs
172
src/lua/util.rs
@ -1,4 +1,5 @@
|
||||
use crate::app::VERSION;
|
||||
use crate::config::NodeTypesConfig;
|
||||
use crate::explorer;
|
||||
use crate::lua;
|
||||
use crate::msg::in_::external::ExplorerConfig;
|
||||
@ -25,38 +26,6 @@ use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
|
||||
let mut util = lua.create_table()?;
|
||||
|
||||
util = version(util, lua)?;
|
||||
util = clone(util, lua)?;
|
||||
util = exists(util, lua)?;
|
||||
util = is_dir(util, lua)?;
|
||||
util = is_file(util, lua)?;
|
||||
util = is_symlink(util, lua)?;
|
||||
util = is_absolute(util, lua)?;
|
||||
util = path_split(util, lua)?;
|
||||
util = node(util, lua)?;
|
||||
util = dirname(util, lua)?;
|
||||
util = basename(util, lua)?;
|
||||
util = absolute(util, lua)?;
|
||||
util = relative_to(util, lua)?;
|
||||
util = shorten(util, lua)?;
|
||||
util = explore(util, lua)?;
|
||||
util = shell_execute(util, lua)?;
|
||||
util = shell_quote(util, lua)?;
|
||||
util = from_json(util, lua)?;
|
||||
util = to_json(util, lua)?;
|
||||
util = from_yaml(util, lua)?;
|
||||
util = to_yaml(util, lua)?;
|
||||
util = lscolor(util, lua)?;
|
||||
util = paint(util, lua)?;
|
||||
util = textwrap(util, lua)?;
|
||||
util = layout_replace(util, lua)?;
|
||||
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the xplr version details.
|
||||
///
|
||||
/// Type: function() -> { major: number, minor: number, patch: number }
|
||||
@ -241,8 +210,6 @@ pub fn path_split<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
/// xplr.util.node("/")
|
||||
/// -- nil
|
||||
/// ```
|
||||
///
|
||||
/// [5]: https://xplr.dev/en/lua-function-calls#node
|
||||
pub fn node<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |lua, path: String| {
|
||||
let path = PathBuf::from(path);
|
||||
@ -262,6 +229,43 @@ 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].
|
||||
///
|
||||
/// Type: function( [Node][5], [NodeTypesConfig][7]|nil ) -> [NodeType][5]
|
||||
///
|
||||
/// If the second argument is missing, global config `xplr.config.node_types`
|
||||
/// will be used.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.node_type(app.focused_node)
|
||||
/// -- { style = { fg = ..., ... }, meta = { icon = ..., ... } ... }
|
||||
///
|
||||
/// xplr.util.node_type(xplr.util.node("/foo/bar"), xplr.config.node_types)
|
||||
/// -- { style = { fg = ..., ... }, meta = { icon = ..., ... } ... }
|
||||
/// ```
|
||||
pub fn node_type<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func =
|
||||
lua.create_function(move |lua, (node, config): (Table, Option<Table>)| {
|
||||
let node: Node = lua.from_value(Value::Table(node))?;
|
||||
let config: Table = if let Some(config) = config {
|
||||
config
|
||||
} else {
|
||||
lua.globals()
|
||||
.get::<_, Table>("xplr")?
|
||||
.get::<_, Table>("config")?
|
||||
.get::<_, Table>("node_types")?
|
||||
};
|
||||
let config: NodeTypesConfig = lua.from_value(Value::Table(config))?;
|
||||
let node_type = config.get(&node);
|
||||
let node_type = lua::serialize(lua, &node_type).map_err(LuaError::custom)?;
|
||||
Ok(node_type)
|
||||
})?;
|
||||
util.set("node_type", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Get the directory name of a given path.
|
||||
///
|
||||
/// Type: function( path:string ) -> path:string|nil
|
||||
@ -430,9 +434,6 @@ pub fn shorten<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
/// xplr.util.explore("/tmp", app.explorer_config)
|
||||
/// -- { { absolute_path = "/tmp/a", ... }, ... }
|
||||
/// ```
|
||||
///
|
||||
/// [1]: https://xplr.dev/en/lua-function-calls#explorer-config
|
||||
/// [2]: https://xplr.dev/en/lua-function-calls#node
|
||||
pub fn explore<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, (path, config): (String, Option<Table>)| {
|
||||
let config: ExplorerConfig = if let Some(cfg) = config {
|
||||
@ -501,6 +502,25 @@ pub fn shell_quote<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Escape commands and paths safely.
|
||||
///
|
||||
/// Type: function( string ) -> string
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.shell_escape("a'b\"c")
|
||||
/// -- "\"a'b\\\"c\""
|
||||
/// ```
|
||||
pub fn shell_escape<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(move |_, string: String| {
|
||||
let val = path::escape(&string).to_string();
|
||||
Ok(val)
|
||||
})?;
|
||||
util.set("shell_escape", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Load JSON string into Lua value.
|
||||
///
|
||||
/// Type: function( string ) -> any
|
||||
@ -604,7 +624,7 @@ pub fn to_yaml<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
/// Get a [Style][3] object for the given path based on the LS_COLORS
|
||||
/// environment variable.
|
||||
///
|
||||
/// Type: function( path:string ) -> Style[3]|nil
|
||||
/// Type: function( path:string ) -> [Style][3]|nil
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
@ -612,15 +632,9 @@ pub fn to_yaml<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
/// xplr.util.lscolor("Desktop")
|
||||
/// -- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
|
||||
/// ```
|
||||
///
|
||||
/// [3]: https://xplr.dev/en/style
|
||||
pub fn lscolor<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let lscolors = LsColors::from_env().unwrap_or_default();
|
||||
let func = lua.create_function(move |lua, path: String| {
|
||||
if *ui::NO_COLOR {
|
||||
return Ok(mlua::Nil);
|
||||
}
|
||||
|
||||
let style = lscolors.style_for_path(path).map(Style::from);
|
||||
lua::serialize(lua, &style).map_err(LuaError::custom)
|
||||
})?;
|
||||
@ -657,6 +671,30 @@ pub fn paint<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Mix multiple [Style][3] objects into one.
|
||||
///
|
||||
/// Type: function( [Style][3], [Style][3], ... ) -> Style[3]
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```lua
|
||||
/// xplr.util.style_mix({ fg = "Red" }, { bg = "Blue" }, { add_modifiers = {"Bold"} })
|
||||
/// -- { fg = "Red", bg = "Blue", add_modifiers = { "Bold" }, sub_modifiers = {} }
|
||||
/// ```
|
||||
pub fn style_mix<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(|lua, styles: Vec<Table>| {
|
||||
let mut style = Style::default();
|
||||
for other in styles {
|
||||
let other: Style = lua.from_value(Value::Table(other))?;
|
||||
style = style.extend(&other);
|
||||
}
|
||||
|
||||
lua::serialize(lua, &style).map_err(LuaError::custom)
|
||||
})?;
|
||||
util.set("style_mix", func)?;
|
||||
Ok(util)
|
||||
}
|
||||
|
||||
/// Wrap the given text to fit the specified width.
|
||||
/// It will try to not split words when possible.
|
||||
///
|
||||
@ -721,8 +759,6 @@ pub fn textwrap<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
/// -- }
|
||||
/// -- }
|
||||
/// ```
|
||||
///
|
||||
/// [4]: https://xplr.dev/en/layout
|
||||
pub fn layout_replace<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
let func = lua.create_function(
|
||||
move |lua, (layout, target, replacement): (Table, Table, Table)| {
|
||||
@ -739,3 +775,47 @@ pub fn layout_replace<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
|
||||
util.set("layout_replaced", 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
|
||||
/// [6]: https://xplr.dev/en/node-type
|
||||
/// [7]: https://xplr.dev/en/node-types
|
||||
|
||||
pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
|
||||
let mut util = lua.create_table()?;
|
||||
|
||||
util = version(util, lua)?;
|
||||
util = clone(util, lua)?;
|
||||
util = exists(util, lua)?;
|
||||
util = is_dir(util, lua)?;
|
||||
util = is_file(util, lua)?;
|
||||
util = is_symlink(util, lua)?;
|
||||
util = is_absolute(util, lua)?;
|
||||
util = path_split(util, lua)?;
|
||||
util = node(util, lua)?;
|
||||
util = node_type(util, lua)?;
|
||||
util = dirname(util, lua)?;
|
||||
util = basename(util, lua)?;
|
||||
util = absolute(util, lua)?;
|
||||
util = relative_to(util, lua)?;
|
||||
util = shorten(util, lua)?;
|
||||
util = explore(util, lua)?;
|
||||
util = shell_execute(util, lua)?;
|
||||
util = shell_quote(util, lua)?;
|
||||
util = shell_escape(util, lua)?;
|
||||
util = from_json(util, lua)?;
|
||||
util = to_json(util, lua)?;
|
||||
util = from_yaml(util, lua)?;
|
||||
util = to_yaml(util, lua)?;
|
||||
util = lscolor(util, lua)?;
|
||||
util = paint(util, lua)?;
|
||||
util = style_mix(util, lua)?;
|
||||
util = textwrap(util, lua)?;
|
||||
util = layout_replace(util, lua)?;
|
||||
|
||||
Ok(util)
|
||||
}
|
||||
|
22
src/path.rs
22
src/path.rs
@ -1,6 +1,7 @@
|
||||
use anyhow::{bail, Result};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use snailquote::escape;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
lazy_static! {
|
||||
@ -470,4 +471,25 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(res, "/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_escape() {
|
||||
let text = "foo".to_string();
|
||||
assert_eq!(escape(&text), "foo");
|
||||
|
||||
let text = "foo bar".to_string();
|
||||
assert_eq!(escape(&text), "'foo bar'");
|
||||
|
||||
let text = "foo\nbar".to_string();
|
||||
assert_eq!(escape(&text), "\"foo\\nbar\"");
|
||||
|
||||
let text = "foo$bar".to_string();
|
||||
assert_eq!(escape(&text), "'foo$bar'");
|
||||
|
||||
let text = "foo'$\n'bar".to_string();
|
||||
assert_eq!(escape(&text), "\"foo'\\$\\n'bar\"");
|
||||
|
||||
let text = "a'b\"c".to_string();
|
||||
assert_eq!(escape(&text), "\"a'b\\\"c\"");
|
||||
}
|
||||
}
|
||||
|
88
src/ui.rs
88
src/ui.rs
@ -1,9 +1,9 @@
|
||||
use crate::app;
|
||||
use crate::app::{HelpMenuLine, NodeFilterApplicable, NodeSorterApplicable};
|
||||
use crate::app::{Node, ResolvedNode};
|
||||
use crate::config::PanelUiConfig;
|
||||
use crate::lua;
|
||||
use crate::permissions::Permissions;
|
||||
use crate::{app, path};
|
||||
use ansi_to_tui::IntoText;
|
||||
use indexmap::IndexSet;
|
||||
use lazy_static::lazy_static;
|
||||
@ -278,6 +278,21 @@ impl Modifier {
|
||||
}
|
||||
}
|
||||
|
||||
fn extend_optional_modifiers(
|
||||
a: Option<IndexSet<Modifier>>,
|
||||
b: Option<IndexSet<Modifier>>,
|
||||
) -> Option<IndexSet<Modifier>> {
|
||||
match (a, b) {
|
||||
(Some(mut a), Some(b)) => {
|
||||
a.extend(b);
|
||||
Some(a)
|
||||
}
|
||||
(Some(a), None) => Some(a),
|
||||
(None, Some(b)) => Some(b),
|
||||
(None, None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Style {
|
||||
@ -291,8 +306,14 @@ impl Style {
|
||||
pub fn extend(mut self, other: &Self) -> Self {
|
||||
self.fg = other.fg.or(self.fg);
|
||||
self.bg = other.bg.or(self.bg);
|
||||
self.add_modifiers = other.add_modifiers.to_owned().or(self.add_modifiers);
|
||||
self.sub_modifiers = other.sub_modifiers.to_owned().or(self.sub_modifiers);
|
||||
self.add_modifiers = extend_optional_modifiers(
|
||||
self.add_modifiers,
|
||||
other.add_modifiers.to_owned(),
|
||||
);
|
||||
self.sub_modifiers = extend_optional_modifiers(
|
||||
self.sub_modifiers,
|
||||
other.sub_modifiers.to_owned(),
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
@ -564,6 +585,7 @@ pub struct NodeUiMetadata {
|
||||
pub is_focused: bool,
|
||||
pub total: usize,
|
||||
pub meta: HashMap<String, String>,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl NodeUiMetadata {
|
||||
@ -580,6 +602,7 @@ impl NodeUiMetadata {
|
||||
is_focused: bool,
|
||||
total: usize,
|
||||
meta: HashMap<String, String>,
|
||||
style: Style,
|
||||
) -> Self {
|
||||
Self {
|
||||
parent: node.parent.to_owned(),
|
||||
@ -612,6 +635,7 @@ impl NodeUiMetadata {
|
||||
is_focused,
|
||||
total,
|
||||
meta,
|
||||
style,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -686,40 +710,7 @@ fn draw_table<B: Backend>(
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut me = node.mime_essence.splitn(2, '/');
|
||||
let mimetype: String =
|
||||
me.next().map(|s| s.into()).unwrap_or_default();
|
||||
let mimesub: String =
|
||||
me.next().map(|s| s.into()).unwrap_or_default();
|
||||
|
||||
let mut node_type = if node.is_symlink {
|
||||
app_config.node_types.symlink.to_owned()
|
||||
} else if node.is_dir {
|
||||
app_config.node_types.directory.to_owned()
|
||||
} else {
|
||||
app_config.node_types.file.to_owned()
|
||||
};
|
||||
|
||||
if let Some(conf) = app_config
|
||||
.node_types
|
||||
.mime_essence
|
||||
.get(&mimetype)
|
||||
.and_then(|t| t.get(&mimesub).or_else(|| t.get("*")))
|
||||
{
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
|
||||
if let Some(conf) =
|
||||
app_config.node_types.extension.get(&node.extension)
|
||||
{
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
|
||||
if let Some(conf) =
|
||||
app_config.node_types.special.get(&node.relative_path)
|
||||
{
|
||||
node_type = node_type.extend(conf);
|
||||
}
|
||||
let node_type = app_config.node_types.get(node);
|
||||
|
||||
let (relative_index, is_before_focus, is_after_focus) =
|
||||
match dir.focus.cmp(&index) {
|
||||
@ -763,6 +754,7 @@ fn draw_table<B: Backend>(
|
||||
is_focused,
|
||||
dir.total,
|
||||
node_type.meta,
|
||||
style,
|
||||
);
|
||||
|
||||
let cols = lua::serialize::<NodeUiMetadata>(lua, &meta)
|
||||
@ -789,7 +781,7 @@ fn draw_table<B: Backend>(
|
||||
.map(|x| Cell::from(x.to_owned()))
|
||||
.collect::<Vec<Cell>>();
|
||||
|
||||
Row::new(cols).style(style.into())
|
||||
Row::new(cols)
|
||||
})
|
||||
.collect::<Vec<Row>>()
|
||||
})
|
||||
@ -810,9 +802,9 @@ fn draw_table<B: Backend>(
|
||||
} else {
|
||||
&app.pwd
|
||||
}
|
||||
.trim_matches('/')
|
||||
.replace('\\', "\\\\")
|
||||
.replace('\n', "\\n");
|
||||
.trim_matches('/');
|
||||
|
||||
let pwd = path::escape(pwd);
|
||||
|
||||
let vroot_indicator = if app.vroot.is_some() { "vroot:" } else { "" };
|
||||
|
||||
@ -886,8 +878,6 @@ fn draw_selection<B: Backend>(
|
||||
lua::serialize::<Node>(lua, n)
|
||||
.map(|n| lua::call(lua, f, n).unwrap_or_else(|e| e.to_string()))
|
||||
.unwrap_or_else(|e| e.to_string())
|
||||
.replace('\\', "\\\\")
|
||||
.replace('\n', "\\n")
|
||||
})
|
||||
.unwrap_or_else(|| n.absolute_path.clone());
|
||||
string_to_text(out)
|
||||
@ -1551,7 +1541,11 @@ mod tests {
|
||||
Style {
|
||||
fg: Some(Color::Cyan),
|
||||
bg: Some(Color::Magenta),
|
||||
add_modifiers: modifier(Modifier::CrossedOut),
|
||||
add_modifiers: Some(
|
||||
vec![Modifier::Bold, Modifier::CrossedOut]
|
||||
.into_iter()
|
||||
.collect()
|
||||
),
|
||||
sub_modifiers: modifier(Modifier::Italic),
|
||||
}
|
||||
);
|
||||
@ -1561,7 +1555,11 @@ mod tests {
|
||||
Style {
|
||||
fg: Some(Color::Red),
|
||||
bg: Some(Color::Magenta),
|
||||
add_modifiers: modifier(Modifier::Bold),
|
||||
add_modifiers: Some(
|
||||
vec![Modifier::Bold, Modifier::CrossedOut]
|
||||
.into_iter()
|
||||
.collect()
|
||||
),
|
||||
sub_modifiers: modifier(Modifier::Italic),
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user