feat(lua): add basic LuaCATS types for internal lua code

This commit adds rudimentary types for the internal lua code. This does
not have a big effect on its own, but it can be a part of a bigger
effort to add types to the codebase.

The types are only usable within yazi itself and are not published for
plugin developers to use (yet).

I only added types for the things that are defined in the lua side of
the codebase. This means most of the useful typing is still missing. I
want to add that separately, as it would be difficult to keep the types
in sync with the actual codebase.
This commit is contained in:
Mika Vilpas 2024-05-01 13:27:20 +03:00
parent bdb28f7691
commit 372ecdd403
25 changed files with 139 additions and 2 deletions

View file

@ -1,7 +1,11 @@
---@class yazi.Current
---@field area? unknown
Current = { Current = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param area unknown
---@return table
function Current:empty(area) function Current:empty(area)
local folder = Folder:by_kind(Folder.CURRENT) local folder = Folder:by_kind(Folder.CURRENT)
@ -17,6 +21,8 @@ function Current:empty(area)
} }
end end
---@param area unknown
---@return table
function Current:render(area) function Current:render(area)
self.area = area self.area = area

View file

@ -1,5 +1,8 @@
---@class yazi.File
File = {} File = {}
---@param file unknown
---@return table
function File:icon(file) function File:icon(file)
local icon = file:icon() local icon = file:icon()
if not icon then if not icon then
@ -11,11 +14,15 @@ function File:icon(file)
end end
end end
---@param file unknown
---@return table
function File:prefix(file) function File:prefix(file)
local prefix = file:prefix() or "" local prefix = file:prefix() or ""
return prefix == "" and {} or { ui.Span(prefix .. "/") } return prefix == "" and {} or { ui.Span(prefix .. "/") }
end end
---@param file unknown
---@return table
function File:highlights(file) function File:highlights(file)
local name = file.name:gsub("\r", "?", 1) local name = file.name:gsub("\r", "?", 1)
local highlights = file:highlights() local highlights = file:highlights()
@ -53,6 +60,8 @@ function File:found(file)
} }
end end
---@param file unknown
---@return table
function File:symlink(file) function File:symlink(file)
if not MANAGER.show_symlink then if not MANAGER.show_symlink then
return {} return {}
@ -62,6 +71,8 @@ function File:symlink(file)
return to and { ui.Span(" -> " .. tostring(to)):italic() } or {} return to and { ui.Span(" -> " .. tostring(to)):italic() } or {}
end end
---@param file unknown
---@return table
function File:full(file) function File:full(file)
return ya.flat { return ya.flat {
self:icon(file), self:icon(file),
@ -72,6 +83,8 @@ function File:full(file)
} }
end end
---@param file unknown
---@return table
function File:style(file) function File:style(file)
local style = file:style() local style = file:style()
if not file:is_hovered() then if not file:is_hovered() then
@ -83,6 +96,8 @@ function File:style(file)
end end
end end
---@param file unknown
---@return 0 | 1 | 2 | 3 | 4
function File:marker(file) function File:marker(file)
local yanked = file:is_yanked() local yanked = file:is_yanked()
if yanked ~= 0 then if yanked ~= 0 then

View file

@ -1,9 +1,12 @@
---@class yazi.Folder
Folder = { Folder = {
PARENT = 0, PARENT = 0,
CURRENT = 1, CURRENT = 1,
PREVIEW = 2, PREVIEW = 2,
} }
---@param area unknown
---@param files unknown[]
function Folder:linemode(area, files) function Folder:linemode(area, files)
local mode = cx.active.conf.linemode local mode = cx.active.conf.linemode
if mode == "none" then if mode == "none" then
@ -29,6 +32,9 @@ function Folder:linemode(area, files)
return ui.Paragraph(area, lines):align(ui.Paragraph.RIGHT) return ui.Paragraph(area, lines):align(ui.Paragraph.RIGHT)
end end
---@param area unknown
---@param markers unknown[]
---@return table
function Folder:markers(area, markers) function Folder:markers(area, markers)
if #markers == 0 or area.w * area.h == 0 then if #markers == 0 or area.w * area.h == 0 then
return {} return {}
@ -73,6 +79,7 @@ function Folder:markers(area, markers)
return elements return elements
end end
---@param kind yazi.FolderType
function Folder:by_kind(kind) function Folder:by_kind(kind)
if kind == self.PARENT then if kind == self.PARENT then
return cx.active.parent return cx.active.parent

View file

