refactor: optimize

This commit is contained in:
Kristofers Solo 2025-05-26 01:10:15 +03:00
parent 97619e677c
commit 363f3e78a3
Signed by: kristoferssolo
GPG Key ID: 8687F2D3EEE6F0ED
6 changed files with 247 additions and 29 deletions

View File

@ -4,23 +4,134 @@ local cfg = require("lualine-harpoon.config")
local req = require("lualine_require")
local Component = req.require("lualine.component")
---@class LualineHarpoonComponent : lualine.Component
---@field cache LualineHarpoonCache
---@field options LualineHarpoonConfig
local M = Component:extend()
---@class LualineHarpoonCache
---@field result string?
---@field is_valid boolean
---@field last_buf string?
---@field last_changedtick integer?
---@field last_status_hash string?
---@class LualineHarpoonConfig
---@field symbol LualineHarpoonSymbols
---@field icon string
---@field show_when_empty boolean
---@field show_icon boolean
---@field format function?
---@field colors LualineHarpoonColors
---@field cache_timeout integer
---@class LualineHarpoonSymbols
---@field open string
---@field close string
---@field separator string
---@field unknown string
---@class LualineHarpoonColors
---@field active string?
---@field inactive string?
---@param opts table?
function M:init(opts)
M.super.init(self, opts)
M.super:init(opts)
self.options = vim.tbl_deep_extend("force", cfg, self.options or {})
self.cache = {
result = nil,
is_valid = false,
last_buf = nil,
last_changedtick = nil,
last_status_hash = nil,
}
self:setup_cache_invalidation()
end
function M:update_status()
local st = status.get_status()
if st.total == 0 then
return ""
---@private
function M:setup_cache_invalidation()
local group_name = "LualineHarpoon_" .. tostring(self)
local group = vim.api.nvim_create_augroup(group_name, { clear = true })
vim.api.nvim_create_autocmd({
"BufEnter",
"BufWritePost",
"User",
}, {
group = group,
callback = function()
self.cache.is_valid = false
end,
})
end
---@param st HarpoonStatus
---@return string
function M:hash_status(st)
return string.format("%s:%s", tostring(st.current or "nil"), st.total)
end
---@return boolean
function M:is_cache_valid()
if not self.cache.is_valid then
return false
end
local s = self.options.symbol
local n = st.current and tostring(st.current) or s.unknown
return string.format("%s%s%s%d%s", s.open, n, s.separator, st.total, s.close)
local current_buf = vim.api.nvim_buf_get_name(0)
local current_changetick = vim.api.nvim_buf_get_changedtick(0)
if self.cache.last_buf ~= current_buf or self.cache.last_changedtick ~= current_changetick then
return false
end
local current_status = status.get_status()
local current_hash = self:hash_status(current_status)
return self.cache.last_status_hash == current_hash
end
---@param new_cfg LualineHarpoonConfig
function M.upadte_config(new_cfg)
cfg = new_cfg
end
---@param result string
---@private
function M:update_cache(result)
local current_status = status.get_status()
self.cache.result = result
self.cache.is_valid = true
self.cache.last_buf = vim.api.nvim_buf_get_name(0)
self.cache.last_changedtick = vim.api.nvim_buf_get_changedtick(0)
self.cache.last_status_hash = self:hash_status(current_status)
end
---@return string
function M:update_status()
if self:is_cache_valid() then
return self.cache.result
end
local st = status.get_status()
local result = ""
if type(self.options.format) == "function" then
result = self.options.format(st.current, st.total)
elseif st.total > 0 then
local s = self.options.symbol
local n = st.current and tostring(st.current) or s.unknown
result = string.format("%s%s%s%d%s", s.open, n, s.separator, st.total, s.close)
end
self:update_cache(result)
return result
end
---@return string
function M:draw_status()
local text = self:update_status()
if self.color_hl and text ~= "" then

View File

