Statistics for NeoVim usage
- Live tracking for mode switching
- Live tracking for (some) motions
- Store usage statistics to disk
- Send periodic sync data to a web endpoint
- Send realtime data to a web endpoint
nav = { 'h', 'j', 'k', 'l' },
word = { 'w', 'b', 'e', 'ge' },
scroll = { '<C-d>', '<C-u>', '<C-f>', '<C-b>' },
find = { 'f', 'F', 't', 'T' },
search = { '*', '#', 'n', 'N' },
paragraph = { '{', '}' },
line = { '0', '$', '^', 'g_' },
indent = { '>', '<', '=', '>>', '<<' },
jumps = { 'gi', 'gv', '<C-o>', '<C-i>', 'g;', 'g,' },
Session tracking should start as soon as NeoVim starts, along with local storage syncing as well as the web sync endpoint.
Available commands:
:Stalker
- Show current session statistics:StalkerTotals
- Show total statistics:StalkerResetSync
- Reset web sync state after failures:StalkerResetRlSync
- Reset live sync state after failure
- If web sync fails, use
:StalkerResetSync
to reset the sync state - If live sync fails, use
:StalkerResetRlSync
to reset the sync state - Check
:messages
for debug output whenverbose = true
- NeoVim >= v0.9.0 (I need to double check this)
- cURL, I guess. For some things.
{
'lnus/stalker.nvim',
opts = {} -- config goes here
},
Alternatively
{
'lnus/stalker.nvim',
config = function()
require('stalker').setup {} -- config goes here
end,
},
These options can all be passed through opts
if using lazy.nvim
require('stalker').setup{
-- These are the default config values
verbose = false, -- Enable debug logging
-- Storage (Will probably be moved into sub-table)
store_locally = true, -- Should save stats to file
sync_interval = 30, -- Interval for saving to file/send to endpoint
sync_endpoint = nil, -- Optional web sync endpoint
-- Enable or disable specific tracking options
tracking = {
motions = true,
modes = true,
},
-- Realtime
realtime = {
enabled = false, -- What it says on the tin
sync_endpoint = nil, -- Realtime sync endpoint
sync_delay = 200, -- How often to flush&&send buffer in ms
max_buffer_size = 10, -- Force flush&&send buffer if this big
},
}
Local data will be stored within vim.fn.stdpath 'data'
in the stalker
directory.
Session stats and the total of all session stats will be
stored in the json
format.
To set up realtime data syncing, structure config like this:
require('stalker').setup {
realtime = {
enabled = true,
sync_endpoint = 'http://localhost:8000/live',
headers = {
Authorization = 'Secret-Token',
},
},
}
@app.post("/live")
async def receive_live(request: Request):
# Read the raw body data and decode to string
raw_data = await request.body()
data_str = raw_data.decode("utf-8", errors="replace").strip()
# Split the events by delim (newline)
events = data_str.split("\n") if data_str else []
print("\n=== Stalker Update ===")
print(f"Received {len(events)} ({len(raw_data)} bytes) event(s):")
for event in events:
print(f"Event: {event}")
print("======================\n")
return {"status": "ok"}
Note to self: Add config to pass additional headers, for auth etc.
To set up periodic data syncing, structure config like this:
require('stalker').setup {
sync_endpoint = 'WEB_ENDPOINT'
}
@app.post("/stalker")
async def receive_stats(request: Request):
data = await request.json()
timestamp = datetime.fromtimestamp(data["timestamp"])
print("\n=== Stalker Update ===")
print(f"Time: {timestamp}")
print(f"Event: {data['event_type']}")
print("Stats:")
print(json.dumps(data["stats"], indent=2))
print("===================\n")
return {"status": "ok"}
- feat: Add tracking for all modes
- feat: Add config for custom tracking
- feat: Add optional command tracking
- This could expose data, so:
- Make it off by default
- Track only base command?
- This could expose data, so:
- feat: Add event types, and send those as well
- Change event: Mode changes (Instead of sending n_to_i)
- Motion event: Motion keys (Currently only event)
- BufEnter event: When entering a buffer, send filetype?
- VimEnter/Session start event (just send on plugin init)
- VimLeave/Session end event (I already autocmd this)
- perf: Double check the perf of event buffer
- I feel like just posting to an endpoint over and over isn't... great.
- If event buffer feels nice to use, we can use that for persitent data too, rather than a periodic timer. So everything registers as an event and then we match over config to decide if to write to file, send realtime data or big sync to endpoint.
- refactor: Update file structure of repo
MIT