@ -1,7 +1,10 @@
---@class yazi.Header
Header = { Header = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param max integer
---@return table
function Header:cwd(max) function Header:cwd(max)
local cwd = cx.active.current.cwd local cwd = cx.active.current.cwd
local readable = ya.readable_path(tostring(cwd)) local readable = ya.readable_path(tostring(cwd))
@ -10,6 +13,7 @@ function Header:cwd(max)
return ui.Span(ya.truncate(text, { max = max, rtl = true })):style(THEME.manager.cwd) return ui.Span(ya.truncate(text, { max = max, rtl = true })):style(THEME.manager.cwd)
end end
---@return table
function Header:count() function Header:count()
local yanked = #cx.yanked local yanked = #cx.yanked
@ -35,6 +39,7 @@ function Header:count()
} }
end end
---@return table
function Header:tabs() function Header:tabs()
local tabs = #cx.tabs local tabs = #cx.tabs
if tabs == 1 then if tabs == 1 then
@ -57,6 +62,9 @@ function Header:tabs()
end end
-- TODO: remove this function after v0.2.5 release -- TODO: remove this function after v0.2.5 release
--
---@param area unknown
---@return table
function Header:layout(area) function Header:layout(area)
if not ya.deprecated_header_layout then if not ya.deprecated_header_layout then
ya.deprecated_header_layout = true ya.deprecated_header_layout = true
@ -76,6 +84,8 @@ function Header:layout(area)
:split(area) :split(area)
end end
---@param area unknown
---@return table
function Header:render(area) function Header:render(area)
self.area = area self.area = area

View file

@ -1,7 +1,10 @@
---@class yazi.Manager
Manager = { Manager = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param area unknown
---@return unknown[]
function Manager:layout(area) function Manager:layout(area)
self.area = area self.area = area
@ -15,6 +18,8 @@ function Manager:layout(area)
:split(area) :split(area)
end end
---@param area unknown
---@return unknown[]
function Manager:render(area) function Manager:render(area)
local chunks = self:layout(area) local chunks = self:layout(area)

View file

@ -1,7 +1,10 @@
---@class yazi.Parent
Parent = { Parent = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param area unknown
---@return table
function Parent:render(area) function Parent:render(area)
self.area = area self.area = area

View file

@ -1,7 +1,10 @@
---@class yazi.Preview
Preview = { Preview = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param area unknown
---@return table
function Preview:render(area) function Preview:render(area)
self.area = area self.area = area
return {} return {}

View file

@ -1,7 +1,11 @@
---@class yazi.Progress
Progress = { Progress = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@param area unknown
---@param offset integer
---@return table
function Progress:render(area, offset) function Progress:render(area, offset)
self.area = ui.Rect { self.area = ui.Rect {
x = math.max(0, area.w - offset - 21), x = math.max(0, area.w - offset - 21),
@ -18,6 +22,7 @@ end
-- --
-- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`, -- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`,
-- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition". -- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition".
---@return table
function Progress:partial_render() function Progress:partial_render()
local progress = cx.tasks.progress local progress = cx.tasks.progress
if progress.total == 0 then if progress.total == 0 then

View file

@ -1,7 +1,9 @@
---@class yazi.Status
Status = { Status = {
area = ui.Rect.default, area = ui.Rect.default,
} }
---@return unknown
function Status.style() function Status.style()
if cx.active.mode.is_select then if cx.active.mode.is_select then
return THEME.status.mode_select return THEME.status.mode_select
@ -12,6 +14,7 @@ function Status.style()
end end
end end
---@return table
function Status:mode() function Status:mode()
local mode = tostring(cx.active.mode):upper() local mode = tostring(cx.active.mode):upper()
if mode == "UNSET" then if mode == "UNSET" then
@ -25,6 +28,7 @@ function Status:mode()
} }
end end
---@return table
function Status:size() function Status:size()
local h = cx.active.current.hovered local h = cx.active.current.hovered
if not h then if not h then
@ -38,6 +42,7 @@ function Status:size()
} }
end end
---@return table
function Status:name() function Status:name()
local h = cx.active.current.hovered local h = cx.active.current.hovered
if not h then if not h then
@ -47,6 +52,7 @@ function Status:name()
return ui.Span(" " .. h.name) return ui.Span(" " .. h.name)
end end
---@return table
function Status:permissions() function Status:permissions()
local h = cx.active.current.hovered local h = cx.active.current.hovered
if not h then if not h then
@ -76,6 +82,7 @@ function Status:permissions()
return ui.Line(spans) return ui.Line(spans)
end end
---@return table
function Status:percentage() function Status:percentage()
local percent = 0 local percent = 0
local cursor = cx.active.current.cursor local cursor = cx.active.current.cursor
@ -99,6 +106,7 @@ function Status:percentage()
} }
end end
---@return table
function Status:position() function Status:position()
local cursor = cx.active.current.cursor local cursor = cx.active.current.cursor
local length = #cx.active.current.files local length = #cx.active.current.files
@ -110,6 +118,8 @@ function Status:position()
} }
end end
---@param area unknown
---@return table
function Status:render(area) function Status:render(area)
self.area = area self.area = area

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local _, bound = ya.preview_archive(self) local _, bound = ya.preview_archive(self)
if bound then if bound then
@ -7,6 +8,7 @@ function M:peek()
end end
end end
---@param units number
function M:seek(units) function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local _, bound = ya.preview_code(self) local _, bound = ya.preview_code(self)
if bound then if bound then
@ -7,6 +8,7 @@ function M:peek()
end end
end end
---@param units number
function M:seek(units) function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:setup() function M:setup()
ps.sub_remote("dds-cd", function(url) ya.manager_emit("cd", { url }) end) ps.sub_remote("dds-cd", function(url) ya.manager_emit("cd", { url }) end)
end end

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local cmd = os.getenv("YAZI_FILE_ONE") or "file" local cmd = os.getenv("YAZI_FILE_ONE") or "file"
local output, code = Command(cmd):args({ "-bL", tostring(self.file.url) }):stdout(Command.PIPED):output() local output, code = Command(cmd):args({ "-bL", tostring(self.file.url) }):stdout(Command.PIPED):output()
@ -16,6 +17,7 @@ function M:peek()
ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) }) ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) })
end end
---@return nil
function M:seek() end function M:seek() end
return M return M

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local folder = Folder:by_kind(Folder.PREVIEW) local folder = Folder:by_kind(Folder.PREVIEW)
if not folder or folder.cwd ~= self.file.url then if not folder or folder.cwd ~= self.file.url then
@ -38,6 +39,7 @@ function M:peek()
) )
end end
---@return nil
function M:seek(units) function M:seek(units)
local folder = Folder:by_kind(Folder.PREVIEW) local folder = Folder:by_kind(Folder.PREVIEW)
if folder and folder.cwd == self.file.url then if folder and folder.cwd == self.file.url then

