Skip to content

Commit 6c3423d

Browse files
committed
refactor: simplify cache and add loading state
1 parent dcd77e0 commit 6c3423d

File tree

4 files changed

+123
-48
lines changed

4 files changed

+123
-48
lines changed

lua/ccusage/cli.lua

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,47 @@ local M = {}
33

44
local config = require("ccusage.config")
55

6-
-- Simple cache
6+
-- Simplified cache with 5-second TTL
77
local cache = {
8-
data = nil,
9-
last_update = 0,
10-
availability = nil,
8+
blocks = { data = nil, timestamp = 0 },
9+
availability = { data = nil, timestamp = 0 },
1110
}
1211

13-
---Simple jobstart to get ccusage blocks
14-
---@return CCUsage.Data?
15-
M.ccusage_blocks = function()
16-
-- Check cache first (5 second cache)
17-
---@diagnostic disable-next-line: undefined-field
18-
local now = vim.uv.now()
12+
local CACHE_TTL = 5000 -- 5 seconds in milliseconds
1913

20-
if cache.data and (now - cache.last_update) < 5000 then
21-
return cache.data
14+
---Check if cached value is still valid
15+
---@param cache_entry table cache entry with data and timestamp
16+
---@return boolean
17+
local function is_cache_valid(cache_entry)
18+
if not cache_entry.data then
19+
return false
2220
end
21+
---@diagnostic disable-next-line: undefined-field
22+
return (vim.uv.now() - cache_entry.timestamp) < CACHE_TTL
23+
end
2324

24-
-- Update timestamp immediately to prevent multiple jobs
25-
cache.last_update = now
25+
---Update cache entry
26+
---@param cache_entry table cache entry to update
27+
---@param data any data to cache
28+
local function update_cache(cache_entry, data)
29+
cache_entry.data = data
30+
---@diagnostic disable-next-line: undefined-field
31+
cache_entry.timestamp = vim.uv.now()
32+
end
33+
34+
---Get ccusage blocks data with simple caching
35+
---@param opts? {bypass_cache?: boolean, callback?: fun(data: CCUsage.Data?)}
36+
---@return CCUsage.Data?
37+
M.ccusage_blocks = function(opts)
38+
opts = opts or {}
39+
40+
-- Return cached data if valid and not bypassing cache
41+
if not opts.bypass_cache and is_cache_valid(cache.blocks) then
42+
if opts.callback then
43+
opts.callback(cache.blocks.data)
44+
end
45+
return cache.blocks.data
46+
end
2647

2748
local base_cmd = config.options.ccusage_cmd
2849

@@ -33,20 +54,39 @@ M.ccusage_blocks = function()
3354
local result = table.concat(data, "\n")
3455
local ok, parsed = pcall(vim.json.decode, result)
3556
if ok then
36-
cache.data = parsed
57+
-- Update cache only if not bypassing
58+
if not opts.bypass_cache then
59+
update_cache(cache.blocks, parsed)
60+
end
61+
if opts.callback then
62+
opts.callback(parsed)
63+
end
3764
end
3865
end
3966
end,
67+
on_exit = function(_, exit_code, _)
68+
if exit_code ~= 0 and opts.callback then
69+
opts.callback(nil)
70+
end
71+
end,
4072
})
4173

42-
return cache.data
74+
-- Return cached data if available and not bypassing cache
75+
return not opts.bypass_cache and cache.blocks.data or nil
4376
end
4477

45-
---Check if ccusage CLI is available
78+
---Check if ccusage CLI is available with simple caching
79+
---@param opts? {bypass_cache?: boolean, callback?: fun(available: boolean)}
4680
---@return boolean
47-
M.is_available = function()
48-
if cache.availability ~= nil then
49-
return cache.availability
81+
M.is_available = function(opts)
82+
opts = opts or {}
83+
84+
-- Return cached result if valid and not bypassing cache
85+
if not opts.bypass_cache and is_cache_valid(cache.availability) then
86+
if opts.callback then
87+
opts.callback(cache.availability.data)
88+
end
89+
return cache.availability.data
5090
end
5191

5292
local base_cmd = config.options.ccusage_cmd
@@ -59,19 +99,27 @@ M.is_available = function()
5999
end
60100
end,
61101
on_exit = function(_, exit_code, _)
62-
cache.availability = (exit_code == 0 and found)
102+
local available = (exit_code == 0 and found)
103+
-- Update cache only if not bypassing
104+
if not opts.bypass_cache then
105+
update_cache(cache.availability, available)
106+
end
107+
if opts.callback then
108+
opts.callback(available)
109+
end
63110
end,
64111
})
65112

66-
return cache.availability or false
113+
-- Return cached value if available and not bypassing cache
114+
return not opts.bypass_cache and (cache.availability.data or false) or false
67115
end
68116

