mirror of
https://github.com/theniceboy/.config.git
synced 2025-12-26 14:44:57 +08:00
upgrade yazi plugins
This commit is contained in:
parent
4782aca481
commit
1dd8680f69
12 changed files with 990 additions and 529 deletions
|
|
@ -1,32 +1,32 @@
|
|||
[[plugin.deps]]
|
||||
use = "llanosrocas/yaziline"
|
||||
rev = "e06c47f"
|
||||
hash = "7df84299e89ffefd4686d1e20a295c8a"
|
||||
rev = "e79b067"
|
||||
hash = "f590c5b7d0730e8d6023b1b34ddf7ead"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "Rolv-Apneseth/starship"
|
||||
rev = "6c639b4"
|
||||
hash = "5614c46d5de76623c283d415bfac8025"
|
||||
rev = "6a0f3f7"
|
||||
hash = "d5a74d95c2e8194ac352c5e10e81e64f"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:git"
|
||||
rev = "5186af7"
|
||||
hash = "771f18427fb75fb19990ce602bb322f4"
|
||||
rev = "5eea960"
|
||||
hash = "e01006bfb38951ea89031bd918b20dbb"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "yazi-rs/plugins:smart-enter"
|
||||
rev = "5186af7"
|
||||
hash = "aef2b1a805b80cce573bb766f1459d88"
|
||||
rev = "5eea960"
|
||||
hash = "56fdabc96fc1f4d53c96eb884b02a5be"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "h-hg/yamb"
|
||||
rev = "3f7c51f"
|
||||
hash = "e11b980e5635f0fbabd80931b1a1347e"
|
||||
rev = "22af003"
|
||||
hash = "7cc42012a7c2099f80064d228feb8d44"
|
||||
|
||||
[[plugin.deps]]
|
||||
use = "KKV9/compress"
|
||||
rev = "60b24af"
|
||||
hash = "ee025be766240cc98e671754ac836da3"
|
||||
rev = "9fc8fe0"
|
||||
hash = "2ad086899bf368fba211a33d8f68c39d"
|
||||
|
||||
[flavor]
|
||||
deps = []
|
||||
|
|
|
|||
|
|
@ -1,48 +1,173 @@
|
|||
# ~~archive.yazi~~ compress.yazi
|
||||
<h1 align="center">🗜️ compress.yazi</h1>
|
||||
<p align="center">
|
||||
<b>A blazing fast, flexible archive plugin for <a href="https://github.com/sxyazi/yazi">Yazi</a></b><br>
|
||||
<i>Effortlessly compress your files and folders with style!</i>
|
||||
</p>
|
||||
|
||||
A Yazi plugin that compresses selected files to an archive. Supporting yazi versions 0.2.5 and up.
|
||||
---
|
||||
|
||||
## Supported file types
|
||||
## 📖 Table of Contents
|
||||
|
||||
| Extention | Unix Command | Windows Command |
|
||||
| ------------- | ------------- | --------------- |
|
||||
| .zip | zip -r | 7z a -tzip |
|
||||
| .7z | 7z a | 7z a |
|
||||
| .tar | tar rpf | tar rpf |
|
||||
| .tar.gz | gzip | 7z a -tgzip |
|
||||
| .tar.xz | xz | 7z a -txz |
|
||||
| .tar.bz2 | bzip2 | 7z a -tbzip2 |
|
||||
| .tar.zst | zstd | zstd |
|
||||
- [Features](#-features)
|
||||
- [Supported File Types](#-supported-file-types)
|
||||
- [Installation](#%EF%B8%8F-installation)
|
||||
- [Keymap Example](#-keymap-example)
|
||||
- [Usage](#%EF%B8%8F-usage)
|
||||
- [Flags](#%EF%B8%8F-flags)
|
||||
- [Tips](#-tips)
|
||||
- [Credits](#-credits)
|
||||
|
||||
---
|
||||
|
||||
**NOTE:** Windows users are required to install 7-Zip and add 7z.exe to the `path` environment variable, only tar archives will be available otherwise.
|
||||
## 🚀 Features
|
||||
|
||||
- 🗂️ **Multi-format support:** zip, 7z, rar, tar, tar.gz, tar.xz, tar.bz2, tar.zst, tar.lz4, tar.lha
|
||||
- 🌍 **Cross-platform:** Works on Unix & Windows
|
||||
- 🔒 **Password protection:** Secure your archives (zip/7z/rar)
|
||||
- 🛡️ **Header encryption:** Hide file lists (7z/rar)
|
||||
- ⚡ **Compression level:** Choose your balance of speed vs. size
|
||||
- 🛑 **Overwrite safety:** Never lose files by accident
|
||||
- 🎯 **Seamless Yazi integration:** Fast, native-like UX
|
||||
|
||||
## Install
|
||||
---
|
||||
|
||||
## 📦 Supported File Types
|
||||
|
||||
| Extension | Default Command | 7z Command | Bsdtar Command (Win10+ & Unix) |
|
||||
| ------------- | ----------------- | -------------- | ------------------------------ |
|
||||
| `.zip` | `zip -r` | `7z a -tzip` | `tar -caf` |
|
||||
| `.7z` | `7z a` | `7z a` | |
|
||||
| `.rar` | `rar a` | | |
|
||||
| `.tar` | `tar rpf` | | `tar rpf` |
|
||||
| `.tar.gz` | `tar rpf + gzip` | `7z a -tgzip` | `tar -czf` |
|
||||
| `.tar.xz` | `tar rpf + xz` | `7z a -txz` | `tar -cJf` |
|
||||
| `.tar.bz2` | `tar rpf + bzip2` | `7z a -tbzip2` | `tar -cjf` |
|
||||
| `.tar.zst` | `tar rpf + zstd` | | `tar --zstd -cf` |
|
||||
| `.tar.lz4` | `tar rpf + lz4` | | |
|
||||
| `.tar.lha` | `tar rpf + lha` | | |
|
||||
|
||||
---
|
||||
|
||||
## ⚡️ Installation
|
||||
|
||||
```bash
|
||||
# For Unix platforms
|
||||
# Unix
|
||||
git clone https://github.com/KKV9/compress.yazi.git ~/.config/yazi/plugins/compress.yazi
|
||||
|
||||
## For Windows
|
||||
# Windows (CMD, not PowerShell!)
|
||||
git clone https://github.com/KKV9/compress.yazi.git %AppData%\yazi\config\plugins\compress.yazi
|
||||
|
||||
# Or with yazi plugin manager
|
||||
ya pack -a KKV9/compress
|
||||
ya pkg add KKV9/compress
|
||||
```
|
||||
|
||||
- Add this to your `keymap.toml`:
|
||||
---
|
||||
|
||||
### 🔧 Extras (Windows)
|
||||
|
||||
To enable additional compression formats and features on Windows, follow these steps:
|
||||
|
||||
1. **Install [7-Zip](https://www.7-zip.org/):**
|
||||
Add `C:\Program Files\7-Zip` to your `PATH`.
|
||||
This enables support for `.7z` archives and password-protected `.zip` files.
|
||||
|
||||
2. **Alternative: Install [Nanazip](https://github.com/M2Team/NanaZip):**
|
||||
A modern alternative to 7-Zip with similar functionality and extra features.
|
||||
|
||||
3. **Install [WinRAR](https://www.win-rar.com/download.html):**
|
||||
Add `C:\Program Files\WinRAR` to your `PATH`.
|
||||
This enables support for `.rar` archives.
|
||||
|
||||
4. **Install Additional Tools:**
|
||||
To use formats like `lha`, `lz4`, `gzip`, etc., install their respective tools and ensure they are added to your `PATH`.
|
||||
|
||||
---
|
||||
|
||||
## 🎹 Keymap Example
|
||||
|
||||
Add this to your `keymap.toml`:
|
||||
|
||||
|
||||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "c", "a" ]
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "a" ]
|
||||
run = "plugin compress"
|
||||
desc = "Archive selected files"
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "p" ]
|
||||
run = "plugin compress -p"
|
||||
desc = "Archive selected files (password)"
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "h" ]
|
||||
run = "plugin compress -ph"
|
||||
desc = "Archive selected files (password+header)"
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "l" ]
|
||||
run = "plugin compress -l"
|
||||
desc = "Archive selected files (compression level)"
|
||||
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "u" ]
|
||||
run = "plugin compress -phl"
|
||||
desc = "Archive selected files (password+header+level)"
|
||||
```
|
||||
|
||||
## Usage
|
||||
---
|
||||
|
||||
- Select files or folders to add, then press `c` `a` to create a new archive.
|
||||
- Type a name for the new file.
|
||||
- The file extention must match one of the supported filetype extentions.
|
||||
- The desired archive/compression command must be installed on your system.
|
||||
## 🛠️ Usage
|
||||
|
||||
1. **Select files/folders** in Yazi.
|
||||
2. Press <kbd>c</kbd> <kbd>a</kbd> to open the archive dialog.
|
||||
3. Choose:
|
||||
- <kbd>a</kbd> for a standard archive
|
||||
- <kbd>p</kbd> for password protection (zip/7z/rar)
|
||||
- <kbd>h</kbd> to encrypt header (7z/rar)
|
||||
- <kbd>l</kbd> to set compression level (all compression algorithims)
|
||||
- <kbd>u</kbd> for all options together
|
||||
4. **Type a name** for your archive (or leave blank for suggested name).
|
||||
5. **Enter password** and/or **compression level** if prompted.
|
||||
6. **Overwrite protect** if a file already exists, the new file will be given a suffix _#.
|
||||
7. Enjoy your shiny new archive!
|
||||
|
||||
---
|
||||
|
||||
## 🏳️🌈 Flags
|
||||
|
||||
- Combine flags for more power!
|
||||
- when separating flags with spaces, make sure to single quote them (eg., `'-ph rar'`)
|
||||
- `-p` Password protect (zip/7z/rar)
|
||||
- `-h` Encrypt header (7z/rar)
|
||||
- `-l` Set compression level (all compression algorithims)
|
||||
- `<extention>` Specify a default extention (eg., `7z`, `tar.gz`)
|
||||
|
||||
#### Combining multiple flags:
|
||||
```toml
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "7" ]
|
||||
run = "plugin compress '-ph 7z'"
|
||||
desc = "Archive selected files to 7z (password+header)"
|
||||
[[mgr.prepend_keymap]]
|
||||
on = [ "c", "a", "r" ]
|
||||
run = "plugin compress '-p -l rar'"
|
||||
desc = "Archive selected files to rar (password+level)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
- The file extension **must** match a supported type.
|
||||
- The required compression tool **must** be installed and in your `PATH` (7zip/rar etc.).
|
||||
- If no extention is provided, the default extention (zip) will be appended automatically.
|
||||
|
||||
---
|
||||
|
||||
## 📣 Credits
|
||||
|
||||
Made with ❤️ for [Yazi](https://github.com/sxyazi/yazi) by [KKV9](https://github.com/KKV9).
|
||||
Contributions are welcome! Feel free to submit a pull request.
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,228 +1,494 @@
|
|||
-- Send error notification
|
||||
local function notify_error(message, urgency)
|
||||
ya.notify({
|
||||
title = "Archive",
|
||||
content = message,
|
||||
level = urgency,
|
||||
timeout = 5,
|
||||
})
|
||||
end
|
||||
|
||||
-- Check for windows
|
||||
local is_windows = ya.target_family() == "windows"
|
||||
-- Define flags and strings
|
||||
local is_password, is_encrypted, is_level, cmd_password, cmd_level, default_extension = false, false, false, "", "", "zip"
|
||||
|
||||
-- Make table of selected or hovered: path = filenames
|
||||
local selected_or_hovered = ya.sync(function()
|
||||
local tab, paths, names, path_fnames = cx.active, {}, {}, {}
|
||||
for _, u in pairs(tab.selected) do
|
||||
paths[#paths + 1] = tostring(u:parent())
|
||||
names[#names + 1] = tostring(u:name())
|
||||
end
|
||||
if #paths == 0 and tab.current.hovered then
|
||||
paths[1] = tostring(tab.current.hovered.url:parent())
|
||||
names[1] = tostring(tab.current.hovered.name)
|
||||
end
|
||||
for idx, name in ipairs(names) do
|
||||
if not path_fnames[paths[idx]] then
|
||||
path_fnames[paths[idx]] = {}
|
||||
end
|
||||
table.insert(path_fnames[paths[idx]], name)
|
||||
end
|
||||
return path_fnames, tostring(tab.current.cwd)
|
||||
end)
|
||||
-- Function to check valid filename
|
||||
local function is_valid_filename(name)
|
||||
-- Trim whitespace from both ends
|
||||
name = name:match("^%s*(.-)%s*$")
|
||||
if name == "" then
|
||||
return false
|
||||
end
|
||||
if is_windows then
|
||||
-- Windows forbidden chars and reserved names
|
||||
if name:find('[<>:"/\\|%?%*]') then
|
||||
return false
|
||||
end
|
||||
else
|
||||
-- Unix forbidden chars
|
||||
if name:find("/") or name:find("%z") then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check if archive command is available
|
||||
-- Function to send notifications
|
||||
local function notify_error(message, urgency)
|
||||
ya.notify(
|
||||
{
|
||||
title = "Archive",
|
||||
content = message,
|
||||
level = urgency,
|
||||
timeout = 5
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
-- Function to check if command is available
|
||||
local function is_command_available(cmd)
|
||||
local stat_cmd
|
||||
|
||||
if is_windows then
|
||||
stat_cmd = string.format("where %s > nul 2>&1", cmd)
|
||||
else
|
||||
stat_cmd = string.format("command -v %s >/dev/null 2>&1", cmd)
|
||||
end
|
||||
|
||||
local cmd_exists = os.execute(stat_cmd)
|
||||
if cmd_exists then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
local stat_cmd
|
||||
if is_windows then
|
||||
stat_cmd = string.format("where %s > nul 2>&1", cmd)
|
||||
else
|
||||
stat_cmd = string.format("command -v %s >/dev/null 2>&1", cmd)
|
||||
end
|
||||
local cmd_exists = os.execute(stat_cmd)
|
||||
if cmd_exists then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Archive command list --> string
|
||||
local function find_binary(cmd_list)
|
||||
for _, cmd in ipairs(cmd_list) do
|
||||
if is_command_available(cmd) then
|
||||
return cmd
|
||||
end
|
||||
end
|
||||
return cmd_list[1] -- Return first command as fallback
|
||||
-- Function to change command arrays --> string -- Use first command available or first command
|
||||
local function find_command_name(cmd_list)
|
||||
for _, cmd in ipairs(cmd_list) do
|
||||
if is_command_available(cmd) then
|
||||
return cmd
|
||||
end
|
||||
end
|
||||
return cmd_list[1] -- Return first command as fallback
|
||||
end
|
||||
|
||||
-- Check if file exists
|
||||
local function file_exists(name)
|
||||
local f = io.open(name, "r")
|
||||
if f ~= nil then
|
||||
io.close(f)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Append filename to it's parent directory
|
||||
-- Function to append filename to it's parent directory url
|
||||
local function combine_url(path, file)
|
||||
path, file = Url(path), Url(file)
|
||||
return tostring(path:join(file))
|
||||
path, file = Url(path), Url(file)
|
||||
return tostring(path:join(file))
|
||||
end
|
||||
|
||||
-- Function to make a table of selected or hovered files: path = filenames
|
||||
local selected_or_hovered =
|
||||
ya.sync(
|
||||
function()
|
||||
local tab, paths, names, path_fnames = cx.active, {}, {}, {}
|
||||
for _, u in pairs(tab.selected) do
|
||||
paths[#paths + 1] = tostring(u.parent)
|
||||
names[#names + 1] = tostring(u.name)
|
||||
end
|
||||
if #paths == 0 and tab.current.hovered then
|
||||
paths[1] = tostring(tab.current.hovered.url.parent)
|
||||
names[1] = tostring(tab.current.hovered.name)
|
||||
end
|
||||
for idx, name in ipairs(names) do
|
||||
if not path_fnames[paths[idx]] then
|
||||
path_fnames[paths[idx]] = {}
|
||||
end
|
||||
table.insert(path_fnames[paths[idx]], name)
|
||||
end
|
||||
return path_fnames, names, tostring(tab.current.cwd)
|
||||
end
|
||||
)
|
||||
|
||||
-- Table of archive commands
|
||||
local archive_commands = {
|
||||
["%.zip$"] = {
|
||||
{command = "zip", args = {"-r"}, level_arg = "-", level_min = 0, level_max = 9, passwordable = true},
|
||||
{
|
||||
command = {"7z", "7zz", "7za"},
|
||||
args = {"a", "-tzip"},
|
||||
level_arg = "-mx=",
|
||||
level_min = 0,
|
||||
level_max = 9,
|
||||
passwordable = true
|
||||
},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"-caf"},
|
||||
level_arg = {"--option", "compression-level="},
|
||||
level_min = 1,
|
||||
level_max = 9
|
||||
}
|
||||
},
|
||||
["%.7z$"] = {
|
||||
{
|
||||
command = {"7z", "7zz", "7za"},
|
||||
args = {"a"},
|
||||
level_arg = "-mx=",
|
||||
level_min = 0,
|
||||
level_max = 9,
|
||||
header_arg = "-mhe=on",
|
||||
passwordable = true
|
||||
}
|
||||
},
|
||||
["%.rar$"] = {
|
||||
{
|
||||
command = "rar",
|
||||
args = {"a"},
|
||||
level_arg = "-m",
|
||||
level_min = 0,
|
||||
level_max = 5,
|
||||
header_arg = "-hp",
|
||||
passwordable = true
|
||||
}
|
||||
},
|
||||
["%.tar.gz$"] = {
|
||||
{command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "gzip"},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-mx=",
|
||||
level_min = 1,
|
||||
level_max = 9,
|
||||
compress = "7z",
|
||||
compress_args = {"a", "-tgzip"}
|
||||
},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"-czf"},
|
||||
level_arg = {"--option", "gzip:compression-level="},
|
||||
level_min = 1,
|
||||
level_max = 9
|
||||
}
|
||||
},
|
||||
["%.tar.xz$"] = {
|
||||
{command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "xz"},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-mx=",
|
||||
level_min = 1,
|
||||
level_max = 9,
|
||||
compress = "7z",
|
||||
compress_args = {"a", "-txz"}
|
||||
},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"-cJf"},
|
||||
level_arg = {"--option", "xz:compression-level="},
|
||||
level_min = 1,
|
||||
level_max = 9
|
||||
}
|
||||
},
|
||||
["%.tar.bz2$"] = {
|
||||
{command = {"tar", "bsdtar"}, args = {"rpf"}, level_arg = "-", level_min = 1, level_max = 9, compress = "bzip2"},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-mx=",
|
||||
level_min = 1,
|
||||
level_max = 9,
|
||||
compress = "7z",
|
||||
compress_args = {"a", "-tbzip2"}
|
||||
},
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"-cjf"},
|
||||
level_arg = {"--option", "bzip2:compression-level="},
|
||||
level_min = 1,
|
||||
level_max = 9
|
||||
}
|
||||
},
|
||||
["%.tar.zst$"] = {
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-",
|
||||
level_min = 1,
|
||||
level_max = 22,
|
||||
compress = "zstd",
|
||||
compress_args = {"--ultra"}
|
||||
}
|
||||
},
|
||||
["%.tar.lz4$"] = {
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-",
|
||||
level_min = 1,
|
||||
level_max = 12,
|
||||
compress = "lz4"
|
||||
}
|
||||
},
|
||||
["%.tar.lha$"] = {
|
||||
{
|
||||
command = {"tar", "bsdtar"},
|
||||
args = {"rpf"},
|
||||
level_arg = "-o",
|
||||
level_min = 5,
|
||||
level_max = 7,
|
||||
compress = "lha",
|
||||
compress_args = {"-a"}
|
||||
}
|
||||
},
|
||||
["%.tar$"] = {
|
||||
{command = {"tar", "bsdtar"}, args = {"rpf"}}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
entry = function()
|
||||
-- Exit visual mode
|
||||
ya.manager_emit("escape", { visual = true })
|
||||
entry = function(_, job)
|
||||
-- Parse flags and default extension
|
||||
if job.args ~= nil then
|
||||
for _, arg in ipairs(job.args) do
|
||||
if arg:match("^%-(%w+)$") then
|
||||
-- Handle combined flags (e.g., -phl)
|
||||
for flag in arg:sub(2):gmatch(".") do
|
||||
if flag == "p" then
|
||||
is_password = true
|
||||
elseif flag == "h" then
|
||||
is_encrypted = true
|
||||
elseif flag == "l" then
|
||||
is_level = true
|
||||
end
|
||||
end
|
||||
elseif arg:match("^%w+$") then
|
||||
-- Handle default extension (e.g., 7z, zip)
|
||||
if archive_commands["%." .. arg .. "$"] then
|
||||
default_extension = arg
|
||||
else
|
||||
notify_error(string.format("Unsupported extension: %s", arg), "warn")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Define file table and output_dir (pwd)
|
||||
local path_fnames, output_dir = selected_or_hovered()
|
||||
-- Exit visual mode
|
||||
ya.emit("escape", {visual = true})
|
||||
-- Define file table and output_dir (pwd)
|
||||
local path_fnames, fnames, output_dir = selected_or_hovered()
|
||||
-- Get archive filename
|
||||
local output_name, event =
|
||||
ya.input(
|
||||
{
|
||||
title = "Create archive:",
|
||||
position = {"top-center", y = 3, w = 40}
|
||||
}
|
||||
)
|
||||
if event ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get input
|
||||
local output_name, event = ya.input({
|
||||
title = "Create archive:",
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if event ~= 1 then
|
||||
return
|
||||
end
|
||||
-- Determine the default name for the archive
|
||||
local default_name = #fnames == 1 and fnames[1] or Url(output_dir).name
|
||||
output_name = output_name == "" and string.format("%s.%s", default_name, default_extension) or output_name
|
||||
|
||||
-- Use appropriate archive command
|
||||
local archive_commands = {
|
||||
["%.zip$"] = { command = "zip", args = { "-r" } },
|
||||
["%.7z$"] = { command = { "7z", "7zz" }, args = { "a" } },
|
||||
["%.tar.gz$"] = { command = "tar", args = { "rpf" }, compress = "gzip" },
|
||||
["%.tar.xz$"] = { command = "tar", args = { "rpf" }, compress = "xz" },
|
||||
["%.tar.bz2$"] = { command = "tar", args = { "rpf" }, compress = "bzip2" },
|
||||
["%.tar.zst$"] = { command = "tar", args = { "rpf" }, compress = "zstd", compress_args = { "--rm" } },
|
||||
["%.tar$"] = { command = "tar", args = { "rpf" } },
|
||||
}
|
||||
-- Add default extension if none is specified
|
||||
if not output_name:match("%.%w+$") then
|
||||
output_name = string.format("%s.%s", output_name, default_extension)
|
||||
end
|
||||
|
||||
if is_windows then
|
||||
archive_commands = {
|
||||
["%.zip$"] = { command = "7z", args = { "a", "-tzip" } },
|
||||
["%.7z$"] = { command = "7z", args = { "a" } },
|
||||
["%.tar.gz$"] = {
|
||||
command = "tar",
|
||||
args = { "rpf" },
|
||||
compress = "7z",
|
||||
compress_args = { "a", "-tgzip", "-sdel", output_name },
|
||||
},
|
||||
["%.tar.xz$"] = {
|
||||
command = "tar",
|
||||
args = { "rpf" },
|
||||
compress = "7z",
|
||||
compress_args = { "a", "-txz", "-sdel", output_name },
|
||||
},
|
||||
["%.tar.bz2$"] = {
|
||||
command = "tar",
|
||||
args = { "rpf" },
|
||||
compress = "7z",
|
||||
compress_args = { "a", "-tbzip2", "-sdel", output_name },
|
||||
},
|
||||
["%.tar.zst$"] = { command = "tar", args = { "rpf" }, compress = "zstd", compress_args = { "--rm" } },
|
||||
["%.tar$"] = { command = "tar", args = { "rpf" } },
|
||||
}
|
||||
end
|
||||
-- Validate the final archive filename
|
||||
if not is_valid_filename(output_name) then
|
||||
notify_error("Invalid archive filename", "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- Match user input to archive command
|
||||
local archive_cmd, archive_args, archive_compress, archive_compress_args
|
||||
for pattern, cmd_pair in pairs(archive_commands) do
|
||||
if output_name:match(pattern) then
|
||||
archive_cmd = cmd_pair.command
|
||||
archive_args = cmd_pair.args
|
||||
archive_compress = cmd_pair.compress
|
||||
archive_compress_args = cmd_pair.compress_args or {}
|
||||
end
|
||||
end
|
||||
-- Match user input to archive command
|
||||
local archive_cmd,
|
||||
archive_args,
|
||||
archive_compress,
|
||||
archive_level_arg,
|
||||
archive_level_min,
|
||||
archive_level_max,
|
||||
archive_header_arg,
|
||||
archive_passwordable,
|
||||
archive_compress_args
|
||||
local matched_pattern = false
|
||||
for pattern, cmd_list in pairs(archive_commands) do
|
||||
if output_name:match(pattern) then
|
||||
matched_pattern = true -- Mark that file extension is correct
|
||||
for _, cmd in ipairs(cmd_list) do
|
||||
-- Check if archive_cmd is available
|
||||
local find_command = type(cmd.command) == "table" and find_command_name(cmd.command) or cmd.command
|
||||
if is_command_available(find_command) then
|
||||
-- Check if compress_cmd (if listed) is available
|
||||
if cmd.compress == nil or is_command_available(cmd.compress) then
|
||||
archive_cmd = find_command
|
||||
archive_args = cmd.args
|
||||
archive_compress = cmd.compress or ""
|
||||
archive_level_arg = is_level and cmd.level_arg or ""
|
||||
archive_level_min = cmd.level_min
|
||||
archive_level_max = cmd.level_max
|
||||
archive_header_arg = is_encrypted and cmd.header_arg or ""
|
||||
archive_passwordable = cmd.passwordable or false
|
||||
archive_compress_args = cmd.compress_args or {}
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if archive_cmd then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if archive command has multiple names
|
||||
if type(archive_cmd) == "table" then
|
||||
archive_cmd = find_binary(archive_cmd)
|
||||
end
|
||||
-- Check if no archive command is available for the extension
|
||||
if not matched_pattern then
|
||||
notify_error("Unsupported file extension", "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if no archive command is available for the extention
|
||||
if not archive_cmd then
|
||||
notify_error("Unsupported file extention", "error")
|
||||
return
|
||||
end
|
||||
-- Check if no suitable archive program was found
|
||||
if not archive_cmd then
|
||||
notify_error("Could not find a suitable archive program for the selected file extension", "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- Exit if archive command is not available
|
||||
if not is_command_available(archive_cmd) then
|
||||
notify_error(string.format("%s not available", archive_cmd), "error")
|
||||
return
|
||||
end
|
||||
-- Check if archive command has multiple names
|
||||
if type(archive_cmd) == "table" then
|
||||
archive_cmd = find_command_name(archive_cmd)
|
||||
end
|
||||
|
||||
-- Exit if compress command is not available
|
||||
if archive_compress and not is_command_available(archive_compress) then
|
||||
notify_error(string.format("%s compression not available", archive_compress), "error")
|
||||
return
|
||||
end
|
||||
-- Exit if archive command is not available
|
||||
if not is_command_available(archive_cmd) then
|
||||
notify_error(string.format("%s not available", archive_cmd), "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- If file exists show overwrite prompt
|
||||
local output_url = combine_url(output_dir, output_name)
|
||||
while true do
|
||||
if file_exists(output_url) then
|
||||
local overwrite_answer = ya.input({
|
||||
title = "Overwrite " .. output_name .. "? y/N:",
|
||||
position = { "top-center", y = 3, w = 40 },
|
||||
})
|
||||
if overwrite_answer:lower() ~= "y" then
|
||||
notify_error("Operation canceled", "warn")
|
||||
return -- If no overwrite selected, exit
|
||||
else
|
||||
local rm_status, rm_err = os.remove(output_url)
|
||||
if not rm_status then
|
||||
notify_error(string.format("Failed to remove %s, exit code %s", output_name, rm_err), "error")
|
||||
return
|
||||
end -- If overwrite fails, exit
|
||||
end
|
||||
end
|
||||
if archive_compress and not output_name:match("%.tar$") then
|
||||
output_name = output_name:match("(.*%.tar)") -- Test for .tar and .tar.*
|
||||
output_url = combine_url(output_dir, output_name) -- Update output_url
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Exit if compress command is not available
|
||||
if archive_compress ~= "" and not is_command_available(archive_compress) then
|
||||
notify_error(string.format("%s compression not available", archive_compress), "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- Add to output archive in each path, their respective files
|
||||
for path, names in pairs(path_fnames) do
|
||||
local archive_status, archive_err =
|
||||
Command(archive_cmd):args(archive_args):arg(output_url):args(names):cwd(path):spawn():wait()
|
||||
if not archive_status or not archive_status.success then
|
||||
notify_error(
|
||||
string.format(
|
||||
"%s with selected files failed, exit code %s",
|
||||
archive_args,
|
||||
archive_status and archive_status.code or archive_err
|
||||
),
|
||||
"error"
|
||||
)
|
||||
end
|
||||
end
|
||||
-- Add password arg if selected
|
||||
if archive_passwordable and is_password then
|
||||
local output_password, event =
|
||||
ya.input(
|
||||
{
|
||||
title = "Enter password:",
|
||||
obscure = true,
|
||||
position = {"top-center", y = 3, w = 40}
|
||||
}
|
||||
)
|
||||
if event ~= 1 then
|
||||
return
|
||||
end
|
||||
if output_password ~= "" then
|
||||
cmd_password = "-P" .. output_password
|
||||
if archive_cmd == "rar" and is_encrypted then
|
||||
cmd_password = archive_header_arg .. output_password -- Add archive arg for rar
|
||||
end
|
||||
table.insert(archive_args, cmd_password)
|
||||
end
|
||||
end
|
||||
|
||||
-- Use compress command if needed
|
||||
if archive_compress then
|
||||
local compress_status, compress_err =
|
||||
Command(archive_compress):args(archive_compress_args):arg(output_name):cwd(output_dir):spawn():wait()
|
||||
if not compress_status or not compress_status.success then
|
||||
notify_error(
|
||||
string.format(
|
||||
"%s with %s failed, exit code %s",
|
||||
archive_compress,
|
||||
output_name,
|
||||
compress_status and compress_status.code or compress_err
|
||||
),
|
||||
"error"
|
||||
)
|
||||
end
|
||||
end
|
||||
end,
|
||||
-- Add header arg if selected for 7z
|
||||
if is_encrypted and archive_header_arg ~= "" and archive_cmd ~= "rar" then
|
||||
table.insert(archive_args, archive_header_arg)
|
||||
end
|
||||
|
||||
-- Add level arg if selected
|
||||
if archive_level_arg ~= "" and is_level then
|
||||
local output_level, event =
|
||||
ya.input(
|
||||
{
|
||||
title = string.format("Enter compression level (%s - %s)", archive_level_min, archive_level_max),
|
||||
position = {"top-center", y = 3, w = 40}
|
||||
}
|
||||
)
|
||||
if event ~= 1 then
|
||||
return
|
||||
end
|
||||
-- Validate user input for compression level
|
||||
if
|
||||
output_level ~= "" and tonumber(output_level) ~= nil and tonumber(output_level) >= archive_level_min and
|
||||
tonumber(output_level) <= archive_level_max
|
||||
then
|
||||
cmd_level =
|
||||
type(archive_level_arg) == "table" and archive_level_arg[#archive_level_arg] .. output_level or
|
||||
archive_level_arg .. output_level
|
||||
local target_args = archive_compress == "" and archive_args or archive_compress_args
|
||||
if type(archive_level_arg) == "table" then
|
||||
-- Insert each element of archive_level_arg (except last) into target_args at the correct position
|
||||
for i = 1, #archive_level_arg - 1 do
|
||||
table.insert(target_args, i, archive_level_arg[i])
|
||||
end
|
||||
table.insert(target_args, #archive_level_arg, cmd_level) -- Add level at the end
|
||||
else
|
||||
-- Insert the compression level argument at the start if not a table
|
||||
table.insert(target_args, 1, cmd_level)
|
||||
end
|
||||
else
|
||||
notify_error("Invalid level specified. Using defaults.", "warn")
|
||||
end
|
||||
end
|
||||
|
||||
-- Store the original output name for later use
|
||||
local original_name = output_name
|
||||
|
||||
-- If compression is needed, adjust the output name to exclude extensions like ".tar"
|
||||
if archive_compress ~= "" then
|
||||
output_name = output_name:match("(.*%.tar)") or output_name
|
||||
end
|
||||
|
||||
-- Create a temporary directory for intermediate files
|
||||
local temp_dir_name = ".tmp_compress"
|
||||
local temp_dir = combine_url(output_dir, temp_dir_name)
|
||||
local temp_dir, _ = tostring(fs.unique_name(Url(temp_dir)))
|
||||
|
||||
-- Attempt to create the temporary directory
|
||||
local temp_dir_status, temp_dir_err = fs.create("dir_all", Url(temp_dir))
|
||||
if not temp_dir_status then
|
||||
-- Notify the user if the temporary directory creation fails
|
||||
notify_error(string.format("Failed to create temp directory, error code: %s", temp_dir_err), "error")
|
||||
return
|
||||
end
|
||||
|
||||
-- Define the temporary output file path within the temporary directory
|
||||
local temp_output_url = combine_url(temp_dir, output_name)
|
||||
|
||||
-- Add files to the output archive
|
||||
for filepath, filenames in pairs(path_fnames) do
|
||||
-- Execute the archive command for each path and its respective files
|
||||
local archive_status, archive_err =
|
||||
Command(archive_cmd):arg(archive_args):arg(temp_output_url):arg(filenames):cwd(filepath):spawn():wait()
|
||||
if not archive_status or not archive_status.success then
|
||||
-- Notify the user if the archiving process fails and clean up the temporary directory
|
||||
notify_error(string.format("Failed to create archive %s with '%s', error: %s", output_name, archive_cmd, archive_err), "error")
|
||||
local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir))
|
||||
if not cleanup_status then
|
||||
notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- If compression is required, execute the compression command
|
||||
if archive_compress ~= "" then
|
||||
local compress_status, compress_err =
|
||||
Command(archive_compress):arg(archive_compress_args):arg(temp_output_url):spawn():wait()
|
||||
if not compress_status or not compress_status.success then
|
||||
-- Notify the user if the compression process fails and clean up the temporary directory
|
||||
notify_error(string.format("Failed to compress archive %s with '%s', error: %s", output_name, archive_compress, compress_err), "error")
|
||||
local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir))
|
||||
if not cleanup_status then
|
||||
notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Move the final file from the temporary directory to the output directory
|
||||
local final_output_url, temp_url_processed = combine_url(output_dir, original_name), combine_url(temp_dir, original_name)
|
||||
final_output_url, _ = tostring(fs.unique_name(Url(final_output_url)))
|
||||
local move_status, move_err = os.rename(temp_url_processed, final_output_url)
|
||||
if not move_status then
|
||||
-- Notify the user if the move operation fails and clean up the temporary directory
|
||||
notify_error(string.format("Failed to move %s to %s, error: %s", temp_url_processed, final_output_url, move_err), "error")
|
||||
local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir))
|
||||
if not cleanup_status then
|
||||
notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Cleanup the temporary directory after successful operation
|
||||
local cleanup_status, cleanup_err = fs.remove("dir_all", Url(temp_dir))
|
||||
if not cleanup_status then
|
||||
notify_error(string.format("Failed to clean up temporary directory %s, error: %s", temp_dir, cleanup_err), "error")
|
||||
end
|
||||
end
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
# git.yazi
|
||||
|
||||
> [!NOTE]
|
||||
> Yazi v25.2.7 or later is required for this plugin to work.
|
||||
|
||||
Show the status of Git file changes as linemode in the file list.
|
||||
|
||||
https://github.com/user-attachments/assets/34976be9-a871-4ffe-9d5a-c4cdd0bf4576
|
||||
|
|
@ -10,7 +7,7 @@ https://github.com/user-attachments/assets/34976be9-a871-4ffe-9d5a-c4cdd0bf4576
|
|||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a yazi-rs/plugins:git
|
||||
ya pkg add yazi-rs/plugins:git
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
|
@ -39,38 +36,38 @@ run = "git"
|
|||
|
||||
You can customize the [Style](https://yazi-rs.github.io/docs/plugins/layout#style) of the status sign with:
|
||||
|
||||
- `THEME.git.modified`
|
||||
- `THEME.git.added`
|
||||
- `THEME.git.untracked`
|
||||
- `THEME.git.ignored`
|
||||
- `THEME.git.deleted`
|
||||
- `THEME.git.updated`
|
||||
- `th.git.modified`
|
||||
- `th.git.added`
|
||||
- `th.git.untracked`
|
||||
- `th.git.ignored`
|
||||
- `th.git.deleted`
|
||||
- `th.git.updated`
|
||||
|
||||
For example:
|
||||
|
||||
```lua
|
||||
-- ~/.config/yazi/init.lua
|
||||
THEME.git = THEME.git or {}
|
||||
THEME.git.modified = ui.Style():fg("blue")
|
||||
THEME.git.deleted = ui.Style():fg("red"):bold()
|
||||
th.git = th.git or {}
|
||||
th.git.modified = ui.Style():fg("blue")
|
||||
th.git.deleted = ui.Style():fg("red"):bold()
|
||||
```
|
||||
|
||||
You can also customize the text of the status sign with:
|
||||
|
||||
- `THEME.git.modified_sign`
|
||||
- `THEME.git.added_sign`
|
||||
- `THEME.git.untracked_sign`
|
||||
- `THEME.git.ignored_sign`
|
||||
- `THEME.git.deleted_sign`
|
||||
- `THEME.git.updated_sign`
|
||||
- `th.git.modified_sign`
|
||||
- `th.git.added_sign`
|
||||
- `th.git.untracked_sign`
|
||||
- `th.git.ignored_sign`
|
||||
- `th.git.deleted_sign`
|
||||
- `th.git.updated_sign`
|
||||
|
||||
For example:
|
||||
|
||||
```lua
|
||||
-- ~/.config/yazi/init.lua
|
||||
THEME.git = THEME.git or {}
|
||||
THEME.git.modified_sign = "M"
|
||||
THEME.git.deleted_sign = "D"
|
||||
th.git = th.git or {}
|
||||
th.git.modified_sign = "M"
|
||||
th.git.deleted_sign = "D"
|
||||
```
|
||||
|
||||
## License
|
||||
|
|
|
|||
|
|
@ -1,29 +1,45 @@
|
|||
--- @since 25.2.7
|
||||
--- @since 25.5.31
|
||||
|
||||
local WIN = ya.target_family() == "windows"
|
||||
local PATS = {
|
||||
{ "[MT]", 6 }, -- Modified
|
||||
{ "[AC]", 5 }, -- Added
|
||||
{ "?$", 4 }, -- Untracked
|
||||
{ "!$", 3 }, -- Ignored
|
||||
{ "D", 2 }, -- Deleted
|
||||
{ "U", 1 }, -- Updated
|
||||
{ "[AD][AD]", 1 }, -- Updated
|
||||
local WINDOWS = ya.target_family() == "windows"
|
||||
|
||||
-- The code of supported git status,
|
||||
-- also used to determine which status to show for directories when they contain different statuses
|
||||
-- see `bubble_up`
|
||||
local CODES = {
|
||||
excluded = 100, -- ignored directory
|
||||
ignored = 6, -- ignored file
|
||||
untracked = 5,
|
||||
modified = 4,
|
||||
added = 3,
|
||||
deleted = 2,
|
||||
updated = 1,
|
||||
unknown = 0,
|
||||
}
|
||||
|
||||
local PATTERNS = {
|
||||
{ "!$", CODES.ignored },
|
||||
{ "?$", CODES.untracked },
|
||||
{ "[MT]", CODES.modified },
|
||||
{ "[AC]", CODES.added },
|
||||
{ "D", CODES.deleted },
|
||||
{ "U", CODES.updated },
|
||||
{ "[AD][AD]", CODES.updated },
|
||||
}
|
||||
|
||||
local function match(line)
|
||||
local signs = line:sub(1, 2)
|
||||
for _, p in ipairs(PATS) do
|
||||
local path
|
||||
if signs:find(p[1]) then
|
||||
for _, p in ipairs(PATTERNS) do
|
||||
local path, pattern, code = nil, p[1], p[2]
|
||||
if signs:find(pattern) then
|
||||
path = line:sub(4, 4) == '"' and line:sub(5, -2) or line:sub(4)
|
||||
path = WIN and path:gsub("/", "\\") or path
|
||||
path = WINDOWS and path:gsub("/", "\\") or path
|
||||
end
|
||||
if not path then
|
||||
elseif path:find("[/\\]$") then
|
||||
return p[2] == 3 and 30 or p[2], path:sub(1, -2)
|
||||
-- Mark the ignored directory as `excluded`, so we can process it further within `propagate_down`
|
||||
return code == CODES.ignored and CODES.excluded or code, path:sub(1, -2)
|
||||
else
|
||||
return p[2], path
|
||||
return code, path
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -44,34 +60,36 @@ local function root(cwd)
|
|||
if cha and (cha.is_dir or is_worktree(next)) then
|
||||
return tostring(cwd)
|
||||
end
|
||||
cwd = cwd:parent()
|
||||
cwd = cwd.parent
|
||||
until not cwd
|
||||
end
|
||||
|
||||
local function bubble_up(changed)
|
||||
local new, empty = {}, Url("")
|
||||
for k, v in pairs(changed) do
|
||||
if v ~= 3 and v ~= 30 then
|
||||
local url = Url(k):parent()
|
||||
for path, code in pairs(changed) do
|
||||
if code ~= CODES.ignored then
|
||||
local url = Url(path).parent
|
||||
while url and url ~= empty do
|
||||
local s = tostring(url)
|
||||
new[s] = (new[s] or 0) > v and new[s] or v
|
||||
url = url:parent()
|
||||
new[s] = (new[s] or CODES.unknown) > code and new[s] or code
|
||||
url = url.parent
|
||||
end
|
||||
end
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
local function propagate_down(ignored, cwd, repo)
|
||||
local function propagate_down(excluded, cwd, repo)
|
||||
local new, rel = {}, cwd:strip_prefix(repo)
|
||||
for k, v in pairs(ignored) do
|
||||
if v == 30 then
|
||||
if rel:starts_with(k) then
|
||||
new[tostring(repo:join(rel))] = 30
|
||||
elseif cwd == repo:join(k):parent() then
|
||||
new[k] = 3
|
||||
end
|
||||
for _, path in ipairs(excluded) do
|
||||
if rel:starts_with(path) then
|
||||
-- If `cwd` is a subdirectory of an excluded directory, also mark it as `excluded`
|
||||
new[tostring(cwd)] = CODES.excluded
|
||||
elseif cwd == repo:join(path).parent then
|
||||
-- If `path` is a direct subdirectory of `cwd`, mark it as `ignored`
|
||||
new[path] = CODES.ignored
|
||||
else
|
||||
-- Skipping, we only care about `cwd` itself and its direct subdirectories for maximum performance
|
||||
end
|
||||
end
|
||||
return new
|
||||
|
|
@ -80,84 +98,84 @@ end
|
|||
local add = ya.sync(function(st, cwd, repo, changed)
|
||||
st.dirs[cwd] = repo
|
||||
st.repos[repo] = st.repos[repo] or {}
|
||||
for k, v in pairs(changed) do
|
||||
if v == 0 then
|
||||
st.repos[repo][k] = nil
|
||||
elseif v == 30 then
|
||||
st.dirs[k] = ""
|
||||
for path, code in pairs(changed) do
|
||||
if code == CODES.unknown then
|
||||
st.repos[repo][path] = nil
|
||||
elseif code == CODES.excluded then
|
||||
-- Mark the directory with a special value `excluded` so that it can be distinguished during UI rendering
|
||||
st.dirs[path] = CODES.excluded
|
||||
else
|
||||
st.repos[repo][k] = v
|
||||
st.repos[repo][path] = code
|
||||
end
|
||||
end
|
||||
ya.render()
|
||||
end)
|
||||
|
||||
local remove = ya.sync(function(st, cwd)
|
||||
local dir = st.dirs[cwd]
|
||||
if not dir then
|
||||
local repo = st.dirs[cwd]
|
||||
if not repo then
|
||||
return
|
||||
end
|
||||
|
||||
ya.render()
|
||||
st.dirs[cwd] = nil
|
||||
if not st.repos[dir] then
|
||||
if not st.repos[repo] then
|
||||
return
|
||||
end
|
||||
|
||||
for _, r in pairs(st.dirs) do
|
||||
if r == dir then
|
||||
if r == repo then
|
||||
return
|
||||
end
|
||||
end
|
||||
st.repos[dir] = nil
|
||||
st.repos[repo] = nil
|
||||
end)
|
||||
|
||||
local function setup(st, opts)
|
||||
st.dirs = {}
|
||||
st.repos = {}
|
||||
st.dirs = {} -- Mapping between a directory and its corresponding repository
|
||||
st.repos = {} -- Mapping between a repository and the status of each of its files
|
||||
|
||||
opts = opts or {}
|
||||
opts.order = opts.order or 1500
|
||||
|
||||
-- Chosen by ChatGPT fairly, PRs are welcome to adjust them
|
||||
local t = THEME.git or {}
|
||||
local t = th.git or {}
|
||||
local styles = {
|
||||
[6] = t.modified and ui.Style(t.modified) or ui.Style():fg("#ffa500"),
|
||||
[5] = t.added and ui.Style(t.added) or ui.Style():fg("#32cd32"),
|
||||
[4] = t.untracked and ui.Style(t.untracked) or ui.Style():fg("#a9a9a9"),
|
||||
[3] = t.ignored and ui.Style(t.ignored) or ui.Style():fg("#696969"),
|
||||
[2] = t.deleted and ui.Style(t.deleted) or ui.Style():fg("#ff4500"),
|
||||
[1] = t.updated and ui.Style(t.updated) or ui.Style():fg("#1e90ff"),
|
||||
[CODES.ignored] = t.ignored and ui.Style(t.ignored) or ui.Style():fg("darkgray"),
|
||||
[CODES.untracked] = t.untracked and ui.Style(t.untracked) or ui.Style():fg("magenta"),
|
||||
[CODES.modified] = t.modified and ui.Style(t.modified) or ui.Style():fg("yellow"),
|
||||
[CODES.added] = t.added and ui.Style(t.added) or ui.Style():fg("green"),
|
||||
[CODES.deleted] = t.deleted and ui.Style(t.deleted) or ui.Style():fg("red"),
|
||||
[CODES.updated] = t.updated and ui.Style(t.updated) or ui.Style():fg("yellow"),
|
||||
}
|
||||
local signs = {
|
||||
[6] = t.modified_sign and t.modified_sign or "",
|
||||
[5] = t.added_sign and t.added_sign or "",
|
||||
[4] = t.untracked_sign and t.untracked_sign or "",
|
||||
[3] = t.ignored_sign and t.ignored_sign or "",
|
||||
[2] = t.deleted_sign and t.deleted_sign or "",
|
||||
[1] = t.updated_sign and t.updated_sign or "U",
|
||||
[CODES.ignored] = t.ignored_sign or "",
|
||||
[CODES.untracked] = t.untracked_sign or "?",
|
||||
[CODES.modified] = t.modified_sign or "",
|
||||
[CODES.added] = t.added_sign or "",
|
||||
[CODES.deleted] = t.deleted_sign or "",
|
||||
[CODES.updated] = t.updated_sign or "",
|
||||
}
|
||||
|
||||
Linemode:children_add(function(self)
|
||||
local url = self._file.url
|
||||
local dir = st.dirs[tostring(url:parent())]
|
||||
local change
|
||||
if dir then
|
||||
change = dir == "" and 3 or st.repos[dir][tostring(url):sub(#dir + 2)]
|
||||
local repo = st.dirs[tostring(url.base)]
|
||||
local code
|
||||
if repo then
|
||||
code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)]
|
||||
end
|
||||
|
||||
if not change or signs[change] == "" then
|
||||
if not code or signs[code] == "" then
|
||||
return ""
|
||||
elseif self._file:is_hovered() then
|
||||
return ui.Line { " ", signs[change] }
|
||||
elseif self._file.is_hovered then
|
||||
return ui.Line { " ", signs[code] }
|
||||
else
|
||||
return ui.Line { " ", ui.Span(signs[change]):style(styles[change]) }
|
||||
return ui.Line { " ", ui.Span(signs[code]):style(styles[code]) }
|
||||
end
|
||||
end, opts.order)
|
||||
end
|
||||
|
||||
local function fetch(_, job)
|
||||
local cwd = job.files[1].url:parent()
|
||||
local cwd = job.files[1].url.base
|
||||
local repo = root(cwd)
|
||||
if not repo then
|
||||
remove(tostring(cwd))
|
||||
|
|
@ -165,42 +183,43 @@ local function fetch(_, job)
|
|||
end
|
||||
|
||||
local paths = {}
|
||||
for _, f in ipairs(job.files) do
|
||||
paths[#paths + 1] = tostring(f.url)
|
||||
for _, file in ipairs(job.files) do
|
||||
paths[#paths + 1] = tostring(file.url)
|
||||
end
|
||||
|
||||
-- stylua: ignore
|
||||
local output, err = Command("git")
|
||||
:cwd(tostring(cwd))
|
||||
:args({ "--no-optional-locks", "-c", "core.quotePath=", "status", "--porcelain", "-unormal", "--no-renames", "--ignored=matching" })
|
||||
:args(paths)
|
||||
:arg({ "--no-optional-locks", "-c", "core.quotePath=", "status", "--porcelain", "-unormal", "--no-renames", "--ignored=matching" })
|
||||
:arg(paths)
|
||||
:stdout(Command.PIPED)
|
||||
:output()
|
||||
if not output then
|
||||
return true, Err("Cannot spawn `git` command, error: %s", err)
|
||||
end
|
||||
|
||||
local changed, ignored = {}, {}
|
||||
local changed, excluded = {}, {}
|
||||
for line in output.stdout:gmatch("[^\r\n]+") do
|
||||
local sign, path = match(line)
|
||||
if sign == 30 then
|
||||
ignored[path] = sign
|
||||
local code, path = match(line)
|
||||
if code == CODES.excluded then
|
||||
excluded[#excluded + 1] = path
|
||||
else
|
||||
changed[path] = sign
|
||||
changed[path] = code
|
||||
end
|
||||
end
|
||||
|
||||
if job.files[1].cha.is_dir then
|
||||
ya.dict_merge(changed, bubble_up(changed))
|
||||
ya.dict_merge(changed, propagate_down(ignored, cwd, Url(repo)))
|
||||
else
|
||||
ya.dict_merge(changed, propagate_down(ignored, cwd, Url(repo)))
|
||||
end
|
||||
ya.dict_merge(changed, propagate_down(excluded, cwd, Url(repo)))
|
||||
|
||||
-- Reset the status of any files that don't appear in the output of `git status` to `unknown`,
|
||||
-- so that cleaning up outdated statuses from `st.repos`
|
||||
for _, path in ipairs(paths) do
|
||||
local s = path:sub(#repo + 2)
|
||||
changed[s] = changed[s] or CODES.unknown
|
||||
end
|
||||
|
||||
for _, p in ipairs(paths) do
|
||||
local s = p:sub(#repo + 2)
|
||||
changed[s] = changed[s] or 0
|
||||
end
|
||||
add(tostring(cwd), repo, changed)
|
||||
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a yazi-rs/plugins:smart-enter
|
||||
ya pkg add yazi-rs/plugins:smart-enter
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
|
@ -13,7 +13,7 @@ ya pack -a yazi-rs/plugins:smart-enter
|
|||
Bind your <kbd>l</kbd> key to the plugin, in your `~/.config/yazi/keymap.toml`:
|
||||
|
||||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
[[mgr.prepend_keymap]]
|
||||
on = "l"
|
||||
run = "plugin smart-enter"
|
||||
desc = "Enter the child directory, or open the file"
|
||||
|
|
@ -36,5 +36,5 @@ require("smart-enter"):setup {
|
|||
|
||||
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file.
|
||||
|
||||
[open]: https://yazi-rs.github.io/docs/configuration/keymap/#manager.open
|
||||
[enter]: https://yazi-rs.github.io/docs/configuration/keymap/#manager.enter
|
||||
[open]: https://yazi-rs.github.io/docs/configuration/keymap/#mgr.open
|
||||
[enter]: https://yazi-rs.github.io/docs/configuration/keymap/#mgr.enter
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
--- @since 25.2.7
|
||||
--- @since 25.5.31
|
||||
--- @sync entry
|
||||
|
||||
local function setup(self, opts) self.open_multi = opts.open_multi end
|
||||
|
||||
local function entry(self)
|
||||
local h = cx.active.current.hovered
|
||||
ya.manager_emit(h and h.cha.is_dir and "enter" or "open", { hovered = not self.open_multi })
|
||||
ya.emit(h and h.cha.is_dir and "enter" or "open", { hovered = not self.open_multi })
|
||||
end
|
||||
|
||||
return { entry = entry, setup = setup }
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ Starship prompt plugin for [Yazi](https://github.com/sxyazi/yazi)
|
|||
|
||||
## Requirements
|
||||
|
||||
- [Yazi](https://github.com/sxyazi/yazi)
|
||||
- [Yazi](https://github.com/sxyazi/yazi) (v25.4.8+)
|
||||
- [starship](https://github.com/starship/starship)
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
ya pack -a Rolv-Apneseth/starship
|
||||
ya pkg add Rolv-Apneseth/starship
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
|
@ -78,14 +78,13 @@ Tab.build = function(self, ...)
|
|||
|
||||
local c = self._chunks
|
||||
self._chunks = {
|
||||
c[1]:padding(ui.Padding.y(1)),
|
||||
c[2]:padding(ui.Padding(c[1].w > 0 and 0 or 1, c[3].w > 0 and 0 or 1, 1, 1)),
|
||||
c[3]:padding(ui.Padding.y(1)),
|
||||
c[1]:pad(ui.Pad.y(1)),
|
||||
c[2]:pad(ui.Pad(1, c[3].w > 0 and 0 or 1, 1, c[1].w > 0 and 0 or 1)),
|
||||
c[3]:pad(ui.Pad.y(1)),
|
||||
}
|
||||
|
||||
local style = THEME.manager.border_style
|
||||
local style = th.mgr.border_style
|
||||
self._base = ya.list_merge(self._base or {}, {
|
||||
ui.Border(ui.Border.ALL):area(self._area):type(ui.Border.ROUNDED):style(style),
|
||||
ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style),
|
||||
ui.Bar(ui.Bar.LEFT):area(self._chunks[1]):style(style),
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
--- @since 25.2.7
|
||||
--- @since 25.4.8
|
||||
|
||||
-- For development
|
||||
--[[ local function notify(message) ]]
|
||||
|
|
@ -89,19 +89,13 @@ return {
|
|||
if st.cwd ~= cwd then
|
||||
st.cwd = cwd
|
||||
|
||||
if ya.confirm then
|
||||
-- >= yazi 25.2.7
|
||||
ya.manager_emit("plugin", {
|
||||
st._id,
|
||||
ya.quote(tostring(cwd), true),
|
||||
})
|
||||
else
|
||||
-- < yazi 25.2.7
|
||||
ya.manager_emit("plugin", {
|
||||
st._id,
|
||||
args = ya.quote(tostring(cwd), true),
|
||||
})
|
||||
end
|
||||
-- `ya.emit` as of 25.5.28
|
||||
local emit = ya.emit or ya.manager_emit
|
||||
|
||||
emit("plugin", {
|
||||
st._id,
|
||||
ya.quote(tostring(cwd), true),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -110,12 +104,8 @@ return {
|
|||
ps.sub("tab", callback)
|
||||
end,
|
||||
|
||||
entry = function(_, job_or_args)
|
||||
-- yazi 2024-11-29 changed the way arguments are passed to the plugin
|
||||
-- entry point. They were moved inside {args = {...}}. If the user is using
|
||||
-- a version before this change, they can use the old implementation.
|
||||
-- https://github.com/sxyazi/yazi/pull/1966
|
||||
local args = job_or_args.args or job_or_args
|
||||
entry = function(_, job)
|
||||
local args = job.args
|
||||
local command = Command("starship")
|
||||
:arg("prompt")
|
||||
:stdin(Command.INHERIT)
|
||||
|
|
|
|||
|
|
@ -72,41 +72,41 @@ Add this to your `keymap.toml`:
|
|||
```toml
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "a" ]
|
||||
run = "plugin yamb --args=save"
|
||||
run = "plugin yamb save"
|
||||
desc = "Add bookmark"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "g" ]
|
||||
run = "plugin yamb --args=jump_by_key"
|
||||
run = "plugin yamb jump_by_key"
|
||||
desc = "Jump bookmark by key"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "G" ]
|
||||
run = "plugin yamb --args=jump_by_fzf"
|
||||
run = "plugin yamb jump_by_fzf"
|
||||
desc = "Jump bookmark by fzf"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "d" ]
|
||||
run = "plugin yamb --args=delete_by_key"
|
||||
run = "plugin yamb delete_by_key"
|
||||
desc = "Delete bookmark by key"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "D" ]
|
||||
run = "plugin yamb --args=delete_by_fzf"
|
||||
run = "plugin yamb delete_by_fzf"
|
||||
desc = "Delete bookmark by fzf"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "A" ]
|
||||
run = "plugin yamb --args=delete_all"
|
||||
run = "plugin yamb delete_all"
|
||||
desc = "Delete all bookmarks"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "r" ]
|
||||
run = "plugin yamb --args=rename_by_key"
|
||||
run = "plugin yamb rename_by_key"
|
||||
desc = "Rename bookmark by key"
|
||||
|
||||
[[manager.prepend_keymap]]
|
||||
on = [ "u", "R" ]
|
||||
run = "plugin yamb --args=rename_by_fzf"
|
||||
run = "plugin yamb rename_by_fzf"
|
||||
desc = "Rename bookmark by fzf"
|
||||
```
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ Read more about features and configuration [here](#features).
|
|||
|
||||
## Requirements
|
||||
|
||||
- yazi version >= 25.2.11
|
||||
- yazi version >= [25.5.28](https://github.com/sxyazi/yazi/releases/tag/v25.5.28)
|
||||
- Font with symbol support. For example [Nerd Fonts](https://www.nerdfonts.com/).
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
ya pack -a llanosrocas/yaziline
|
||||
ya pkg add llanosrocas/yaziline
|
||||
```
|
||||
|
||||
Or manually copy `init.lua` to the `~/.config/yazi/plugins/yaziline.yazi/init.lua`
|
||||
Or manually copy `main.lua` to the `~/.config/yazi/plugins/yaziline.yazi/main.lua`
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
@ -32,6 +32,12 @@ Optionally, configure line:
|
|||
```lua
|
||||
require("yaziline"):setup({
|
||||
color = "#98c379", -- main theme color
|
||||
secondary_color = "#5A6078", -- secondary color
|
||||
default_files_color = "darkgray", -- color of the file counter when it's inactive
|
||||
selected_files_color = "white",
|
||||
yanked_files_color = "green",
|
||||
cut_files_color = "red",
|
||||
|
||||
separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
|
||||
separator_open = "",
|
||||
separator_close = "",
|
||||
|
|
@ -39,14 +45,30 @@ require("yaziline"):setup({
|
|||
separator_close_thin = "",
|
||||
separator_head = "",
|
||||
separator_tail = "",
|
||||
|
||||
select_symbol = "",
|
||||
yank_symbol = "",
|
||||
|
||||
filename_max_length = 24, -- truncate when filename > 24
|
||||
filename_truncate_length = 6, -- leave 6 chars on both sides
|
||||
filename_truncate_separator = "..." -- the separator of the truncated filename
|
||||
})
|
||||
```
|
||||
|
||||
```
|
||||
MODE size long_file...name.md S 0 Y 0
|
||||
| | | | | | | | |
|
||||
| | | | | | | | └─── yank_symbol
|
||||
| | | | | | | └─────── select_symbol
|
||||
| | | | | | └───────── separator_close_thin
|
||||
| | | | | └─────────────────── filename_truncate_separator
|
||||
| | | | └─────────────────────────────── separator_close
|
||||
| | | └────────────────────────────────── secondary_color
|
||||
| | └────────────────────────────────────── separator_close
|
||||
| └────────────────────────────────────────── color
|
||||
└───────────────────────────────────────────── separator_head
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Preconfigured separators
|
||||
|
|
@ -101,17 +123,15 @@ _You can find more symbols [here](https://www.nerdfonts.com/cheat-sheet)_
|
|||
|
||||
### Colors and font weight
|
||||
|
||||
You can change font weight in your `yazi/flavors/flavor.toml`:
|
||||
|
||||
```toml
|
||||
mode_normal = { bold = false }
|
||||
```
|
||||
|
||||
And set custom color in the `init.lua`:
|
||||
By default yaziline uses color values from your `theme.toml` (or flavor) but you can set custom colors in the `init.lua`:
|
||||
|
||||
```lua
|
||||
require("yaziline"):setup({
|
||||
color = "#98c379"
|
||||
color = "#98c379",
|
||||
default_files_color = "darkgray",
|
||||
selected_files_color = "white",
|
||||
yanked_files_color = "green",
|
||||
cut_files_color = "red",
|
||||
})
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,167 +1,212 @@
|
|||
---@diagnostic disable: undefined-global
|
||||
|
||||
local function setup(_, options)
|
||||
options = options or {}
|
||||
options = options or {}
|
||||
|
||||
local default_separators = {
|
||||
angly = { "", "", "", "" },
|
||||
curvy = { "", "", "", "" },
|
||||
liney = { "", "", "|", "|" },
|
||||
empty = { "", "", "", "" }
|
||||
}
|
||||
local separators = default_separators[options.separator_style or "angly"]
|
||||
local default_separators = {
|
||||
angly = { "", "", "", "" },
|
||||
curvy = { "", "", "", "" },
|
||||
liney = { "", "", "|", "|" },
|
||||
empty = { "", "", "", "" },
|
||||
}
|
||||
local separators = default_separators[options.separator_style or "angly"]
|
||||
|
||||
local config = {
|
||||
separator_styles = {
|
||||
separator_open = options.separator_open or separators[1],
|
||||
separator_close = options.separator_close or separators[2],
|
||||
separator_open_thin = options.separator_open_thin or separators[3],
|
||||
separator_close_thin = options.separator_close_thin or separators[4],
|
||||
separator_head = options.separator_head or "",
|
||||
separator_tail = options.separator_tail or ""
|
||||
},
|
||||
select_symbol = options.select_symbol or "S",
|
||||
yank_symbol = options.yank_symbol or "Y",
|
||||
filename_max_length = options.filename_max_length or 24,
|
||||
filename_truncate_length = options.filename_truncate_length or 6,
|
||||
filename_truncate_separator = options.filename_truncate_separator or "...",
|
||||
color = options.color or nil
|
||||
}
|
||||
local config = {
|
||||
separator_styles = {
|
||||
separator_open = options.separator_open or separators[1],
|
||||
separator_close = options.separator_close or separators[2],
|
||||
separator_open_thin = options.separator_open_thin or separators[3],
|
||||
separator_close_thin = options.separator_close_thin or separators[4],
|
||||
separator_head = options.separator_head or "",
|
||||
separator_tail = options.separator_tail or "",
|
||||
},
|
||||
select_symbol = options.select_symbol or "S",
|
||||
yank_symbol = options.yank_symbol or "Y",
|
||||
|
||||
local current_separator_style = config.separator_styles
|
||||
filename_max_length = options.filename_max_length or 24,
|
||||
filename_truncate_length = options.filename_truncate_length or 6,
|
||||
filename_truncate_separator = options.filename_truncate_separator or "...",
|
||||
|
||||
function Header:count()
|
||||
return ui.Line {}
|
||||
end
|
||||
color = options.color or nil,
|
||||
secondary_color = options.secondary_color or nil,
|
||||
default_files_color = options.default_files_color
|
||||
or th.which.separator_style.fg
|
||||
or "darkgray",
|
||||
selected_files_color = options.selected_files_color
|
||||
or th.mgr.count_selected.bg
|
||||
or "white",
|
||||
yanked_files_color = options.selected_files_color
|
||||
or th.mgr.count_copied.bg
|
||||
or "green",
|
||||
cut_files_color = options.cut_files_color
|
||||
or th.mgr.count_cut.bg
|
||||
or "red",
|
||||
}
|
||||
|
||||
function Status:mode()
|
||||
local mode = tostring(self._tab.mode):upper()
|
||||
if mode == "UNSET" then
|
||||
mode = "UN-SET"
|
||||
end
|
||||
local current_separator_style = config.separator_styles
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(current_separator_style.separator_head):fg(config.color or style.main.bg),
|
||||
ui.Span(" " .. mode .. " "):fg(THEME.which.mask.bg):bg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
function Header:count()
|
||||
return ui.Line({})
|
||||
end
|
||||
|
||||
function Status:size()
|
||||
local h = self._tab.current.hovered
|
||||
if not h then
|
||||
return ui.Line {}
|
||||
end
|
||||
function Status:mode()
|
||||
local mode = tostring(self._tab.mode):upper()
|
||||
|
||||
local style = self:style()
|
||||
return ui.Span(current_separator_style.separator_close .. " " .. ya.readable_size(h:size() or h.cha.len) .. " ")
|
||||
:fg(config.color or style.main.bg):bg(THEME.which.separator_style.fg)
|
||||
end
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(current_separator_style.separator_head)
|
||||
:fg(config.color or style.main.bg),
|
||||
ui.Span(" " .. mode .. " ")
|
||||
:fg(th.which.mask.bg)
|
||||
:bg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
|
||||
function Status:utf8_sub(str, start_char, end_char)
|
||||
local start_byte = utf8.offset(str, start_char)
|
||||
local end_byte = end_char and (utf8.offset(str, end_char + 1) - 1) or #str
|
||||
function Status:size()
|
||||
local h = self._current.hovered
|
||||
local size = h and (h:size() or h.cha.len) or 0
|
||||
|
||||
if not start_byte or not end_byte then
|
||||
return ""
|
||||
end
|
||||
local style = self:style()
|
||||
return ui.Span(current_separator_style.separator_close .. " " .. ya.readable_size(size) .. " ")
|
||||
:fg(config.color or style.main.bg)
|
||||
:bg(config.secondary_color or th.which.separator_style.fg)
|
||||
end
|
||||
|
||||
return string.sub(str, start_byte, end_byte)
|
||||
end
|
||||
function Status:utf8_sub(str, start_char, end_char)
|
||||
local start_byte = utf8.offset(str, start_char)
|
||||
local end_byte = end_char and (utf8.offset(str, end_char + 1) - 1) or #str
|
||||
|
||||
function Status:truncate_name(filename, max_length)
|
||||
local base_name, extension = filename:match("^(.-)(%.[^%.]+)$")
|
||||
base_name = base_name or filename
|
||||
extension = extension or ""
|
||||
if not start_byte or not end_byte then
|
||||
return ""
|
||||
end
|
||||
|
||||
if utf8.len(base_name) > max_length then
|
||||
base_name = self:utf8_sub(base_name, 1, config.filename_truncate_length) ..
|
||||
config.filename_truncate_separator ..
|
||||
self:utf8_sub(base_name, -config.filename_truncate_length)
|
||||
end
|
||||
return string.sub(str, start_byte, end_byte)
|
||||
end
|
||||
|
||||
return base_name .. extension
|
||||
end
|
||||
function Status:truncate_name(filename, max_length)
|
||||
local base_name, extension = filename:match("^(.+)(%.[^%.]+)$")
|
||||
base_name = base_name or filename
|
||||
extension = extension or ""
|
||||
|
||||
function Status:name()
|
||||
local h = self._tab.current.hovered
|
||||
if not h then
|
||||
return ui.Line {}
|
||||
end
|
||||
if utf8.len(base_name) > max_length then
|
||||
base_name = self:utf8_sub(base_name, 1, config.filename_truncate_length)
|
||||
.. config.filename_truncate_separator
|
||||
.. self:utf8_sub(base_name, -config.filename_truncate_length)
|
||||
end
|
||||
|
||||
local truncated_name = self:truncate_name(h.name, config.filename_max_length)
|
||||
return base_name .. extension
|
||||
end
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line {
|
||||
ui.Span(current_separator_style.separator_close .. " "):fg(THEME.which.separator_style.fg),
|
||||
ui.Span(truncated_name):fg(config.color or style.main.bg),
|
||||
}
|
||||
end
|
||||
function Status:name()
|
||||
local h = self._current.hovered
|
||||
if not h then
|
||||
return ui.Line({
|
||||
ui.Span(current_separator_style.separator_close .. " ")
|
||||
:fg(config.secondary_color or th.which.separator_style.fg),
|
||||
ui.Span("Empty dir")
|
||||
:fg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
|
||||
function Status:files()
|
||||
local files_yanked = #cx.yanked
|
||||
local files_selected = #cx.active.selected
|
||||
local files_is_cut = cx.yanked.is_cut
|
||||
local truncated_name = self:truncate_name(h.name, config.filename_max_length)
|
||||
|
||||
local selected_fg = files_selected > 0 and THEME.manager.count_selected.bg or THEME.which.separator_style.fg
|
||||
local yanked_fg = files_yanked > 0 and
|
||||
(files_is_cut and THEME.manager.count_cut.bg or THEME.manager.count_copied.bg) or
|
||||
THEME.which.separator_style.fg
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(current_separator_style.separator_close .. " ")
|
||||
:fg(config.secondary_color or th.which.separator_style.fg),
|
||||
ui.Span(truncated_name)
|
||||
:fg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
|
||||
local yanked_text = files_yanked > 0 and config.yank_symbol .. " " .. files_yanked or config.yank_symbol .. " 0"
|
||||
function Status:files()
|
||||
local files_yanked = #cx.yanked
|
||||
local files_selected = #cx.active.selected
|
||||
local files_cut = cx.yanked.is_cut
|
||||
|
||||
return ui.Line {
|
||||
ui.Span(" " .. current_separator_style.separator_close_thin .. " "):fg(
|
||||
THEME.which.separator_style.fg),
|
||||
ui.Span(config.select_symbol .. " " .. files_selected .. " "):fg(selected_fg),
|
||||
ui.Span(yanked_text .. " "):fg(yanked_fg),
|
||||
}
|
||||
end
|
||||
local selected_fg = files_selected > 0
|
||||
and config.selected_files_color
|
||||
or config.default_files_color
|
||||
local yanked_fg = files_yanked > 0
|
||||
and
|
||||
(files_cut
|
||||
and config.cut_files_color
|
||||
or config.yanked_files_color
|
||||
)
|
||||
or config.default_files_color
|
||||
|
||||
function Status:modified()
|
||||
local hovered = cx.active.current.hovered
|
||||
local cha = hovered.cha
|
||||
local time = (cha.mtime or 0) // 1
|
||||
local yanked_text = files_yanked > 0
|
||||
and config.yank_symbol .. " " .. files_yanked
|
||||
or config.yank_symbol .. " 0"
|
||||
|
||||
return ui.Span(os.date("%Y-%m-%d %H:%M", time) .. " " .. current_separator_style.separator_open_thin .. " "):fg(
|
||||
THEME.which.separator_style.fg)
|
||||
end
|
||||
return ui.Line({
|
||||
ui.Span(" " .. current_separator_style.separator_close_thin .. " ")
|
||||
:fg(th.which.separator_style.fg),
|
||||
ui.Span(config.select_symbol .. " " .. files_selected .. " ")
|
||||
:fg(selected_fg),
|
||||
ui.Span(yanked_text .. " ")
|
||||
:fg(yanked_fg),
|
||||
})
|
||||
end
|
||||
|
||||
function Status:percent()
|
||||
local percent = 0
|
||||
local cursor = self._tab.current.cursor
|
||||
local length = #self._tab.current.files
|
||||
if cursor ~= 0 and length ~= 0 then
|
||||
percent = math.floor((cursor + 1) * 100 / length)
|
||||
end
|
||||
function Status:modified()
|
||||
local hovered = cx.active.current.hovered
|
||||
|
||||
if percent == 0 then
|
||||
percent = " Top "
|
||||
elseif percent == 100 then
|
||||
percent = " Bot "
|
||||
else
|
||||
percent = string.format(" %2d%% ", percent)
|
||||
end
|
||||
if not hovered then
|
||||
return ""
|
||||
end
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line {
|
||||
ui.Span(" " .. current_separator_style.separator_open):fg(THEME.which.separator_style.fg),
|
||||
ui.Span(percent):fg(config.color or style.main.bg):bg(THEME.which.separator_style.fg),
|
||||
ui.Span(current_separator_style.separator_open):fg(config.color or style.main.bg):bg(THEME.which.separator_style.fg)
|
||||
}
|
||||
end
|
||||
local cha = hovered.cha
|
||||
local time = (cha.mtime or 0) // 1
|
||||
|
||||
function Status:position()
|
||||
local cursor = self._tab.current.cursor
|
||||
local length = #self._tab.current.files
|
||||
return ui.Span(os.date("%Y-%m-%d %H:%M", time) .. " " .. current_separator_style.separator_open_thin .. " ")
|
||||
:fg(th.which.separator_style.fg)
|
||||
end
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(string.format(" %2d/%-2d ", cursor + 1, length)):fg(THEME.which.mask.bg):bg(config.color or style.main.bg),
|
||||
ui.Span(current_separator_style.separator_tail):fg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
function Status:percent()
|
||||
local percent = 0
|
||||
local cursor = self._tab.current.cursor
|
||||
local length = #self._tab.current.files
|
||||
if cursor ~= 0 and length ~= 0 then
|
||||
percent = math.floor((cursor + 1) * 100 / length)
|
||||
end
|
||||
|
||||
Status:children_add(Status.files, 4000, Status.LEFT)
|
||||
Status:children_add(Status.modified, 0, Status.RIGHT)
|
||||
if percent == 0 then
|
||||
percent = " Top "
|
||||
elseif percent == 100 then
|
||||
percent = " Bot "
|
||||
else
|
||||
percent = string.format(" %2d%% ", percent)
|
||||
end
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(" " .. current_separator_style.separator_open)
|
||||
:fg(config.secondary_color or th.which.separator_style.fg),
|
||||
ui.Span(percent)
|
||||
:fg(config.color or style.main.bg)
|
||||
:bg(config.secondary_color or th.which.separator_style.fg),
|
||||
ui.Span(current_separator_style.separator_open)
|
||||
:fg(config.color or style.main.bg)
|
||||
:bg(config.secondary_color or th.which.separator_style.fg),
|
||||
})
|
||||
end
|
||||
|
||||
function Status:position()
|
||||
local cursor = self._tab.current.cursor
|
||||
local length = #self._tab.current.files
|
||||
|
||||
local style = self:style()
|
||||
return ui.Line({
|
||||
ui.Span(string.format(" %2d/%-2d ", math.min(cursor + 1, length), length))
|
||||
:fg(th.which.mask.bg)
|
||||
:bg(config.color or style.main.bg),
|
||||
ui.Span(current_separator_style.separator_tail):fg(config.color or style.main.bg),
|
||||
})
|
||||
end
|
||||
|
||||
Status:children_add(Status.files, 4000, Status.LEFT)
|
||||
Status:children_add(Status.modified, 0, Status.RIGHT)
|
||||
end
|
||||
|
||||
return { setup = setup }
|
||||
Loading…
Add table
Add a link
Reference in a new issue