🌙آموزش init.lua و ساخت پلاگین Yazi
نوشتن پلاگین شخصی، Status و Header، DDS و رویدادها.
اینجا دیگه واقعاً وارد اتاق موتور میشیم. init.lua فایلیه که موقع بالا اومدن یازی اجرا میشه و میتونی توش رابط کاربری رو عوض کنی، پلاگین بنویسی و به رویدادها گوش بدی.
اولین قدم: اضافه کردن اطلاعات به نوار وضعیت
-- نمایش مالک و گروه فایل در سمت راست نوار وضعیت
Status:children_add(function()
local h = cx.active.current.hovered
if not h or ya.target_family() ~= "unix" then
return ""
end
return ui.Line {
ui.Span(ya.user_name(h.cha.uid) or tostring(h.cha.uid)):fg("magenta"),
":",
ui.Span(ya.group_name(h.cha.gid) or tostring(h.cha.gid)):fg("magenta"),
" ",
}
end, 500, Status.RIGHT)
-- نمایش مقصد لینک نمادین در سمت چپ
Status:children_add(function(self)
local h = self._current.hovered
if h and h.link_to then
return " -> " .. tostring(h.link_to)
end
return ""
end, 3300, Status.LEFT)
عدد وسط «اولویت» ــه: هرچی کوچیکتر، نزدیکتر به لبه. Status.LEFT و Status.RIGHT هم که معلومه.
هدر شخصیسازیشده
-- نمایش user@hostname در هدر — مخصوصاً وقتی SSH زدی به چند سرور مفیده
Header:children_add(function()
if ya.target_family() ~= "unix" then return "" end
return ui.Span((ya.user_name() or "?") .. "@" .. (ya.host_name() or "?") .. ":"):fg("blue")
end, 500, Header.LEFT)
Linemode سفارشی: حجم + تاریخ با هم
function Linemode:size_and_mtime()
local time = math.floor(self._file.cha.mtime or 0)
if time == 0 then
time = ""
elseif os.date("%Y", time) == os.date("%Y") then
time = os.date("%b %d %H:%M", time)
else
time = os.date("%b %d %Y", time)
end
local size = self._file:size()
return string.format("%s %s", size and ya.readable_size(size) or "-", time)
end
[mgr]
linemode = "size_and_mtime"
نوشتن اولین پلاگین واقعی
بیا یه پلاگین بنویسیم که یه تب جدید میسازه و مستقیم میره داخل پوشهای که مکاننما روشه:
--- @sync entry
return {
entry = function()
local h = cx.active.current.hovered
ya.emit("tab_create", h and h.cha.is_dir and { h.url } or { current = true })
end,
}
[[mgr.prepend_keymap]]
on = [ "t", "t" ]
run = "plugin smart-tab"
desc = "تب جدید در پوشهی زیر مکاننما"
--- @sync entryیعنی این پلاگین همگام اجرا میشه و به وضعیت زندهی برنامه (cx) دسترسی داره.ya.emitیه دستور داخلی یازی رو صدا میزنه — دقیقاً همون دستورهایی که توی keymap مینویسی.cx.active.current.hoveredفایل زیر مکاننمای تب فعاله.
پرسیدن از کاربر
local get_cwd = ya.sync(function() return cx.active.current.cwd end)
return {
entry = function()
local cwd = get_cwd()
local value, event = ya.input {
title = "اسم پوشهی جدید:",
pos = { "top-center", y = 3, w = 40 },
}
if event ~= 1 or not value or value == "" then return end
if value == "." or value == ".." or value:find("/", 1, true) then
return ya.notify { title = "نام نامعتبر", content = "فقط اسم یک پوشه رو بنویس.", timeout = 3, level = "warn" }
end
local ok, err = fs.create("dir", cwd:join(value))
ya.notify {
title = ok and "ساخته شد" or "ساخت ناموفق",
content = ok and value or tostring(err),
timeout = 3,
level = ok and "info" or "error",
}
end,
}
این پلاگین ناهمگامه؛ ya.input و عملیات فایل نباید داخل تابع همگام اجرا بشن.ya.sync فقط مسیر فعلی رو میگیره. اسم پوشه مستقیم به API فایل داده میشه و وارد دستور شل نمیشه. برای اجرا، یه میانبر با run = "plugin quick-mkdir" تعریف کن.
توابع مفید تعاملی: ya.input (گرفتن متن)، ya.confirm (بله/خیر)، ya.notify (اعلان گوشهی صفحه)، ya.which (منوی انتخاب کلید).
DDS — گوش دادن به رویدادها
یازی یه سیستم توزیع داده داره که میتونی به رویدادهاش مشترک بشی. مثلاً این پلاگین باعث میشه پوشهی Downloads همیشه بر اساس تاریخ مرتب باشه و بقیهی پوشهها الفبایی:
local function setup()
ps.sub("ind-sort", function(opt)
local cwd = cx.active.current.cwd
if cwd:ends_with("Downloads") then
opt.by, opt.reverse, opt.dir_first = "mtime", true, false
else
opt.by, opt.reverse, opt.dir_first = "natural", false, true
end
return opt
end)
end
return { setup = setup }
require("folder-rules"):setup()
تأیید قبل از خروج وقتی چند تب بازه
local count = ya.sync(function() return #cx.tabs end)
local function entry()
if count() < 2 then
return ya.emit("quit", {})
end
local yes = ya.confirm {
pos = { "center", w = 62, h = 10 },
title = "خروج؟",
body = ui.Text("چند تا تب باز داری. مطمئنی میخوای بری بیرون؟"):wrap(ui.Wrap.YES),
}
if yes then ya.emit("quit", {}) end
end
return { entry = entry }
[[mgr.prepend_keymap]]
on = "q"
run = "plugin confirm-quit"
دیباگ کردن Lua
# لاگ رو روشن کن
YAZI_LOG=debug yazi
# توی کد Lua:
# ya.dbg("مقدار:", some_value)
# ya.err("خطا رخ داد")
# خروجی رو ببین
tail -f ~/.local/state/yazi/yazi.log
YAZI_ID مشخصه و این دستورها همون نمونه رو کنترل میکنن:ya emit cd /var/log
ya emit reveal /etc/fstab
ya emit-to بدی؛ پیام دلخواه DDS فقط وقتی کاری میکنه که پلاگینی مشترک اون رویداد شده باشه. همین قابلیت پشت خیلی از ترفندهای فصل بعده.