69-
---Force refresh
117+
---Force refresh blocks data
70118
---@return CCUsage.Data?
71119
M.refresh_blocks = function()
72-
cache.data = nil
73-
cache.last_update = 0
74-
return M.ccusage_blocks()
120+
cache.blocks.data = nil
121+
cache.blocks.timestamp = 0
122+
return M.ccusage_blocks({ bypass_cache = true })
75123
end
76124

77125
return M

lua/ccusage/data.lua

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,55 @@
1-
---@class CCUsage.Data
1+
---@class CCUsage.DataModule
22
local M = {}
33

44
---Get formatter context with unified error handling
5+
---@param opts? {bypass_cache?: boolean, callback?: fun(data?: CCUsage.FormatterContext)}
56
---@return CCUsage.FormatterContext context with data/stats as nil if unavailable
6-
M.get_formatter_context = function()
7+
M.get_formatter_context = function(opts)
8+
opts = opts or {}
79
local cli = require("ccusage.cli")
810
local utils = require("ccusage.utils")
911

1012
---@type CCUsage.FormatterContext
11-
local context = {
12-
data = nil,
13-
stats = nil,
14-
}
13+
local context = { data = nil, stats = nil, loading = false }
1514

16-
-- Check if CLI is available
17-
if not cli.is_available() then
18-
return context
15+
-- Helper to create context from blocks data
16+
local function create_context(blocks_data)
17+
if not blocks_data or not blocks_data.blocks or #blocks_data.blocks == 0 then
18+
return { data = nil, stats = nil, loading = false }
19+
end
20+
21+
local stats = utils.compute_stats(blocks_data)
22+
return { data = blocks_data, stats = stats, loading = false }
1923
end
2024

21-
-- Get ccusage data
22-
local blocks_data = cli.ccusage_blocks()
23-
if not blocks_data or not blocks_data.blocks or #blocks_data.blocks == 0 then
25+
-- Async mode with callback
26+
if opts.callback then
27+
cli.is_available({
28+
bypass_cache = opts.bypass_cache,
29+
callback = function(available)
30+
if not available then
31+
opts.callback(context)
32+
return
33+
end
34+
35+
cli.ccusage_blocks({
36+
bypass_cache = opts.bypass_cache,
37+
callback = function(blocks_data)
38+
opts.callback(create_context(blocks_data))
39+
end,
40+
})
41+
end,
42+
})
2443
return context
2544
end
2645

27-
-- Set data
28-
context.data = blocks_data
29-
30-
-- Compute stats from blocks data
31-
local stats = utils.compute_stats(blocks_data)
32-
if stats then
33-
context.stats = stats
46+
-- Sync mode - simplified without loading states
47+
if not cli.is_available({ bypass_cache = opts.bypass_cache }) then
48+
return context
3449
end
3550

36-
return context
51+
local blocks_data = cli.ccusage_blocks({ bypass_cache = opts.bypass_cache })
52+
return create_context(blocks_data)
3753
end
3854

3955
return M

lua/ccusage/types.lua

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
-- lua/ccusage/cli.lua ---------------------------------------------------------
3131

3232
---@class CCUsage.CLI
33-
---@field ccusage_blocks fun(): CCUsage.Data? get ccusage blocks data
34-
---@field is_available fun(): boolean check if ccusage CLI is available
33+
---@field ccusage_blocks fun(opts?: {bypass_cache?: boolean, callback?: fun(data: CCUsage.Data?)}): CCUsage.Data? get ccusage blocks data
34+
---@field is_available fun(opts?: {bypass_cache?: boolean, callback?: fun(available: boolean)}): boolean check if ccusage CLI is available
3535
---@field refresh_blocks fun(): CCUsage.Data? force refresh ccusage blocks data
3636

3737
---@class CCUsage.Block
@@ -68,6 +68,12 @@
6868
---@class CCUsage.FormatterContext
6969
---@field data CCUsage.Data? -- Raw JSON data from ccusage command
7070
---@field stats CCUsage.Stats? -- Pre-computed stats from data for convenience
71+
---@field loading boolean -- Whether data is currently being loaded
72+
73+
-- lua/ccusage/data.lua --------------------------------------------------------
74+
75+
---@class CCUsage.DataModule
76+
---@field get_formatter_context fun(opts?: {bypass_cache?: boolean, callback?: fun(data?: CCUsage.FormatterContext)}): CCUsage.FormatterContext get formatter context with unified error handling
7177

7278
-- lua/ccusage/utils.lua -------------------------------------------------------
7379

lua/lualine/components/ccusage.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ local function ccusage_component()
99
-- Get formatter context with unified error handling
1010
local context = data.get_formatter_context()
1111

12+
-- Handle loading state
13+
if context.loading then
14+
return "ccusage: loading..."
15+
end
16+
1217
-- Handle errors with appropriate statusline messages
1318
if not context.data then
1419
return "ccusage: not found"

0 commit comments

Comments
 (0)