2022-12-23 15:34:55 +08:00
|
|
|
---@class LazyUtilCore
|
2022-11-21 05:33:47 +08:00
|
|
|
local M = {}
|
|
|
|
|
2022-11-29 19:02:25 +08:00
|
|
|
---@alias LazyProfile {data: string|{[string]:string}, time: number, [number]:LazyProfile}
|
2022-11-21 05:33:47 +08:00
|
|
|
|
|
|
|
---@type LazyProfile[]
|
|
|
|
M._profiles = { { name = "lazy" } }
|
|
|
|
|
2022-11-29 19:02:25 +08:00
|
|
|
---@param data (string|{[string]:string})?
|
2022-11-21 05:33:47 +08:00
|
|
|
---@param time number?
|
2022-11-29 19:02:25 +08:00
|
|
|
function M.track(data, time)
|
|
|
|
if data then
|
2022-11-21 05:34:55 +08:00
|
|
|
local entry = {
|
2022-11-29 19:02:25 +08:00
|
|
|
data = data,
|
2022-11-23 23:10:16 +08:00
|
|
|
time = time or vim.loop.hrtime(),
|
2022-11-21 05:34:55 +08:00
|
|
|
}
|
|
|
|
table.insert(M._profiles[#M._profiles], entry)
|
|
|
|
|
|
|
|
if not time then
|
|
|
|
table.insert(M._profiles, entry)
|
|
|
|
end
|
2022-11-23 23:10:16 +08:00
|
|
|
return entry
|
2022-11-21 05:34:55 +08:00
|
|
|
else
|
2022-11-25 05:03:00 +08:00
|
|
|
---@type LazyProfile
|
2022-11-21 05:34:55 +08:00
|
|
|
local entry = table.remove(M._profiles)
|
2022-11-23 23:10:16 +08:00
|
|
|
entry.time = vim.loop.hrtime() - entry.time
|
|
|
|
return entry
|
2022-11-21 05:34:55 +08:00
|
|
|
end
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2023-01-02 18:26:40 +08:00
|
|
|
---@generic F: fun()
|
|
|
|
---@param data (string|{[string]:string})?
|
|
|
|
---@param fn F
|
|
|
|
---@return F
|
|
|
|
function M.trackfn(data, fn)
|
|
|
|
return function(...)
|
|
|
|
M.track(data)
|
|
|
|
local ok, ret = pcall(fn, ...)
|
|
|
|
M.track()
|
|
|
|
if not ok then
|
|
|
|
error(ret)
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-12-23 05:41:44 +08:00
|
|
|
---@param name string
|
2022-12-23 17:18:19 +08:00
|
|
|
---@return string
|
2022-12-23 05:41:44 +08:00
|
|
|
function M.normname(name)
|
2022-12-23 17:18:19 +08:00
|
|
|
local ret = name:lower():gsub("^n?vim%-", ""):gsub("%.n?vim$", ""):gsub("%.lua", ""):gsub("[^a-z]+", "")
|
|
|
|
return ret
|
2022-12-23 05:41:44 +08:00
|
|
|
end
|
|
|
|
|
2022-12-23 17:18:19 +08:00
|
|
|
---@return string
|
2022-12-20 14:19:41 +08:00
|
|
|
function M.norm(path)
|
|
|
|
if path:sub(1, 1) == "~" then
|
2022-12-20 15:47:01 +08:00
|
|
|
local home = vim.loop.os_homedir()
|
|
|
|
if home:sub(-1) == "\\" or home:sub(-1) == "/" then
|
|
|
|
home = home:sub(1, -2)
|
|
|
|
end
|
|
|
|
path = home .. path:sub(2)
|
2022-12-20 14:19:41 +08:00
|
|
|
end
|
2022-12-31 16:55:50 +08:00
|
|
|
path = path:gsub("\\", "/"):gsub("/+", "/")
|
2022-12-21 17:17:10 +08:00
|
|
|
return path:sub(-1) == "/" and path:sub(1, -2) or path
|
2022-12-20 14:19:41 +08:00
|
|
|
end
|
|
|
|
|
2022-12-30 18:52:09 +08:00
|
|
|
---@param opts? string|{msg:string, on_error:fun(msg)}
|
|
|
|
function M.try(fn, opts)
|
|
|
|
opts = type(opts) == "string" and { msg = opts } or opts or {}
|
|
|
|
local msg = opts.msg
|
2022-11-26 05:50:17 +08:00
|
|
|
-- error handler
|
|
|
|
local error_handler = function(err)
|
|
|
|
local Config = require("lazy.core.config")
|
|
|
|
local trace = {}
|
|
|
|
local level = 1
|
|
|
|
while true do
|
|
|
|
local info = debug.getinfo(level, "Sln")
|
|
|
|
if not info then
|
|
|
|
break
|
|
|
|
end
|
2022-12-02 16:22:15 +08:00
|
|
|
if info.what ~= "C" and not info.source:find("lazy.nvim") then
|
2022-11-26 05:50:17 +08:00
|
|
|
local source = info.source:sub(2)
|
2022-12-03 22:48:06 +08:00
|
|
|
if source:find(Config.options.root, 1, true) == 1 then
|
|
|
|
source = source:sub(#Config.options.root + 1)
|
2022-11-26 05:50:17 +08:00
|
|
|
end
|
|
|
|
source = vim.fn.fnamemodify(source, ":p:~:.")
|
|
|
|
local line = " - " .. source .. ":" .. info.currentline
|
|
|
|
if info.name then
|
|
|
|
line = line .. " _in_ **" .. info.name .. "**"
|
|
|
|
end
|
|
|
|
table.insert(trace, line)
|
|
|
|
end
|
|
|
|
level = level + 1
|
|
|
|
end
|
2023-01-05 18:31:13 +08:00
|
|
|
msg = (msg and (msg .. "\n\n") or "") .. err
|
2022-11-28 14:36:32 +08:00
|
|
|
if #trace > 0 then
|
|
|
|
msg = msg .. "\n\n# stacktrace:\n" .. table.concat(trace, "\n")
|
|
|
|
end
|
2022-12-30 18:52:09 +08:00
|
|
|
if opts.on_error then
|
|
|
|
opts.on_error(msg)
|
|
|
|
else
|
|
|
|
vim.schedule(function()
|
|
|
|
M.error(msg)
|
|
|
|
end)
|
|
|
|
end
|
2022-11-26 05:50:17 +08:00
|
|
|
return err
|
|
|
|
end
|
|
|
|
|
|
|
|
---@type boolean, any
|
|
|
|
local ok, result = xpcall(fn, error_handler)
|
|
|
|
return ok and result or nil
|
|
|
|
end
|
|
|
|
|
2022-12-02 16:22:15 +08:00
|
|
|
function M.get_source()
|
|
|
|
local f = 2
|
|
|
|
while true do
|
|
|
|
local info = debug.getinfo(f, "S")
|
|
|
|
if not info then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
if info.what ~= "C" and not info.source:find("lazy.nvim", 1, true) then
|
|
|
|
return info.source:sub(2)
|
|
|
|
end
|
|
|
|
f = f + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-25 22:35:40 +08:00
|
|
|
-- Fast implementation to check if a table is a list
|
|
|
|
---@param t table
|
|
|
|
function M.is_list(t)
|
|
|
|
local i = 0
|
|
|
|
---@diagnostic disable-next-line: no-unknown
|
|
|
|
for _ in pairs(t) do
|
|
|
|
i = i + 1
|
|
|
|
if t[i] == nil then
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2022-11-21 05:33:47 +08:00
|
|
|
function M.very_lazy()
|
2022-11-21 05:34:55 +08:00
|
|
|
local function _load()
|
2023-01-11 20:40:19 +08:00
|
|
|
vim.schedule(function()
|
2023-01-16 19:37:28 +08:00
|
|
|
if vim.v.exiting ~= vim.NIL then
|
|
|
|
return
|
|
|
|
end
|
2023-01-07 16:12:51 +08:00
|
|
|
vim.g.did_very_lazy = true
|
2023-01-13 16:00:15 +08:00
|
|
|
vim.api.nvim_exec_autocmds("User", { pattern = "VeryLazy", modeline = false })
|
2023-01-11 20:40:19 +08:00
|
|
|
end)
|
2022-11-21 05:34:55 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
vim.api.nvim_create_autocmd("User", {
|
|
|
|
pattern = "LazyDone",
|
|
|
|
once = true,
|
|
|
|
callback = function()
|
|
|
|
if vim.v.vim_did_enter == 1 then
|
|
|
|
_load()
|
|
|
|
else
|
2023-01-11 20:40:19 +08:00
|
|
|
vim.api.nvim_create_autocmd("UIEnter", {
|
2022-11-21 05:34:55 +08:00
|
|
|
once = true,
|
|
|
|
callback = function()
|
|
|
|
_load()
|
|
|
|
end,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
})
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2022-11-25 05:03:00 +08:00
|
|
|
---@alias FileType "file"|"directory"|"link"
|
|
|
|
---@param path string
|
2022-11-28 18:35:47 +08:00
|
|
|
---@param fn fun(path: string, name:string, type:FileType):boolean?
|
2022-11-25 05:03:00 +08:00
|
|
|
function M.ls(path, fn)
|
|
|
|
local handle = vim.loop.fs_scandir(path)
|
|
|
|
while handle do
|
|
|
|
local name, t = vim.loop.fs_scandir_next(handle)
|
|
|
|
if not name then
|
|
|
|
break
|
2022-11-21 05:34:55 +08:00
|
|
|
end
|
2023-01-17 21:35:21 +08:00
|
|
|
|
|
|
|
local fname = path .. "/" .. name
|
|
|
|
|
|
|
|
-- HACK: type is not always returned due to a bug in luv,
|
|
|
|
-- so fecth it with fs_stat instead when needed.
|
|
|
|
-- see https://github.com/folke/lazy.nvim/issues/306
|
|
|
|
if fn(fname, name, t or vim.loop.fs_stat(fname).type) == false then
|
2022-11-28 18:35:47 +08:00
|
|
|
break
|
|
|
|
end
|
2022-11-21 05:34:55 +08:00
|
|
|
end
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2022-11-25 05:03:00 +08:00
|
|
|
---@param path string
|
|
|
|
---@param fn fun(path: string, name:string, type:FileType)
|
|
|
|
function M.walk(path, fn)
|
|
|
|
M.ls(path, function(child, name, type)
|
|
|
|
if type == "directory" then
|
|
|
|
M.walk(child, fn)
|
2022-11-21 05:34:55 +08:00
|
|
|
end
|
2022-11-25 05:03:00 +08:00
|
|
|
fn(child, name, type)
|
|
|
|
end)
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2022-12-21 16:03:40 +08:00
|
|
|
---@param modname string
|
2022-11-25 05:03:00 +08:00
|
|
|
---@param fn fun(modname:string, modpath:string)
|
2023-01-02 03:19:09 +08:00
|
|
|
function M.lsmod(modname, fn)
|
|
|
|
local Cache = require("lazy.core.cache")
|
2023-01-04 05:31:18 +08:00
|
|
|
local root = Cache.find_root(modname)
|
2023-01-02 03:19:09 +08:00
|
|
|
if not root then
|
|
|
|
return
|
2022-12-21 16:03:40 +08:00
|
|
|
end
|
2023-01-02 03:19:09 +08:00
|
|
|
|
2023-01-04 05:31:18 +08:00
|
|
|
if vim.loop.fs_stat(root .. ".lua") then
|
|
|
|
fn(modname, root .. ".lua")
|
2022-12-21 16:03:40 +08:00
|
|
|
end
|
2023-01-02 03:19:09 +08:00
|
|
|
|
2022-11-25 05:03:00 +08:00
|
|
|
M.ls(root, function(path, name, type)
|
2023-01-02 22:04:32 +08:00
|
|
|
if name == "init.lua" then
|
|
|
|
fn(modname, path)
|
|
|
|
elseif (type == "file" or type == "link") and name:sub(-4) == ".lua" then
|
2023-01-02 03:19:09 +08:00
|
|
|
fn(modname .. "." .. name:sub(1, -5), path)
|
2022-11-25 05:03:00 +08:00
|
|
|
elseif type == "directory" and vim.loop.fs_stat(path .. "/init.lua") then
|
2022-12-21 16:03:40 +08:00
|
|
|
fn(modname .. "." .. name, path .. "/init.lua")
|
2022-11-25 05:03:00 +08:00
|
|
|
end
|
|
|
|
end)
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2023-01-17 21:00:33 +08:00
|
|
|
---@generic T
|
|
|
|
---@param list T[]
|
|
|
|
---@param add T[]
|
|
|
|
---@return T[]
|
|
|
|
function M.extend(list, add)
|
|
|
|
local idx = {}
|
|
|
|
for _, v in ipairs(list) do
|
|
|
|
idx[v] = v
|
|
|
|
end
|
|
|
|
for _, a in ipairs(add) do
|
|
|
|
if not idx[a] then
|
|
|
|
table.insert(list, a)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return list
|
|
|
|
end
|
|
|
|
|
2023-01-10 15:42:20 +08:00
|
|
|
---@alias LazyNotifyOpts {lang?:string, title?:string, level?:number}
|
|
|
|
|
2022-12-02 23:52:22 +08:00
|
|
|
---@param msg string|string[]
|
2023-01-10 15:42:20 +08:00
|
|
|
---@param opts? LazyNotifyOpts
|
|
|
|
function M.notify(msg, opts)
|
2023-01-03 23:17:15 +08:00
|
|
|
if vim.in_fast_event() then
|
2023-01-10 15:42:20 +08:00
|
|
|
return vim.schedule(function()
|
|
|
|
M.notify(msg, opts)
|
2023-01-03 23:17:15 +08:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
opts = opts or {}
|
2022-12-04 06:15:50 +08:00
|
|
|
if type(msg) == "table" then
|
|
|
|
msg = table.concat(
|
|
|
|
vim.tbl_filter(function(line)
|
|
|
|
return line or false
|
|
|
|
end, msg),
|
|
|
|
"\n"
|
|
|
|
)
|
|
|
|
end
|
2023-01-03 23:17:15 +08:00
|
|
|
local lang = opts.lang or "markdown"
|
2023-01-10 15:42:20 +08:00
|
|
|
vim.notify(msg, opts.level or vim.log.levels.INFO, {
|
2022-11-26 05:50:17 +08:00
|
|
|
on_open = function(win)
|
2023-01-03 23:17:15 +08:00
|
|
|
pcall(require, "nvim-treesitter")
|
2022-11-26 05:50:17 +08:00
|
|
|
vim.wo[win].conceallevel = 3
|
|
|
|
vim.wo[win].concealcursor = ""
|
|
|
|
vim.wo[win].spell = false
|
|
|
|
local buf = vim.api.nvim_win_get_buf(win)
|
2023-01-03 23:17:15 +08:00
|
|
|
if not pcall(vim.treesitter.start, buf, lang) then
|
|
|
|
vim.bo[buf].filetype = lang
|
|
|
|
vim.bo[buf].syntax = lang
|
2022-12-27 19:35:02 +08:00
|
|
|
end
|
2022-11-26 05:50:17 +08:00
|
|
|
end,
|
2023-01-10 15:42:20 +08:00
|
|
|
title = opts.title or "lazy.nvim",
|
2022-11-21 05:34:55 +08:00
|
|
|
})
|
2022-11-21 05:33:47 +08:00
|
|
|
end
|
|
|
|
|
2022-12-02 23:52:22 +08:00
|
|
|
---@param msg string|string[]
|
2023-01-10 15:42:20 +08:00
|
|
|
---@param opts? LazyNotifyOpts
|
|
|
|
function M.error(msg, opts)
|
|
|
|
opts = opts or {}
|
|
|
|
opts.level = vim.log.levels.ERROR
|
|
|
|
M.notify(msg, opts)
|
2022-11-26 05:50:17 +08:00
|
|
|
end
|
|
|
|
|
2022-12-02 23:52:22 +08:00
|
|
|
---@param msg string|string[]
|
2023-01-10 15:42:20 +08:00
|
|
|
---@param opts? LazyNotifyOpts
|
|
|
|
function M.info(msg, opts)
|
|
|
|
opts = opts or {}
|
|
|
|
opts.level = vim.log.levels.INFO
|
|
|
|
M.notify(msg, opts)
|
2022-11-23 05:35:06 +08:00
|
|
|
end
|
|
|
|
|
2022-12-02 23:52:22 +08:00
|
|
|
---@param msg string|string[]
|
2023-01-10 15:42:20 +08:00
|
|
|
---@param opts? LazyNotifyOpts
|
|
|
|
function M.warn(msg, opts)
|
|
|
|
opts = opts or {}
|
|
|
|
opts.level = vim.log.levels.WARN
|
|
|
|
M.notify(msg, opts)
|
2022-12-02 23:52:22 +08:00
|
|
|
end
|
|
|
|
|
2023-01-03 23:17:15 +08:00
|
|
|
---@param msg string|table
|
2023-01-10 15:42:20 +08:00
|
|
|
---@param opts? LazyNotifyOpts
|
|
|
|
function M.debug(msg, opts)
|
2023-01-03 23:17:15 +08:00
|
|
|
if not require("lazy.core.config").options.debug then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
opts = opts or {}
|
2023-01-10 15:42:20 +08:00
|
|
|
if opts.title then
|
|
|
|
opts.title = "lazy.nvim: " .. opts.title
|
|
|
|
end
|
2023-01-03 23:17:15 +08:00
|
|
|
if type(msg) == "string" then
|
2023-01-10 15:42:20 +08:00
|
|
|
M.notify(msg, opts)
|
2023-01-03 23:17:15 +08:00
|
|
|
else
|
|
|
|
opts.lang = "lua"
|
2023-01-10 15:42:20 +08:00
|
|
|
M.notify(vim.inspect(msg), opts)
|
2023-01-03 23:17:15 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-08 21:58:17 +08:00
|
|
|
local function can_merge(v)
|
|
|
|
return type(v) == "table" and (vim.tbl_isempty(v) or not M.is_list(v))
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Merges the values similar to vim.tbl_deep_extend with the **force** behavior,
|
|
|
|
--- but the values can be any type, in which case they override the values on the left.
|
|
|
|
--- Values will me merged in-place in the first left-most table. If you want the result to be in
|
|
|
|
--- a new table, then simply pass an empty table as the first argument `vim.merge({}, ...)`
|
|
|
|
--- Supports clearing values by setting a key to `vim.NIL`
|
2023-01-09 01:45:30 +08:00
|
|
|
---@generic T
|
|
|
|
---@param ... T
|
|
|
|
---@return T
|
2023-01-08 21:58:17 +08:00
|
|
|
function M.merge(...)
|
|
|
|
local values = { ... }
|
|
|
|
local ret = values[1]
|
2023-01-10 14:32:28 +08:00
|
|
|
|
|
|
|
if ret == vim.NIL then
|
|
|
|
ret = nil
|
|
|
|
end
|
|
|
|
|
2023-01-08 21:58:17 +08:00
|
|
|
for i = 2, #values, 1 do
|
|
|
|
local value = values[i]
|
2023-01-12 20:13:30 +08:00
|
|
|
if can_merge(ret) and can_merge(value) then
|
2023-01-08 21:58:17 +08:00
|
|
|
for k, v in pairs(value) do
|
|
|
|
ret[k] = M.merge(ret[k], v)
|
|
|
|
end
|
|
|
|
elseif value == vim.NIL then
|
|
|
|
ret = nil
|
|
|
|
else
|
|
|
|
ret = value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return ret
|
|
|
|
end
|
|
|
|
|
2022-11-21 05:33:47 +08:00
|
|
|
return M
|