yazi config migration

This commit is contained in:
David Chen 2025-02-27 09:49:39 -08:00
parent 3d36c50c25
commit 872d045147
18 changed files with 293 additions and 164 deletions

View file

@ -10,15 +10,15 @@ prepend_keymap = [
{ on = [
"'",
"a",
], run = "plugin yamb --args=save", desc = "Add bookmark" },
], run = "plugin yamb save", desc = "Add bookmark" },
{ on = [
"'",
"'",
], run = "plugin yamb --args=jump_by_fzf", desc = "Jump bookmark by fzf" },
], run = "plugin yamb jump_by_fzf", desc = "Jump bookmark by fzf" },
{ on = [
"'",
"r",
], run = "plugin yamb --args=delete_by_key", desc = "Delete bookmark by key" },
], run = "plugin yamb delete_by_key", desc = "Delete bookmark by key" },
{ on = [
"P",
], run = "spot", desc = "Open spotter" },
@ -56,8 +56,8 @@ keymap = [
{ on = "<PageUp>", run = "arrow -100%", desc = "Move cursor up one page" },
{ on = "<PageDown>", run = "arrow 100%", desc = "Move cursor down one page" },
{ on = ["g", "g"], run = "arrow -99999999", desc = "Move cursor to the top" },
{ on = "G", run = "arrow 99999999", desc = "Move cursor to the bottom" },
{ on = ["g", "g"], run = "arrow top", desc = "Move cursor to the top" },
{ on = "G", run = "arrow bot", desc = "Move cursor to the bottom" },
# Navigation
{ on = "n", run = "leave", desc = "Go back to the parent directory" },

View file

@ -1,5 +1,32 @@
[plugin]
deps = [{ use = "llanosrocas/yaziline", rev = "5886330" }, { use = "Rolv-Apneseth/starship", rev = "247f49d" }, { use = "yazi-rs/plugins:git", rev = "ec97f88" }, { use = "yazi-rs/plugins:smart-enter", rev = "7afba3a" }, { use = "h-hg/yamb", rev = "108347b" }, { use = "KKV9/compress", rev = "60b24af" }]
[[plugin.deps]]
use = "llanosrocas/yaziline"
rev = "e06c47f"
hash = "7df84299e89ffefd4686d1e20a295c8a"
[[plugin.deps]]
use = "Rolv-Apneseth/starship"
rev = "6c639b4"
hash = "5614c46d5de76623c283d415bfac8025"
[[plugin.deps]]
use = "yazi-rs/plugins:git"
rev = "5186af7"
hash = "771f18427fb75fb19990ce602bb322f4"
[[plugin.deps]]
use = "yazi-rs/plugins:smart-enter"
rev = "5186af7"
hash = "aef2b1a805b80cce573bb766f1459d88"
[[plugin.deps]]
use = "h-hg/yamb"
rev = "3f7c51f"
hash = "e11b980e5635f0fbabd80931b1a1347e"
[[plugin.deps]]
use = "KKV9/compress"
rev = "60b24af"
hash = "ee025be766240cc98e671754ac836da3"
[flavor]
deps = []

View file

@ -1,7 +1,7 @@
# git.yazi
> [!NOTE]
> Yazi v0.3.3 or later is required for this plugin to work.
> 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.
@ -37,9 +37,6 @@ run = "git"
## Advanced
> [!NOTE]
> This section currently requires Yazi nightly that includes https://github.com/sxyazi/yazi/pull/1637
You can customize the [Style](https://yazi-rs.github.io/docs/plugins/layout#style) of the status sign with:
- `THEME.git.modified`

View file

@ -1,3 +1,5 @@
--- @since 25.2.7
local WIN = ya.target_family() == "windows"
local PATS = {
{ "[MT]", 6 }, -- Modified
@ -145,24 +147,21 @@ local function setup(st, opts)
end
if not change or signs[change] == "" then
return ui.Line("")
return ""
elseif self._file:is_hovered() then
return ui.Line { ui.Span(" "), ui.Span(signs[change]) }
return ui.Line { " ", signs[change] }
else
return ui.Line { ui.Span(" "), ui.Span(signs[change]):style(styles[change]) }
return ui.Line { " ", ui.Span(signs[change]):style(styles[change]) }
end
end, opts.order)
end
local function fetch(self, job)
-- TODO: remove this once Yazi 0.4 is released
job = job or self
local function fetch(_, job)
local cwd = job.files[1].url:parent()
local repo = root(cwd)
if not repo then
remove(tostring(cwd))
return 1
return true
end
local paths = {}
@ -178,8 +177,7 @@ local function fetch(self, job)
:stdout(Command.PIPED)
:output()
if not output then
ya.err("Cannot spawn git command, error code " .. tostring(err))
return 0
return true, Err("Cannot spawn `git` command, error: %s", err)
end
local changed, ignored = {}, {}
@ -205,7 +203,7 @@ local function fetch(self, job)
end
add(tostring(cwd), repo, changed)
return 3
return false
end
return { setup = setup, fetch = fetch }

View file

@ -1,3 +1,4 @@
--- @since 25.2.7
--- @sync entry
local function setup(self, opts) self.open_multi = opts.open_multi end

View file

@ -32,15 +32,24 @@ Add this to `~/.config/yazi/init.lua`:
require("starship"):setup()
```
If you wish to define a custom config file for `starship` to use, you can pass in a path
to the setup function like this:
Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`.
## Config
Here is an example with all available config options:
```lua
starship:setup({ config_file = "/home/rolv/.config/starship_secondary.toml" })
require("starship"):setup({
-- Hide flags (such as filter, find and search). This is recommended for starship themes which
-- are intended to go across the entire width of the terminal.
hide_flags = false, -- Default: false
-- Whether to place flags after the starship prompt. False means the flags will be placed before the prompt.
flags_after_prompt = true, -- Default: true
-- Custom starship configuration file to use
config_file = "~/.config/starship_full.toml", -- Default: nil
})
```
Make sure you have [starship](https://github.com/starship/starship) installed and in your `PATH`.
## Extra
If you use a `starship` theme with a background colour, it might look a bit to cramped on just the one line `Yazi` gives the header by default. To fix this, you can add this to your `init.lua`:
@ -50,21 +59,21 @@ If you use a `starship` theme with a background colour, it might look a bit to c
```lua
local old_build = Tab.build
Tab.build = function(self, ...)
local bar = function(c, x, y)
if x <= 0 or x == self._area.w - 1 then
return ui.Bar(ui.Rect.default, ui.Bar.TOP)
return ui.Bar(ui.Bar.TOP):area(ui.Rect.default)
end
return ui.Bar(
ui.Rect({
return ui.Bar(ui.Bar.TOP)
:area(ui.Rect({
x = x,
y = math.max(0, y),
w = ya.clamp(0, self._area.w - x, 1),
h = math.min(1, self._area.h),
}),
ui.Bar.TOP
):symbol(c)
}))
:symbol(c)
end
local c = self._chunks
@ -76,15 +85,14 @@ Tab.build = function(self, ...)
local style = THEME.manager.border_style
self._base = ya.list_merge(self._base or {}, {
-- Enable for full border
--[[ ui.Border(self._area, ui.Border.ALL):type(ui.Border.ROUNDED):style(style), ]]
ui.Bar(self._chunks[1], ui.Bar.RIGHT):style(style),
ui.Bar(self._chunks[3], ui.Bar.LEFT):style(style),
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),
bar("┬", c[1].right - 1, c[1].y),
bar("┴", c[1].right - 1, c[1].bottom - 1),
bar("┬", c[2].right, c[2].y),
bar("┴", c[2].right, c[1].bottom - 1),
bar("┴", c[2].right, c[2].bottom - 1),
})
old_build(self, ...)

View file

@ -1,83 +0,0 @@
local save = ya.sync(function(st, cwd, output)
if cx.active.current.cwd == Url(cwd) then
st.output = output
ya.render()
end
end)
-- Helper function for accessing the `config_file` state variable
---@return string
local get_config_file = ya.sync(function(st)
return st.config_file
end)
return {
---User arguments for setup method
---@class SetupArgs
---@field config_file string Absolute path to a starship config file
--- Setup plugin
--- @param st table State
--- @param args SetupArgs|nil
setup = function(st, args)
-- Replace default header widget
Header:children_remove(1, Header.LEFT)
Header:children_add(function()
return ui.Line.parse(st.output or "")
end, 1000, Header.LEFT)
-- Check for custom starship config file
if args ~= nil and args.config_file ~= nil then
local url = Url(args.config_file)
if url.is_regular then
local config_file = args.config_file
-- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable
local home = os.getenv("HOME")
if home then
home = tostring(home)
config_file = config_file:gsub("^~", home):gsub("^$HOME", home)
end
st.config_file = config_file
end
end
-- Pass current working directory and custom config path (if specified) to the plugin's entry point
---Callback for subscribers to update the prompt
local callback = function()
local cwd = cx.active.current.cwd
if st.cwd ~= cwd then
st.cwd = cwd
ya.manager_emit("plugin", {
st._id,
args = ya.quote(tostring(cwd), true),
})
end
end
-- Subscribe to events
ps.sub("cd", callback)
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
local command = Command("starship"):arg("prompt"):cwd(args[1]):env("STARSHIP_SHELL", "")
-- Point to custom starship config
local config_file = get_config_file()
if config_file then
command = command:env("STARSHIP_CONFIG", config_file)
end
local output = command:output()
if output then
save(args[1], output.stdout:gsub("^%s+", ""))
end
end,
}

View file

@ -0,0 +1,136 @@
--- @since 25.2.7
-- For development
--[[ local function notify(message) ]]
--[[ ya.notify({ title = "Starship", content = message, timeout = 3 }) ]]
--[[ end ]]
local save = ya.sync(function(st, _cwd, output)
st.output = output
ya.render()
end)
-- Helper function for accessing the `config_file` state variable
---@return string
local get_config_file = ya.sync(function(st)
return st.config_file
end)
return {
---User arguments for setup method
---@class SetupArgs
---@field config_file string Absolute path to a starship config file
---@field hide_flags boolean Whether to hide all flags (such as filter and search). Recommended for themes which are intended to take the full width of the terminal.
---@field flags_after_prompt boolean Whether to place flags (such as filter and search) after the starship prompt. By default this is true.
--- Setup plugin
--- @param st table State
--- @param args SetupArgs|nil
setup = function(st, args)
local hide_flags = false
local flags_after_prompt = true
-- Check setup args
if args ~= nil then
if args.config_file ~= nil then
local url = Url(args.config_file)
if url.is_regular then
local config_file = args.config_file
-- Manually replace '~' and '$HOME' at the start of the path with the OS environment variable
local home = os.getenv("HOME")
if home then
home = tostring(home)
config_file = config_file:gsub("^~", home):gsub("^$HOME", home)
end
st.config_file = config_file
end
end
if args.hide_flags ~= nil then
hide_flags = args.hide_flags
end
if args.flags_after_prompt ~= nil then
flags_after_prompt = args.flags_after_prompt
end
end
-- Replace default header widget
Header:children_remove(1, Header.LEFT)
Header:children_add(function(self)
local max = self._area.w - self._right_width
if max <= 0 then
return ""
end
if hide_flags or not st.output then
return ui.Line.parse(st.output or "")
end
-- Split `st.output` at the first line break (or keep as is if none was found)
local output = st.output:match("([^\n]*)\n?") or st.output
local flags = self:flags()
if flags_after_prompt then
output = output .. " " .. flags
else
output = flags .. " " .. output
end
return ui.Line.parse(output)
end, 1000, Header.LEFT)
-- Pass current working directory and custom config path (if specified) to the plugin's entry point
---Callback for subscribers to update the prompt
local callback = function()
local cwd = cx.active.current.cwd
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
end
end
-- Subscribe to events
ps.sub("cd", callback)
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
local command = Command("starship")
:arg("prompt")
:stdin(Command.INHERIT)
:cwd(args[1])
:env("STARSHIP_SHELL", "")
-- Point to custom starship config
local config_file = get_config_file()
if config_file then
command = command:env("STARSHIP_CONFIG", config_file)
end
local output = command:output()
if output then
save(args[1], output.stdout:gsub("^%s+", ""))
end
end,
}

View file

@ -139,7 +139,7 @@ local generate_key = function(bookmarks)
end)
local idx = 1
for _, key in ipairs(keys) do
if key2rank[key] < key2rank[mb[idx]] then
if idx > #mb or key2rank[key] < key2rank[mb[idx]] then
return key
end
idx = idx + 1

View file

@ -8,7 +8,7 @@ Read more about features and configuration [here](#features).
## Requirements
- yazi version >= 0.3.0
- yazi version >= 25.2.11
- Font with symbol support. For example [Nerd Fonts](https://www.nerdfonts.com/).
## Installation
@ -31,15 +31,19 @@ Optionally, configure line:
```lua
require("yaziline"):setup({
separator_style = "angly" -- "angly" | "curvy" | "liney" | "empty"
color = "#98c379", -- main theme color
separator_style = "angly", -- "angly" | "curvy" | "liney" | "empty"
separator_open = "",
separator_close = "",
separator_open_thin = "",
separator_close_thin = "",
separator_head = "",
separator_tail = "",
select_symbol = "",
yank_symbol = "󰆐",
filename_max_length = 24, -- trim when filename > 24
filename_trim_length = 6 -- trim 6 chars from both ends
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
})
```
@ -70,6 +74,8 @@ require("yaziline"):setup({
separator_close = "", -- instead of 
separator_open_thin = "", -- change to anything
separator_close_thin = "", -- change to anything
separator_head = "", -- to match the style
separator_tail = "" -- to match the style
})
```
@ -85,7 +91,7 @@ You can provide your own symbols for `select` and `yank`. For example:
require("yaziline"):setup({
-- Optinal config
select_symbol = "", -- "S" by default
yank_symbol = "󰆐", -- "Y" by default
yank_symbol = "󰆐" -- "Y" by default
})
```
@ -95,10 +101,18 @@ _You can find more symbols [here](https://www.nerdfonts.com/cheat-sheet)_
### Colors and font weight
You can change background and font weight in your `yazi/flavors/flavor.toml`.
You can change font weight in your `yazi/flavors/flavor.toml`:
```toml
mode_normal = { bg = "#98c379", bold = false }
mode_normal = { bold = false }
```
And set custom color in the `init.lua`:
```lua
require("yaziline"):setup({
color = "#98c379"
})
```
For example, here is how my line looks like:
@ -109,14 +123,15 @@ For example, here is how my line looks like:
Displays the number of selected ('S') and yanked ('Y') files on the left. If files are cut, the yank counter changes color, since its `yank --cut` under the hood.
### Trimmed Filename
### Truncated filename
Displays the trimmed filename on the left, which is useful for smaller screens or long filenames. By default, it's 24 characters with trimming to 12. Adjust in the `setup`.
Displays the truncated filename on the left, which is useful for smaller windows or long filenames. By default, it's 24 characters with trimming to 12 (6 + 6). Adjust in the `setup`.
```lua
require("yaziline"):setup({
filename_max_length = 24, -- trim when filename > 24
filename_trim_length = 6 -- trim 6 chars from both ends
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
})
```

View file

@ -14,12 +14,16 @@ local function setup(_, options)
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_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_trim_length = options.filename_trim_length or 6
filename_truncate_length = options.filename_truncate_length or 6,
filename_truncate_separator = options.filename_truncate_separator or "...",
color = options.color or nil
}
local current_separator_style = config.separator_styles
@ -35,18 +39,46 @@ local function setup(_, options)
end
local style = self:style()
return ui.Span(" " .. mode .. " "):style(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 Status:size()
function Status:size()
local h = self._tab.current.hovered
if not h then
return ui.Line {}
return ui.Line {}
end
local style = self:style()
return ui.Span(current_separator_style.separator_close .. " " .. ya.readable_size(h:size() or h.cha.len) .. " ")
:fg(style.bg):bg(THEME.status.separator_style.bg)
:fg(config.color or style.main.bg):bg(THEME.which.separator_style.fg)
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
if not start_byte or not end_byte then
return ""
end
return string.sub(str, start_byte, end_byte)
end
function Status:truncate_name(filename, max_length)
local base_name, extension = filename:match("^(.-)(%.[^%.]+)$")
base_name = base_name or filename
extension = extension or ""
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 base_name .. extension
end
function Status:name()
@ -55,14 +87,12 @@ local function setup(_, options)
return ui.Line {}
end
local trimmed_name = #h.name > config.filename_max_length and
(string.sub(h.name, 1, config.filename_trim_length) .. "..." .. string.sub(h.name, -config.filename_trim_length)) or
h.name
local truncated_name = self:truncate_name(h.name, config.filename_max_length)
local style = self:style()
return ui.Line {
ui.Span(current_separator_style.separator_close .. " "):fg(THEME.status.separator_style.fg),
ui.Span(trimmed_name):fg(style.bg),
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
@ -71,34 +101,31 @@ local function setup(_, options)
local files_selected = #cx.active.selected
local files_is_cut = cx.yanked.is_cut
local selected_fg = files_selected > 0 and THEME.manager.count_selected.bg or THEME.status.separator_style.fg
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.status.separator_style.fg
THEME.which.separator_style.fg
local yanked_text = files_yanked > 0 and config.yank_symbol .. " " .. files_yanked or config.yank_symbol .. " 0"
return ui.Line {
ui.Span(" " .. current_separator_style.separator_close_thin .. " "):fg(THEME.status.separator_style.fg),
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
function Status:modified()
function Status:modified()
local hovered = cx.active.current.hovered
if not hovered then
return ui.Line {} -- Return empty line if no file is hovered
end
local cha = hovered.cha
local time = (cha.mtime or 0) // 1 -- Use mtime instead of modified
local time = (cha.mtime or 0) // 1
return ui.Span(os.date("%Y-%m-%d %H:%M", time) .. " " .. current_separator_style.separator_open_thin .. " ")
:fg(THEME.status.separator_style.fg)
end
return ui.Span(os.date("%Y-%m-%d %H:%M", time) .. " " .. current_separator_style.separator_open_thin .. " "):fg(
THEME.which.separator_style.fg)
end
function Status:percentage()
function Status:percent()
local percent = 0
local cursor = self._tab.current.cursor
local length = #self._tab.current.files
@ -111,14 +138,14 @@ end
elseif percent == 100 then
percent = " Bot "
else
percent = string.format(" %3d%% ", percent)
percent = string.format(" %2d%% ", percent)
end
local style = self:style()
return ui.Line {
ui.Span(" " .. current_separator_style.separator_open):fg(THEME.status.separator_style.fg),
ui.Span(percent):fg(style.bg):bg(THEME.status.separator_style.bg),
ui.Span(current_separator_style.separator_open):fg(style.bg):bg(THEME.status.separator_style.bg)
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
@ -127,11 +154,14 @@ end
local length = #self._tab.current.files
local style = self:style()
return ui.Span(string.format(" %2d/%-2d ", cursor + 1, length)):style(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
Status:children_add(Status.files, 4000, Status.LEFT)
Status:children_add(Status.mtime, 0, Status.RIGHT)
Status:children_add(Status.modified, 0, Status.RIGHT)
end
return { setup = setup }
return { setup = setup }