@ -1,3 +1,23 @@
---@class LualineHarpoonConfig
---@field symbol LualineHarpoonSymbols
---@field icon string
---@field show_when_empty boolean
---@field show_icon boolean
---@field format function?
---@field colors LualineHarpoonColors
---@field cache_timeout integer
---@class LualineHarpoonSymbols
---@field open string
---@field close string
---@field separator string
---@field unknown string
---@class LualineHarpoonColors
---@field active string?
---@field inactive string?
---@type LualineHarpoonConfig
local M = {
symbol = {
open = "[",
@ -6,6 +26,14 @@ local M = {
unknown = "?",
},
icon = "󰀱",
show_when_empty = false,
show_icon = true,
format = nil,
colors = {
active = nil,
inactive = nil,
},
cache_timeout = 100,
}
return M

View File

@ -1,27 +1,40 @@
---@class LualineHarpoonHealth
local M = {}
---@return nil
function M.check()
vim.health.start("lualine-harpoon")
local has_lualine = pcall(require, "lualine")
-- Check for required dependencies
local has_lualine, lualine_version = pcall(require, "lualine")
if has_lualine then
vim.health.ok("lualine is installed")
if lualine_version and lualine_version.version then
vim.health.info("lualine version: " .. lualine_version.version)
end
else
vim.health.error("lualine is not installed")
end
local has_harpoon = pcall(require, "harpoon")
local has_harpoon, harpoon_module = pcall(require, "harpoon")
if has_harpoon then
vim.health.ok("harpoon is installed")
-- Check if it's harpoon 2.x
if harpoon_module and harpoon_module.list then
vim.health.info("harpoon 2.x detected")
else
vim.health.warn("harpoon 1.x detected - may not be fully compatible")
end
else
vim.health.warn("harpoon is not installed - component will show empty")
end
local has_plenary = pcall(require, "plenary.path")
if has_plenary then
vim.health.ok("plenary.nvim is installed (recommended)")
-- Check Neovim version
local nvim_version = vim.version()
if nvim_version.major == 0 and nvim_version.minor < 8 then
vim.health.error("Neovim 0.8+ is required")
else
vim.health.warn("plenary.nvim is not installed - path normalization disabled")
vim.health.ok(string.format("Neovim %d.%d.%d", nvim_version.major, nvim_version.minor, nvim_version.patch))
end
end

View File

@ -1,9 +1,32 @@
local component = require("lualine-harpoon.component")
local cfg = require("lualine-harpoon.config")
---@class LualineHarpoonPlugin
---@field component LualineHarpoonComponent
local M = {
component = component,
}
function M.setup(_opts) end
---@param opts LualineHarpoonConfig?
---@return nil
function M.setup(opts)
opts = opts or {}
-- Validate options
if opts.cache_timeout and type(opts.cache_timeout) ~= "number" then
vim.notify("lualine-harpoon: cache_timeout must be a number", vim.log.levels.WARN)
opts.cache_timeout = nil
end
if opts.format and type(opts.format) ~= "function" then
vim.notify("lualine-harpoon: format must be a function", vim.log.levels.WARN)
opts.format = nil
end
---@type LualineHarpoonConfig
local updated_cfg = vim.tbl_deep_extend("force", cfg, opts)
component.upadte_config(updated_cfg)
end
return M

View File

@ -1,27 +1,49 @@
local ok, harpoon = pcall(require, "harpoon")
local utils = require("lualine-harpoon.utils")
---@class HarpoonStatus
---@field current integer? 1-based index of current file in harpoon list
---@field total integer Total number of files in harpoon list
---@class LualineHarpoonStatus
local M = {}
--- Get current 1-based index and total Harpoon marks
---@return { current: integer?, total: integer }
---@return HarpoonStatus
function M.get_status()
if not ok then
return { current = nil, total = 0 }
end
local list = harpoon:list()
local total = list:length()
local success, list = pcall(function()
return harpoon:list()
end)
if not success or not list then
return { current = nil, total = 0 }
end
local total = list:length()
if total == 0 then
return { current = nil, total = 0 }
end
local root = list.config.get_root_dir()
local success_root, root = pcall(function()
return list.config.get_root_dir()
end)
if not success_root then
return { current = nil, total = 0 }
end
local buf = vim.api.nvim_buf_get_name(0)
local rel_buf = utils.normalize(buf, root)
local _, idx = list:get_by_value(rel_buf)
if type(idx) ~= "number" or idx < 1 or idx > total then
local success_get, idx = pcall(function()
local _, index = list:get_by_value(rel_buf)
return index
end)
if not success_get or type(idx) ~= "number" or idx < 1 or idx > total then
idx = nil
end
return { current = idx, total = total }

View File

@ -1,16 +1,37 @@
local ok_path, Path = pcall(require, "plenary.path")
---@class LualineHarpoonUtils
local M = {}
--- Normalize `buf` relative to `root`, if plenary.path is available.
---@param buf string
---@param root string
---@return string
---Normalize `buf` relative to `root` using Neovim built-ins
---@param buf string Absolute path to buffer
---@param root string Root directory path
---@return string Normalized path relative to root
function M.normalize(buf, root)
if ok_path then
return Path:new(buf):make_relative(root)
else
if not buf or buf == "" then
return ""
end
if not root or root == "" then
return buf
end
-- Use vim.fs.normalize for consistent path separators (Neovim 0.8+)
if vim.fs and vim.fs.normalize then
buf = vim.fs.normalize(buf)
root = vim.fs.normalize(root)
end
-- Use vim.fn.fnamemodify to make path relative
local relative = vim.fn.fnamemodify(buf, ":p:.")
-- If the buffer is within the root directory, make it relative to root
if buf:find("^" .. vim.pesc(root), 1) then
relative = buf:sub(#root + 1)
-- Remove leading path separator
relative = relative:gsub("^[/\\]", "")
return relative ~= "" and relative or "."
end
return relative
end
return M