View file

@ -1,7 +1,9 @@
local state = ya.sync(function() return cx.active.current.cwd end) local state = ya.sync(function() return cx.active.current.cwd end)
---@return nil
local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end
---@return nil
local function entry() local function entry()
local _permit = ya.hide() local _permit = ya.hide()
local cwd = tostring(state()) local cwd = tostring(state())

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local url = ya.file_cache(self) local url = ya.file_cache(self)
if not url or not fs.cha(url) then if not url or not fs.cha(url) then
@ -10,8 +11,10 @@ function M:peek()
ya.preview_widgets(self, {}) ya.preview_widgets(self, {})
end end
---@return nil
function M:seek() end function M:seek() end
---@return yazi.PreloaderReturnValue
function M:preload() function M:preload()
local cache = ya.file_cache(self) local cache = ya.file_cache(self)
if not cache or fs.cha(cache) then if not cache or fs.cha(cache) then

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local child = Command("jq") local child = Command("jq")
:args({ :args({
@ -41,6 +42,7 @@ function M:peek()
end end
end end
---@return nil
function M:seek(units) function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then
@ -52,6 +54,7 @@ function M:seek(units)
end end
end end
---@return nil
function M:fallback_to_builtin() function M:fallback_to_builtin()
local _, bound = ya.preview_code(self) local _, bound = ya.preview_code(self)
if bound then if bound then

View file

@ -2,6 +2,8 @@ local SUPPORTED_TYPES = "application/audio/biosig/chemical/font/image/inode/mess
local M = {} local M = {}
---@param s string
---@return string | nil
local function match_mimetype(s) local function match_mimetype(s)
local type, sub = s:match("([-a-z]+/)([+-.a-zA-Z0-9]+)%s*$") local type, sub = s:match("([-a-z]+/)([+-.a-zA-Z0-9]+)%s*$")
if type and sub and string.find(SUPPORTED_TYPES, type, 1, true) then if type and sub and string.find(SUPPORTED_TYPES, type, 1, true) then
@ -9,6 +11,7 @@ local function match_mimetype(s)
end end
end end
---@return yazi.PreloaderReturnValue
function M:preload() function M:preload()
local urls = {} local urls = {}
for _, file in ipairs(self.files) do for _, file in ipairs(self.files) do

View file

@ -4,6 +4,7 @@ function M:peek() end
function M:seek() end function M:seek() end
---@return yazi.PreloaderReturnValue
function M:preload() return 1 end function M:preload() return 1 end
return M return M

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local cache = ya.file_cache(self) local cache = ya.file_cache(self)
if not cache then if not cache then
@ -12,6 +13,7 @@ function M:peek()
end end
end end
---@return nil
function M:seek(units) function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then
@ -20,6 +22,7 @@ function M:seek(units)
end end
end end
---@return yazi.PreloaderReturnValue
function M:preload() function M:preload()
local cache = ya.file_cache(self) local cache = ya.file_cache(self)
if not cache or fs.cha(cache) then if not cache or fs.cha(cache) then

View file

@ -1,3 +1,4 @@
---@return nil
local function setup(_, opts) local function setup(_, opts)
if opts.sync_yanked then if opts.sync_yanked then
ps.sub_remote("yank", function(body) ya.manager_emit("update_yanked", { cut = body.cut, urls = body }) end) ps.sub_remote("yank", function(body) ya.manager_emit("update_yanked", { cut = body.cut, urls = body }) end)

View file

@ -1,5 +1,6 @@
local M = {} local M = {}
---@return nil
function M:peek() function M:peek()
local cache = ya.file_cache(self) local cache = ya.file_cache(self)
if not cache then if not cache then
@ -12,6 +13,7 @@ function M:peek()
end end
end end
---@return nil
function M:seek(units) function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then
@ -22,6 +24,7 @@ function M:seek(units)
end end
end end
---@return yazi.PreloaderReturnValue
function M:preload() function M:preload()
local percentage = 5 + self.skip local percentage = 5 + self.skip
if percentage > 95 then if percentage > 95 then

View file

@ -11,6 +11,8 @@ local function fail(s, ...)
ya.notify { title = "Zoxide", content = string.format(s, ...), timeout = 5, level = "error" } ya.notify { title = "Zoxide", content = string.format(s, ...), timeout = 5, level = "error" }
end end
---@param cwd string
---@return integer
local function head(cwd) local function head(cwd)
local child = Command("zoxide"):args({ "query", "-l" }):stdout(Command.PIPED):spawn() local child = Command("zoxide"):args({ "query", "-l" }):stdout(Command.PIPED):spawn()
if not child then if not child then
@ -31,6 +33,10 @@ local function head(cwd)
return n return n
end end
---@class yazi.ZoxideOpts
---@field update_db? boolean
---@param opts yazi.ZoxideOpts
local function setup(_, opts) local function setup(_, opts)
opts = opts or {} opts = opts or {}
@ -48,6 +54,7 @@ local function setup(_, opts)
end end
end end
---@return nil
local function entry() local function entry()
local st = state() local st = state()
if st.empty == true then if st.empty == true then

View file

@ -0,0 +1,13 @@
---@meta
-- This file contains some of the types used in yazi. It only exists to
-- provide these types and should not be loaded at runtime.
return
---@alias yazi.FolderType "PARENT" | "CURRENT" | "PREVIEW"
---@alias yazi.PreloaderReturnValue
---| 0 # Failure, don't continue
---| 1 # Success, don't continue
---| 2 # Failure, continue
---| 3 # Success, continue

View file

@ -1,7 +1,13 @@
table.unpack = table.unpack or unpack table.unpack = table.unpack or unpack
---@class yazi.Ya
ya = ya or {} ya = ya or {}
---@param min number
---@param x number
---@param max number
---@return number
---@nodiscard
function ya.clamp(min, x, max) function ya.clamp(min, x, max)
if x < min then if x < min then
return min return min
@ -12,8 +18,16 @@ function ya.clamp(min, x, max)
end end
end end
function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end ---@param x number
---@return number
---@nodiscard
function ya.round(x)
return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)
end
---@param t table
---@return table
---@nodiscard
function ya.flat(t) function ya.flat(t)
local r = {} local r = {}
for _, v in ipairs(t) do for _, v in ipairs(t) do
@ -28,8 +42,17 @@ function ya.flat(t)
return r return r
end end
function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end ---@param str string
---@return string
---@nodiscard
function ya.basename(str)
---@diagnostic disable-next-line: redundant-return-value
return string.gsub(str, "(.*[/\\])(.*)", "%2")
end
---@param size number
---@return string
---@nodiscard
function ya.readable_size(size) function ya.readable_size(size)
local units = { "B", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q" } local units = { "B", "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q" }
local i = 1 local i = 1
@ -40,6 +63,8 @@ function ya.readable_size(size)
return string.format("%.1f%s", size, units[i]) return string.format("%.1f%s", size, units[i])
end end
---@param path string
---@return string
function ya.readable_path(path) function ya.readable_path(path)
local home = os.getenv("HOME") or os.getenv("USERPROFILE") local home = os.getenv("HOME") or os.getenv("USERPROFILE")
if not home then if not home then