From c9916c7429f5e7f6f068427d23b8cc3870acac60 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sat, 27 Dec 2025 17:37:03 +0330 Subject: [PATCH 1/2] feat(test): performance test added --- test_performance.lua | 74 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test_performance.lua diff --git a/test_performance.lua b/test_performance.lua new file mode 100644 index 0000000..10e37b6 --- /dev/null +++ b/test_performance.lua @@ -0,0 +1,74 @@ +-- Test script for raphael.nvim performance improvements +-- This script tests the new caching and debouncing functionality + +local function test_palette_cache() + print("Testing palette cache functionality...") + + local palette_cache = require("raphael.core.palette_cache") + + -- Test generating palette data + local test_theme = "default" -- Use a basic theme that should always exist + local palette_data = palette_cache.generate_palette_data(test_theme) + + if palette_data then + print("✓ Successfully generated palette data for theme: " .. test_theme) + print(" Highlight groups found: " .. vim.inspect(vim.tbl_keys(palette_data))) + else + print("✗ Failed to generate palette data for theme: " .. test_theme) + end + + -- Test caching + ---@diagnostic disable-next-line: param-type-mismatch + palette_cache.cache_palette(test_theme, palette_data) + local cached_data = palette_cache.get_cached_palette(test_theme) + + if cached_data then + print("✓ Successfully cached and retrieved palette data") + else + print("✗ Failed to retrieve cached palette data") + end + + -- Test cache statistics + local stats = palette_cache.get_stats() + print("✓ Cache statistics: " .. vim.inspect(stats)) +end + +local function test_debounce() + print("\nTesting debounce functionality...") + + local debounce_utils = require("raphael.utils.debounce") + local test_count = 0 + + -- Create a debounced function + local debounced_fn = debounce_utils.debounce(function() + test_count = test_count + 1 + end, 50) -- 50ms delay + + -- Call the function multiple times quickly + ---@diagnostic disable-next-line: unused-local + for i = 1, 5 do + debounced_fn() + end + + print("✓ Debounced function called 5 times rapidly") + print(" Expected execution count after delay: 1") + print(" (Actual count will be checked after delay)") + + -- Wait and check result + vim.defer_fn(function() + print(" Actual execution count: " .. test_count) + if test_count == 1 then + print("✓ Debounce working correctly") + else + print("✗ Debounce not working as expected") + end + end, 100) -- Wait 100ms to ensure debounce has completed +end + +-- Run tests +print("Starting performance improvement tests for raphael.nvim...\n") +test_palette_cache() +test_debounce() + +print("\nTests initiated. Check results after delays for debounce test.") + From 5dd263b53b4e3da17504185636cb7b1b37568926 Mon Sep 17 00:00:00 2001 From: mavantgarderc Date: Sat, 27 Dec 2025 17:46:31 +0330 Subject: [PATCH 2/2] feat(cache): oiled up palette (icons) & theme loading by optimizing the cache --- .luarc.json | 5 + lua/raphael/core/autocmds.lua | 23 ++- lua/raphael/core/cache.lua | 14 ++ lua/raphael/core/init.lua | 58 +++++- lua/raphael/core/palette_cache.lua | 287 +++++++++++++++++++++++++++++ lua/raphael/picker/preview.lua | 114 ++++++------ lua/raphael/utils/debounce.lua | 70 +++++++ test_performance.lua | 17 +- 8 files changed, 512 insertions(+), 76 deletions(-) create mode 100644 .luarc.json create mode 100644 lua/raphael/core/palette_cache.lua create mode 100644 lua/raphael/utils/debounce.lua diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..1e1765c --- /dev/null +++ b/.luarc.json @@ -0,0 +1,5 @@ +{ + "diagnostics.globals": [ + "vim" + ] +} \ No newline at end of file diff --git a/lua/raphael/core/autocmds.lua b/lua/raphael/core/autocmds.lua index ea87d9d..5140def 100644 --- a/lua/raphael/core/autocmds.lua +++ b/lua/raphael/core/autocmds.lua @@ -238,10 +238,23 @@ function M.picker_cursor_autocmd(picker_buf, cbs) end cbs = cbs or {} local parse = cbs.parse - local preview = cbs.preview + local preview_fn = cbs.preview local highlight = cbs.highlight local update_preview = cbs.update_preview + local debounce_utils = require("raphael.utils.debounce") + local debounced_preview = debounce_utils.debounce(function(theme) + if theme and type(preview_fn) == "function" then + preview_fn(theme) + end + end, 100) + + local debounced_update_preview = debounce_utils.debounce(function(opts) + if type(update_preview) == "function" then + update_preview(opts or { debounced = true }) + end + end, 50) + vim.api.nvim_create_autocmd("CursorMoved", { buffer = picker_buf, callback = function() @@ -254,15 +267,13 @@ function M.picker_cursor_autocmd(picker_buf, cbs) if type(parse) == "function" then theme = parse(line) end - if theme and type(preview) == "function" then - preview(theme) + if theme then + debounced_preview(theme) end if type(highlight) == "function" then highlight() end - if type(update_preview) == "function" then - update_preview({ debounced = true }) - end + debounced_update_preview({ debounced = true }) end, }) end diff --git a/lua/raphael/core/cache.lua b/lua/raphael/core/cache.lua index 8d66139..5918a56 100644 --- a/lua/raphael/core/cache.lua +++ b/lua/raphael/core/cache.lua @@ -315,6 +315,7 @@ function M.toggle_bookmark(theme, scope) state.bookmarks[scope] = list M.write(state) + ---@diagnostic disable-next-line: redundant-return-value return false, state.bookmarks else if #list >= constants.MAX_BOOKMARKS then @@ -322,11 +323,13 @@ function M.toggle_bookmark(theme, scope) string.format("raphael.nvim: Max bookmarks (%d) reached in scope '%s'!", constants.MAX_BOOKMARKS, scope), vim.log.levels.WARN ) + ---@diagnostic disable-next-line: redundant-return-value return false, nil end table.insert(list, theme) state.bookmarks[scope] = list M.write(state) + ---@diagnostic disable-next-line: redundant-return-value return true, state.bookmarks end end @@ -409,6 +412,14 @@ function M.get_all_usage() return state.usage or {} end +--- Clear expired entries from the palette cache +function M.clear_expired_palette_cache() + local ok, palette_cache = pcall(require, "raphael.core.palette_cache") + if ok and palette_cache and palette_cache.clear_expired then + palette_cache.clear_expired() + end +end + --- Get or set collapsed state for a group key in persistent state. --- --- If `collapsed` is provided, it sets the value and writes to disk. @@ -511,6 +522,7 @@ end --- @param theme string --- @param scope string|nil function M.set_quick_slot(slot, theme, scope) + ---@diagnostic disable-next-line: cast-local-type slot = normalize_slot(slot) scope = scope or "__global" if not slot then @@ -537,6 +549,7 @@ end --- @param slot string|number --- @param scope string|nil function M.clear_quick_slot(slot, scope) + ---@diagnostic disable-next-line: cast-local-type slot = normalize_slot(slot) scope = scope or "__global" if not slot then @@ -557,6 +570,7 @@ end --- @param scope string|nil --- @return string|nil function M.get_quick_slot(slot, scope) + ---@diagnostic disable-next-line: cast-local-type slot = normalize_slot(slot) scope = scope or "__global" if not slot then diff --git a/lua/raphael/core/init.lua b/lua/raphael/core/init.lua index ab08fa7..b96a643 100644 --- a/lua/raphael/core/init.lua +++ b/lua/raphael/core/init.lua @@ -19,6 +19,14 @@ local history = require("raphael.extras.history") local themes = require("raphael.themes") local picker = require("raphael.picker.ui") +local palette_cache = nil +local function get_palette_cache() + if not palette_cache then + palette_cache = require("raphael.core.palette_cache") + end + return palette_cache +end + local M = {} --- Base configuration as provided by the user (after validation). @@ -137,7 +145,6 @@ local function make_effective_config(base, profile_name) end local effective = vim.tbl_deep_extend("force", base_copy, prof_cfg) - -- Ensure profiles table itself isn't overridden by profile contents. effective.profiles = profiles effective.current_profile = profile_name return effective @@ -180,7 +187,6 @@ function M.setup(config) M.config = make_effective_config(M.base_config, requested_profile) M.state.current_profile = requested_profile - -- Refresh scoped state snapshots from disk M.state.bookmarks = cache.get_bookmarks_table() M.state.quick_slots = cache.get_quick_slots_table() @@ -387,7 +393,33 @@ end ---@param opts table|nil function M.open_picker(opts) - return picker.open(M, opts or {}) + opts = opts or {} + + local preload_themes = vim.schedule_wrap(function() + local themes_to_preload = {} + if opts.exclude_configured then + local all_installed = vim.tbl_keys(themes.installed) + local all_configured = themes.get_all_themes() + for _, theme in ipairs(all_installed) do + if not vim.tbl_contains(all_configured, theme) then + table.insert(themes_to_preload, theme) + end + end + else + themes_to_preload = themes.get_all_themes() + end + + local palette_cache_mod = get_palette_cache() + if palette_cache_mod then + palette_cache_mod.preload_palettes(themes_to_preload) + end + end) + + local result = picker.open(M, opts or {}) + + preload_themes() + + return result end --- Set a quick favorite slot (0–9) to a theme (profile-aware if profile_scoped_state=true). @@ -451,7 +483,6 @@ function M.set_profile(name, apply_default) M.config = effective M.state.current_profile = name - -- refresh bookmarks/quick_slots from disk (so scope view is correct) M.state.bookmarks = cache.get_bookmarks_table() M.state.quick_slots = cache.get_quick_slots_table() @@ -573,4 +604,23 @@ function M.get_profile_config(name) return make_effective_config(M.base_config, name) end +-- Set up periodic cleanup of expired cache entries (lazy initialization) +local function setup_periodic_cleanup() + vim.schedule(function() + vim.defer_fn(function() + local ok, palette_cache_mod = pcall(get_palette_cache) + if ok and palette_cache_mod and palette_cache_mod.clear_expired then + palette_cache_mod.clear_expired() + end + local ok2, cache_mod = pcall(require, "raphael.core.cache") + if ok2 and cache_mod and cache_mod.clear_expired_palette_cache then + cache_mod.clear_expired_palette_cache() + end + setup_periodic_cleanup() + end, 300000) -- ~ 5 minutes + end) +end + +vim.defer_fn(setup_periodic_cleanup, 10000) + return M diff --git a/lua/raphael/core/palette_cache.lua b/lua/raphael/core/palette_cache.lua new file mode 100644 index 0000000..bcd6a54 --- /dev/null +++ b/lua/raphael/core/palette_cache.lua @@ -0,0 +1,287 @@ +-- lua/raphael/core/palette_cache.lua +-- Enhanced caching system for theme palettes and highlight information +-- This module provides efficient caching and retrieval of theme palette data + +local M = {} + +local themes = require("raphael.themes") +local PALETTE_HL = { + "Normal", + "Comment", + "String", + "Keyword", + "Function", + "Type", + "Constant", + "Special", +} + +-- Cache structure: { theme_name = { timestamp, palette_data } } +local cache = {} +local max_cache_size = 50 +local cache_timeout = 300 + +--- Get highlight RGB value for a given highlight group +---@param name string Highlight group name +---@return table|nil hl_data Highlight data with fg, bg, etc. +local function get_hl_rgb(name) + local ok, hl = pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) + if ok and hl then + return hl + end + return nil +end + +--- Generate palette data for a theme +---@param theme string Theme name +---@return table|nil palette_data Palette data with highlight information +function M.generate_palette_data(theme) + if not theme or not themes.is_available(theme) then + return nil + end + + local current_colors_name = vim.g.colors_name + local current_hls = {} + + for _, hl_name in ipairs(PALETTE_HL) do + current_hls[hl_name] = get_hl_rgb(hl_name) + end + + local success = pcall(function() + vim.cmd("hi clear") + if vim.fn.exists("syntax_on") == 1 then + vim.cmd("syntax reset") + end + + local lua_path = vim.api.nvim_get_runtime_file("colors/" .. theme .. ".lua", false)[1] + local vim_path = vim.api.nvim_get_runtime_file("colors/" .. theme .. ".vim", false)[1] + local path = lua_path or vim_path + + if path then + if lua_path then + dofile(path) + else + vim.cmd("source " .. vim.fn.fnameescape(path)) + end + else + vim.cmd.colorscheme(theme) + end + + vim.cmd("syntax on") + vim.cmd("doautocmd ColorScheme") + end) + + if not success then + for hl_name, hl_data in pairs(current_hls) do + if hl_data then + pcall(vim.api.nvim_set_hl, 0, hl_name, hl_data) + end + end + if current_colors_name then + pcall(vim.api.nvim_set_var, "colors_name", current_colors_name) + else + pcall(vim.api.nvim_del_var, "colors_name") + end + return nil + end + + local palette_data = {} + for _, hl_name in ipairs(PALETTE_HL) do + local hl = get_hl_rgb(hl_name) + if hl then + palette_data[hl_name] = { + fg = hl.fg or hl.foreground, + bg = hl.bg or hl.background, + sp = hl.sp or hl.special, + bold = hl.bold, + italic = hl.italic, + underline = hl.underline, + reverse = hl.reverse, + } + end + end + + for hl_name, hl_data in pairs(current_hls) do + if hl_data then + pcall(vim.api.nvim_set_hl, 0, hl_name, hl_data) + end + end + if current_colors_name then + pcall(vim.api.nvim_set_var, "colors_name", current_colors_name) + else + pcall(vim.api.nvim_del_var, "colors_name") + end + + if current_colors_name then + pcall(vim.cmd.colorscheme, current_colors_name) + end + + return palette_data +end + +--- Check if cache entry is still valid (not expired) +---@param entry table Cache entry with timestamp +---@return boolean Validity status +local function is_cache_valid(entry) + if not entry or not entry.timestamp then + return false + end + return (os.time() - entry.timestamp) < cache_timeout +end + +--- Get cached palette data for a theme +---@param theme string Theme name +---@return table|nil palette_data Cached palette data or nil if not available/cached +function M.get_cached_palette(theme) + if not theme then + return nil + end + + local entry = cache[theme] + if entry and is_cache_valid(entry) then + return entry.palette_data + end + + if entry then + cache[theme] = nil + end + + return nil +end + +--- Cache palette data for a theme +---@param theme string Theme name +---@param palette_data table Palette data to cache +function M.cache_palette(theme, palette_data) + if not theme or not palette_data then + return + end + + local cache_keys = {} + for k in pairs(cache) do + table.insert(cache_keys, k) + end + + if #cache_keys >= max_cache_size then + local oldest_key = nil + local oldest_time = math.huge + for k, v in pairs(cache) do + if v.timestamp and v.timestamp < oldest_time then + oldest_time = v.timestamp + oldest_key = k + end + end + if oldest_key then + cache[oldest_key] = nil + end + end + + cache[theme] = { + timestamp = os.time(), + palette_data = palette_data, + } +end + +--- Preload palettes for a list of themes (async and non-blocking) +---@param themes_list table List of theme names to preload +function M.preload_palettes(themes_list) + if not themes_list or type(themes_list) ~= "table" then + return + end + + local max_preload = 10 + local preload_list = {} + + for i, theme in ipairs(themes_list) do + if i > max_preload then + break + end + table.insert(preload_list, theme) + end + + vim.defer_fn(function() + local function preload_next(index) + if index > #preload_list then + return + end + + local theme = preload_list[index] + if not M.get_cached_palette(theme) then + local palette_data = M.generate_palette_data(theme) + if palette_data then + M.cache_palette(theme, palette_data) + end + end + + vim.defer_fn(function() + preload_next(index + 1) + end, 10) + end + + preload_next(1) + end, 10) +end + +--- Clear the entire cache +function M.clear_cache() + cache = {} +end + +--- Clear expired entries from cache +function M.clear_expired() + local now = os.time() + for theme, entry in pairs(cache) do + if (now - entry.timestamp) >= cache_timeout then + cache[theme] = nil + end + end +end + +--- Get cache statistics +---@return table Cache statistics +function M.get_stats() + local valid_count = 0 + local expired_count = 0 + local now = os.time() + + for _, entry in pairs(cache) do + if (now - entry.timestamp) < cache_timeout then + valid_count = valid_count + 1 + else + expired_count = expired_count + 1 + end + end + + return { + total_entries = valid_count + expired_count, + valid_entries = valid_count, + expired_entries = expired_count, + max_size = max_cache_size, + timeout_seconds = cache_timeout, + } +end + +--- Get palette data with caching +--- This is the main function to use when you need palette data +---@param theme string Theme name +---@return table|nil palette_data Palette data (cached or generated) +function M.get_palette_with_cache(theme) + if not theme then + return nil + end + + local cached = M.get_cached_palette(theme) + if cached then + return cached + end + + local palette_data = M.generate_palette_data(theme) + if palette_data then + M.cache_palette(theme, palette_data) + return palette_data + end + + return nil +end + +return M diff --git a/lua/raphael/picker/preview.lua b/lua/raphael/picker/preview.lua index 963f30f..32ffd48 100644 --- a/lua/raphael/picker/preview.lua +++ b/lua/raphael/picker/preview.lua @@ -18,6 +18,7 @@ local M = {} local themes = require("raphael.themes") local samples = require("raphael.core.samples") +local palette_cache = require("raphael.core.palette_cache") local C = require("raphael.constants") -- Palette preview (top mini bar) @@ -54,14 +55,6 @@ local function reset_compare_state() active_preview_theme = nil end -local function get_hl_rgb(name) - local ok, hl = pcall(vim.api.nvim_get_hl, 0, { name = name, link = false }) - if ok and hl then - return hl - end - return nil -end - local function ensure_palette_hl(idx, color_int) if not color_int then return nil @@ -91,6 +84,8 @@ end --- Draws a single-line window above the picker, consisting of BLOCK icons --- colored by various highlight groups (Normal, Comment, String, etc.) --- +--- Uses the enhanced caching system for improved performance. +--- ---@param ctx table # picker context ---@param theme string function M.update_palette(ctx, theme) @@ -127,20 +122,17 @@ function M.update_palette(ctx, theme) local ns = C.NS.PALETTE pcall(vim.api.nvim_buf_clear_namespace, palette_buf, ns, 0, -1) - if not palette_hl_cache[theme] or type(palette_hl_cache[theme]) ~= "table" then - palette_hl_cache[theme] = {} - for _, hl_name in ipairs(PALETTE_HL) do - local hl = get_hl_rgb(hl_name) - if hl then - palette_hl_cache[theme][hl_name] = hl.fg or hl.bg - end - end + local palette_data = palette_cache.get_palette_with_cache(theme) + if not palette_data then + pcall(vim.api.nvim_buf_clear_namespace, palette_buf, ns, 0, -1) + vim.cmd("redraw!") + return end for i, hl_name in ipairs(PALETTE_HL) do - local color_int = palette_hl_cache[theme][hl_name] - if color_int then - local gname = ensure_palette_hl(i, color_int) + local hl_info = palette_data[hl_name] + if hl_info and hl_info.fg then + local gname = ensure_palette_hl(i, hl_info.fg) if gname then local search_pos, occurrence = 1, 0 while true do @@ -240,7 +232,6 @@ function M.preview_theme(ctx, theme) return end - -- In compare mode, moving cursor updates the candidate theme. if compare_mode then compare_candidate_theme = theme compare_active_side = "candidate" @@ -296,6 +287,10 @@ local function get_active_theme_label(ctx) return active_preview_theme or (ctx.core.state and ctx.core.state.current) or "?" end +-- Debounce utility for preview updates +local debounce_utils = require("raphael.utils.debounce") +local debounced_code_preview = nil + --- Update code preview buffer based on current_lang and preview/compare state. ---@param ctx table function M.update_code_preview(ctx) @@ -303,42 +298,48 @@ function M.update_code_preview(ctx) return end - ensure_code_buf() + if not debounced_code_preview then + debounced_code_preview = debounce_utils.debounce(function(ctx_copy) + ensure_code_buf() - local lang_info = samples.get_language_info(current_lang or "lua") - local sample_code = samples.get_sample(current_lang or "lua") + local lang_info = samples.get_language_info(current_lang or "lua") + local sample_code = samples.get_sample(current_lang or "lua") - if not sample_code then - vim.api.nvim_buf_set_lines(code_buf, 0, -1, false, { - "Sample unavailable - fallback to basic text.", - }) - return - end + if not sample_code then + vim.api.nvim_buf_set_lines(code_buf, 0, -1, false, { + "Sample unavailable - fallback to basic text.", + }) + return + end - local theme_label = get_active_theme_label(ctx) - local header - if compare_mode then - header = string.format( - "[%s] - compare base=%s | cand=%s | showing=%s", - lang_info.display, - compare_base_theme or "?", - compare_candidate_theme or "?", - theme_label or "?" - ) - else - header = string.format("[%s] - [%s]", lang_info.display, theme_label or "?") - end + local theme_label = get_active_theme_label(ctx_copy) + local header + if compare_mode then + header = string.format( + "[%s] - compare base=%s | cand=%s | showing=%s", + lang_info.display, + compare_base_theme or "?", + compare_candidate_theme or "?", + theme_label or "?" + ) + else + header = string.format("[%s] - [%s]", lang_info.display, theme_label or "?") + end - local lines = vim.split(sample_code, "\n") - local all = { header, "" } - for _, l in ipairs(lines) do - table.insert(all, l) - end + local lines = vim.split(sample_code, "\n") + local all = { header, "" } + for _, l in ipairs(lines) do + table.insert(all, l) + end + + vim.api.nvim_buf_set_lines(code_buf, 0, -1, false, all) + vim.api.nvim_set_option_value("filetype", lang_info.ft, { buf = code_buf }) - vim.api.nvim_buf_set_lines(code_buf, 0, -1, false, all) - vim.api.nvim_set_option_value("filetype", lang_info.ft, { buf = code_buf }) + vim.cmd(string.format("silent! syntax on | syntax enable | setlocal syntax=%s", lang_info.ft)) + end, 75) + end - vim.cmd(string.format("silent! syntax on | syntax enable | setlocal syntax=%s", lang_info.ft)) + debounced_code_preview(ctx) end local function open_code_window(ctx) @@ -452,7 +453,6 @@ function M.toggle_compare(ctx, candidate_theme) return end - -- Ensure preview window is visible and language selected. if not is_preview_visible then local allowed_langs = get_allowed_langs(ctx.core) current_lang = current_lang or allowed_langs[1] or "lua" @@ -508,6 +508,7 @@ function M.toggle_compare(ctx, candidate_theme) end palette_hl_cache = {} + ---@diagnostic disable-next-line: param-type-mismatch M.update_palette(ctx, active_preview_theme) M.update_code_preview(ctx) end @@ -534,17 +535,22 @@ end --- { --- palette_cache_size = integer, --- active_timers = integer, -- currently always 0 +--- enhanced_cache_stats = table, -- stats from the new caching system --- } function M.get_cache_stats() - local palette_size = 0 + local legacy_palette_size = 0 for k, _ in pairs(palette_hl_cache) do if type(k) == "string" then - palette_size = palette_size + 1 + legacy_palette_size = legacy_palette_size + 1 end end + + local enhanced_stats = palette_cache.get_stats() + return { - palette_cache_size = palette_size, + palette_cache_size = legacy_palette_size, active_timers = 0, + enhanced_cache_stats = enhanced_stats, } end diff --git a/lua/raphael/utils/debounce.lua b/lua/raphael/utils/debounce.lua new file mode 100644 index 0000000..e2c13dc --- /dev/null +++ b/lua/raphael/utils/debounce.lua @@ -0,0 +1,70 @@ +-- lua/raphael/utils/debounce.lua +-- Debouncing utility for Raphael.nvim +-- Provides debouncing functionality to improve performance during rapid events + +local M = {} + +--- Create a debounced function +--- @param fn function The function to debounce +--- @param delay number Delay in milliseconds +--- @return function The debounced function +function M.debounce(fn, delay) + local timer = nil + return function(...) + local args = { ... } + if timer then + local ok = pcall(function() -- luacheck: ignore + if timer and not timer:is_closing() then + timer:stop() + vim.schedule(function() + if timer and not timer:is_closing() then + timer:close() + end + end) + end + end) + timer = nil + end + timer = vim.defer_fn(function() + ---@diagnostic disable-next-line: deprecated + pcall(fn, _G.unpack(args)) + timer = nil + end, delay) + end +end + +--- Create a throttled function +--- @param fn function The function to throttle +--- @param delay number Delay in milliseconds +--- @return function The throttled function +function M.throttle(fn, delay) + local timer = nil + local last_run = 0 + local queued_args = nil + + return function(...) + local args = { ... } + local now = vim.loop.now() + + if now - last_run > delay then + last_run = now + ---@diagnostic disable-next-line: deprecated + pcall(fn, _G.unpack(args)) + else + queued_args = args + if not timer then + timer = vim.defer_fn(function() + if queued_args then + ---@diagnostic disable-next-line: deprecated + pcall(fn, _G.unpack(queued_args)) + queued_args = nil + end + timer = nil + last_run = vim.loop.now() + end, delay - (now - last_run)) + end + end + end +end + +return M diff --git a/test_performance.lua b/test_performance.lua index 10e37b6..9904d96 100644 --- a/test_performance.lua +++ b/test_performance.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: unused-local -- Test script for raphael.nvim performance improvements -- This script tests the new caching and debouncing functionality @@ -6,8 +7,7 @@ local function test_palette_cache() local palette_cache = require("raphael.core.palette_cache") - -- Test generating palette data - local test_theme = "default" -- Use a basic theme that should always exist + local test_theme = "default" local palette_data = palette_cache.generate_palette_data(test_theme) if palette_data then @@ -17,7 +17,6 @@ local function test_palette_cache() print("✗ Failed to generate palette data for theme: " .. test_theme) end - -- Test caching ---@diagnostic disable-next-line: param-type-mismatch palette_cache.cache_palette(test_theme, palette_data) local cached_data = palette_cache.get_cached_palette(test_theme) @@ -28,7 +27,6 @@ local function test_palette_cache() print("✗ Failed to retrieve cached palette data") end - -- Test cache statistics local stats = palette_cache.get_stats() print("✓ Cache statistics: " .. vim.inspect(stats)) end @@ -39,14 +37,11 @@ local function test_debounce() local debounce_utils = require("raphael.utils.debounce") local test_count = 0 - -- Create a debounced function local debounced_fn = debounce_utils.debounce(function() test_count = test_count + 1 - end, 50) -- 50ms delay + end, 50) - -- Call the function multiple times quickly - ---@diagnostic disable-next-line: unused-local - for i = 1, 5 do + for i = 1, 5 do --luacheck: ignore debounced_fn() end @@ -54,7 +49,6 @@ local function test_debounce() print(" Expected execution count after delay: 1") print(" (Actual count will be checked after delay)") - -- Wait and check result vim.defer_fn(function() print(" Actual execution count: " .. test_count) if test_count == 1 then @@ -62,7 +56,7 @@ local function test_debounce() else print("✗ Debounce not working as expected") end - end, 100) -- Wait 100ms to ensure debounce has completed + end, 100) end -- Run tests @@ -71,4 +65,3 @@ test_palette_cache() test_debounce() print("\nTests initiated. Check results after delays for debounce test.") -