Update yazi and its plugins

This commit is contained in:
Hydroxycarbamide 2025-04-10 15:28:26 +02:00
parent ab39adc2f3
commit 09065ab756
Signed by: Siklos
GPG key ID: C06D07D96997549A
16 changed files with 1805 additions and 113 deletions

View file

@ -152,7 +152,7 @@ keymap = [
{ on = "}", run = "tab_swap 1", desc = "Swap current tab with next tab" },
# Tasks
{ on = "w", run = "tasks_show", desc = "Show task manager" },
{ on = "w", run = "tasks:show", desc = "Show task manager" },
# Help
{ on = "~", run = "help", desc = "Open help" },
@ -164,6 +164,15 @@ on = [ "g", "i" ]
run = "plugin lazygit"
desc = "run lazygit"
[[manager.prepend_keymap]]
on = "M"
run = "plugin mount"
[[manager.prepend_keymap]]
on = [ "g", "c" ]
run = "plugin vcs-files"
desc = "Show Git file changes"
[tasks]
keymap = [
@ -328,7 +337,7 @@ keymap = [
keymap = [
{ on = "<C-c>", run = "close", desc = "Cancel completion" },
{ on = "<Tab>", run = "close --submit", desc = "Submit the completion" },
{ on = "<Enter>", run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" },
{ on = "<Enter>", run = [ "close --submit", "input:close --submit" ], desc = "Submit the completion and input" },
{ on = "<A-k>", run = "arrow -1", desc = "Move cursor up" },
{ on = "<A-j>", run = "arrow 1", desc = "Move cursor down" },

View file

@ -1,17 +1,32 @@
[[plugin.deps]]
use = "yazi-rs/plugins:git"
rev = "5186af7"
hash = "771f18427fb75fb19990ce602bb322f4"
rev = "a1738e8"
hash = "e0ada736ea676c2bbb3ec705a49526ef"
[[plugin.deps]]
use = "yazi-rs/plugins:full-border"
rev = "5186af7"
hash = "ae9e1d0c6bfd68cdebc98cc684c22b45"
rev = "a1738e8"
hash = "1f3dad061209081a6b04dd6ff2cb06c7"
[[plugin.deps]]
use = "Lil-Dank/lazygit"
rev = "9f924e3"
hash = "2b0c33928986f126d5c5a4cac31a6b46"
rev = "7a08a09"
hash = "a1fa2b3e1826c3a34804ea8c548e9f80"
[[plugin.deps]]
use = "yazi-rs/plugins:mime-ext"
rev = "a1738e8"
hash = "5e24c167e9ae9a203a48f2a438b8c705"
[[plugin.deps]]
use = "yazi-rs/plugins:mount"
rev = "a1738e8"
hash = "dd97eede8e20e59cd2604e8006e470e2"
[[plugin.deps]]
use = "yazi-rs/plugins:vcs-files"
rev = "a1738e8"
hash = "36405f8781fcaf33ceab24dffd5cea19"
[flavor]
deps = []

View file

@ -1,4 +1,4 @@
--- @since 25.2.7
--- @since 25.2.26
local function setup(_, opts)
local type = opts and opts.type or ui.Border.ROUNDED
@ -6,7 +6,7 @@ local function setup(_, opts)
Tab.build = function(self, ...)
local bar = function(c, x, y)
if x <= 0 or x == self._area.w - 1 then
if x <= 0 or x == self._area.w - 1 or th.mgr.border_symbol ~= "" then
return ui.Bar(ui.Bar.TOP)
end
@ -24,7 +24,7 @@ local function setup(_, opts)
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(type):style(style),
ui.Bar(ui.Bar.RIGHT):area(self._chunks[1]):style(style),

View file

@ -1,7 +1,7 @@
# git.yazi
> [!NOTE]
> Yazi v25.2.7 or later is required for this plugin to work.
> Yazi v25.2.26 or later is required for this plugin to work.
Show the status of Git file changes as linemode in the file list.
@ -39,38 +39,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

View file

@ -1,29 +1,45 @@
--- @since 25.2.7
--- @since 25.4.4
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,8 +183,8 @@ 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
@ -180,27 +198,28 @@ local function fetch(_, job)
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

View file

@ -4,7 +4,7 @@ return {
if output.stderr ~= "" then
ya.notify({
title = "lazygit",
content = "Not in a git directory",
content = "Not in a git directory\nError: " .. output.stderr,
level = "warn",
timeout = 5,
})
@ -28,4 +28,4 @@ return {
end
end
end,
}
}

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 yazi-rs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,56 @@
# mime-ext.yazi
A mime-type provider based on a file extension database, replacing the [builtin `file(1)`](https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/mime.lua) to speed up mime-type retrieval at the expense of accuracy.
See https://yazi-rs.github.io/docs/tips#make-yazi-even-faster for more information.
## Installation
```sh
ya pack -a yazi-rs/plugins:mime-ext
```
## Usage
Add this to your `~/.config/yazi/yazi.toml`:
```toml
[[plugin.prepend_fetchers]]
id = "mime"
name = "*"
run = "mime-ext"
prio = "high"
```
## Advanced
You can also customize it in your `~/.config/yazi/init.lua` with:
```lua
require("mime-ext"):setup {
-- Expand the existing filename database (lowercase), for example:
with_files = {
makefile = "text/makefile",
-- ...
},
-- Expand the existing extension database (lowercase), for example:
with_exts = {
mk = "text/makefile",
-- ...
},
-- If the mime-type is not in both filename and extension databases,
-- then fallback to Yazi's preset `mime` plugin, which uses `file(1)`
fallback_file1 = false,
}
```
## TODO
- Add more file types (PRs welcome!).
- Compress mime-type tables.
## License
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 yazi-rs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,51 @@
# mount.yazi
> [!NOTE]
> Yazi v25.2.7 or later is required for this plugin to work.
A mount manager for Yazi, providing disk mount, unmount, and eject functionality.
Supported platforms:
- Linux with [`udisksctl`](https://github.com/storaged-project/udisks) and [`lsblk`](https://github.com/util-linux/util-linux)
- macOS with `diskutil`
https://github.com/user-attachments/assets/c6f780ab-458b-420f-85cf-2fc45fcfe3a2
## Installation
```sh
ya pack -a yazi-rs/plugins:mount
```
## Usage
Add this to your `~/.config/yazi/keymap.toml`:
```toml
[[manager.prepend_keymap]]
on = "M"
run = "plugin mount"
```
Available keybindings:
| Key binding | Alternate key | Action |
| ------------ | ------------- | --------------------- |
| <kbd>q</kbd> | - | Quit the plugin |
| <kbd>k</kbd> | <kbd></kbd> | Move up |
| <kbd>j</kbd> | <kbd></kbd> | Move down |
| <kbd>l</kbd> | <kbd></kbd> | Enter the mount point |
| <kbd>m</kbd> | - | Mount the partition |
| <kbd>u</kbd> | - | Unmount the partition |
| <kbd>e</kbd> | - | Eject the disk |
## TODO
- Custom keybindings
- Windows support (I don't have an Windows machine for testing, PRs welcome!)
- Support mount, unmount, and eject the entire disk
## License
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file.

View file

@ -0,0 +1,285 @@
--- @since 25.2.26
local toggle_ui = ya.sync(function(self)
if self.children then
Modal:children_remove(self.children)
self.children = nil
else
self.children = Modal:children_add(self, 10)
end
ya.render()
end)
local subscribe = ya.sync(function(self)
ps.unsub("mount")
ps.sub("mount", function() ya.mgr_emit("plugin", { self._id, "refresh" }) end)
end)
local update_partitions = ya.sync(function(self, partitions)
self.partitions = partitions
self.cursor = math.max(0, math.min(self.cursor or 0, #self.partitions - 1))
ya.render()
end)
local active_partition = ya.sync(function(self) return self.partitions[self.cursor + 1] end)
local update_cursor = ya.sync(function(self, cursor)
if #self.partitions == 0 then
self.cursor = 0
else
self.cursor = ya.clamp(0, self.cursor + cursor, #self.partitions - 1)
end
ya.render()
end)
local M = {
keys = {
{ on = "q", run = "quit" },
{ on = "k", run = "up" },
{ on = "j", run = "down" },
{ on = "l", run = { "enter", "quit" } },
{ on = "<Up>", run = "up" },
{ on = "<Down>", run = "down" },
{ on = "<Right>", run = { "enter", "quit" } },
{ on = "m", run = "mount" },
{ on = "u", run = "unmount" },
{ on = "e", run = "eject" },
},
}
function M:new(area)
self:layout(area)
return self
end
function M:layout(area)
local chunks = ui.Layout()
:constraints({
ui.Constraint.Percentage(10),
ui.Constraint.Percentage(80),
ui.Constraint.Percentage(10),
})
:split(area)
local chunks = ui.Layout()
:direction(ui.Layout.HORIZONTAL)
:constraints({
ui.Constraint.Percentage(10),
ui.Constraint.Percentage(80),
ui.Constraint.Percentage(10),
})
:split(chunks[2])
self._area = chunks[2]
end
function M:entry(job)
if job.args[1] == "refresh" then
return update_partitions(self.obtain())
end
toggle_ui()
update_partitions(self.obtain())
subscribe()
local tx1, rx1 = ya.chan("mpsc")
local tx2, rx2 = ya.chan("mpsc")
function producer()
while true do
local cand = self.keys[ya.which { cands = self.keys, silent = true }] or { run = {} }
for _, r in ipairs(type(cand.run) == "table" and cand.run or { cand.run }) do
tx1:send(r)
if r == "quit" then
toggle_ui()
return
end
end
end
end
function consumer1()
repeat
local run = rx1:recv()
if run == "quit" then
tx2:send(run)
break
elseif run == "up" then
update_cursor(-1)
elseif run == "down" then
update_cursor(1)
elseif run == "enter" then
local active = active_partition()
if active and active.dist then
ya.mgr_emit("cd", { active.dist })
end
else
tx2:send(run)
end
until not run
end
function consumer2()
repeat
local run = rx2:recv()
if run == "quit" then
break
elseif run == "mount" then
self.operate("mount")
elseif run == "unmount" then
self.operate("unmount")
elseif run == "eject" then
self.operate("eject")
end
until not run
end
ya.join(producer, consumer1, consumer2)
end
function M:reflow() return { self } end
function M:redraw()
local rows = {}
for _, p in ipairs(self.partitions or {}) do
if not p.sub then
rows[#rows + 1] = ui.Row { p.main }
elseif p.sub == "" then
rows[#rows + 1] = ui.Row { p.main, p.label or "", p.dist or "", p.fstype or "" }
else
rows[#rows + 1] = ui.Row { " " .. p.sub, p.label or "", p.dist or "", p.fstype or "" }
end
end
return {
ui.Clear(self._area),
ui.Border(ui.Border.ALL)
:area(self._area)
:type(ui.Border.ROUNDED)
:style(ui.Style():fg("blue"))
:title(ui.Line("Mount"):align(ui.Line.CENTER)),
ui.Table(rows)
:area(self._area:pad(ui.Pad(1, 2, 1, 2)))
:header(ui.Row({ "Src", "Label", "Dist", "FSType" }):style(ui.Style():bold()))
:row(self.cursor)
:row_style(ui.Style():fg("blue"):underline())
:widths {
ui.Constraint.Length(20),
ui.Constraint.Length(20),
ui.Constraint.Percentage(70),
ui.Constraint.Length(10),
},
}
end
function M.obtain()
local tbl = {}
local last
for _, p in ipairs(fs.partitions()) do
local main, sub = M.split(p.src)
if main and last ~= main then
if p.src == main then
last, p.main, p.sub, tbl[#tbl + 1] = p.src, p.src, "", p
else
last, tbl[#tbl + 1] = main, { src = main, main = main, sub = "" }
end
end
if sub then
if tbl[#tbl].sub == "" and tbl[#tbl].main == main then
tbl[#tbl].sub = nil
end
p.main, p.sub, tbl[#tbl + 1] = main, sub, p
end
end
table.sort(M.fillin(tbl), function(a, b)
if a.main == b.main then
return (a.sub or "") < (b.sub or "")
else
return a.main > b.main
end
end)
return tbl
end
function M.split(src)
local pats = {
{ "^/dev/sd[a-z]", "%d+$" }, -- /dev/sda1
{ "^/dev/nvme%d+n%d+", "p%d+$" }, -- /dev/nvme0n1p1
{ "^/dev/mmcblk%d+", "p%d+$" }, -- /dev/mmcblk0p1
{ "^/dev/disk%d+", ".+$" }, -- /dev/disk1s1
}
for _, p in ipairs(pats) do
local main = src:match(p[1])
if main then
return main, src:sub(#main + 1):match(p[2])
end
end
end
function M.fillin(tbl)
if ya.target_os() ~= "linux" then
return tbl
end
local sources, indices = {}, {}
for i, p in ipairs(tbl) do
if p.sub and not p.fstype then
sources[#sources + 1], indices[p.src] = p.src, i
end
end
if #sources == 0 then
return tbl
end
local output, err = Command("lsblk"):args({ "-p", "-o", "name,fstype", "-J" }):args(sources):output()
if err then
ya.dbg("Failed to fetch filesystem types for unmounted partitions: " .. err)
return tbl
end
local t = ya.json_decode(output and output.stdout or "")
for _, p in ipairs(t and t.blockdevices or {}) do
tbl[indices[p.name]].fstype = p.fstype
end
return tbl
end
function M.operate(type)
local active = active_partition()
if not active then
return
elseif not active.sub then
return -- TODO: mount/unmount main disk
end
local output, err
if ya.target_os() == "macos" then
output, err = Command("diskutil"):args({ type, active.src }):output()
end
if ya.target_os() == "linux" then
if type == "eject" then
Command("udisksctl"):args({ "unmount", "-b", active.src }):status()
output, err = Command("udisksctl"):args({ "power-off", "-b", active.src }):output()
else
output, err = Command("udisksctl"):args({ type, "-b", active.src }):output()
end
end
if not output then
M.fail("Failed to %s `%s`: %s", type, active.src, err)
elseif not output.status.success then
M.fail("Failed to %s `%s`: %s", type, active.src, output.stderr)
end
end
function M.fail(s, ...) ya.notify { title = "Mount", content = string.format(s, ...), timeout = 10, level = "error" } end
function M:click() end
function M:scroll() end
function M:touch() end
return M

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 yazi-rs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,29 @@
# vcs-files.yazi
Show Git file changes in Yazi.
https://github.com/user-attachments/assets/465b801b-3516-4f57-be09-8405da21e34d
## Installation
```sh
ya pack -a yazi-rs/plugins:vcs-files
```
## Usage
```toml
# keymap.toml
[[manager.prepend_keymap]]
on = [ "g", "c" ]
run = "plugin vcs-files"
desc = "Show Git file changes"
```
## TODO
- [ ] Add support for other VCS (e.g. Mercurial, Subversion)
## License
This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file.

View file

@ -0,0 +1,33 @@
--- @since 25.4.8
local root = ya.sync(function() return cx.active.current.cwd end)
local function fail(content) return ya.notify { title = "VCS Files", content = content, timeout = 5, level = "error" } end
local function entry()
local root = root()
local output, err = Command("git"):cwd(tostring(root)):args({ "diff", "--name-only", "HEAD" }):output()
if err then
return fail("Failed to run `git diff`, error: " .. err)
elseif not output.status.success then
return fail("Failed to run `git diff`, stderr: " .. output.stderr)
end
local id = ya.id("ft")
local cwd = root:into_search("Git changes")
ya.mgr_emit("cd", { Url(cwd) })
ya.mgr_emit("update_files", { op = fs.op("part", { id = id, url = Url(cwd), files = {} }) })
local files = {}
for line in output.stdout:gmatch("[^\r\n]+") do
local url = cwd:join(line)
local cha = fs.cha(url, true)
if cha then
files[#files + 1] = File { url = url, cha = cha }
end
end
ya.mgr_emit("update_files", { op = fs.op("part", { id = id, url = Url(cwd), files = files }) })
ya.mgr_emit("update_files", { op = fs.op("done", { id = id, url = cwd, cha = Cha { kind = 16 } }) })
end
return { entry = entry }

View file

@ -82,7 +82,7 @@ macro_workers = 25
bizarre_retry = 5
image_alloc = 536870912 # 512MB
image_bound = [ 0, 0 ]
suppress_preload = false
suppress_preload = true
[plugin]
@ -156,6 +156,12 @@ id = "git"
name = "*/"
run = "git"
[[plugin.prepend_fetchers]]
id = "mime"
name = "*"
run = "mime-ext"
prio = "high"
[input]
cursor_blink = false