keybind-cheatsheet 0.2.5: fix the panel never loading (and dying on the first click) on mid-size configs - #534
Open
UmedjonBA wants to merge 1 commit into
Conversation
…CPU budget) On a ~800-line niri config (130 bindings) the plugin never leaves "Reading keybindings...": the Luau runtime kills every callback that exceeds its CPU budget, and both the config parse and the panel render run past it. Interacting with an already-drawn panel (search, refresh, edit mode) failed the same way, which also left internal state stuck. Service: - Parse niri configs with a small sh/sed/awk pipeline instead of the in-process tokenizer: tokenizing a 33 KB config alone measured 31.6 ms. Chunking it with a coroutine does not help - the budget accumulates over the coroutine's whole lifetime (verified: 3-op slices still died, at slice 2194). The Lua parsers are untouched and still cover the self-test fixtures; the awk output matches tests/fixtures/niri exactly. - Convert the emitted rows into bindings a few per update() tick, since an async callback's budget is smaller still. - Faster tokenizer paths (whole strings, whitespace runs and words in one match each) for the parsers that remain in-process. Panel: - Cache each binding's search haystack, resolved category and built row node; a full 130-row redraw drops from ~22 ms to ~6-7 ms. - Render progressively: at most 24 rows per pass, the rest fills in over a chain of async callbacks, and every render outside that chain restarts from a small chunk, so a click never pays for the whole tree. - render() only schedules; the draw happens in its own callback. - Split onOpen (preferences JSON + snapshot + draw) into async steps. - Drop the sticky in-progress flags: a callback killed mid-render used to leave them set, and the panel stopped redrawing entirely until restart. Version 0.2.5; declares the awk/sed/sh the niri path now uses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
18 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plugin
kenn/keybind-cheatsheetplugin.toml)What it does
Fixes the plugin being unusable on a mid-size niri config. On my ~800-line
config.kdl(130 bindings) the panel never left "Reading keybindings…", and once a cached list did appear, the first click on Refresh / edit mode / the palette produced an error and the panel stopped responding entirely.Everything traces back to the Luau CPU budget (~25 ms per callback), and three findings that shaped the fix — happy to be corrected if any of them is wrong on your setup:
Service
sh/sed/awkpipeline that emits one line per binding; the Lua side only converts those rows, a few perupdate()tick (an async callback's budget is smaller still). Include directives, globs, multi-line binds,hotkey-overlay-titleand// #"Category"headers are all handled; the output matchestests/fixtures/niriandtests/expected.jsonexactly.parseNiriContentand the other in-process parsers are left in place (still exported, still covered by the self-test, which passes on all four fixtures) — the shell path is only used for the live niri refresh.find/matcheach instead of a per-character loop) for the parsers that remain in-process.Panel
render()only schedules — the draw itself runs in its own callback, so a handler's budget is not spent on it.onOpenno longer does preferences JSON + snapshot + draw in one go.Result on the same config: panel opens immediately, fills in within ~0.3 s, search and buttons stay responsive, and the log shows zero budget errors across repeated opens, refreshes and typing (previously one on nearly every action).
External dependencies
awk,sed,sh(added todependencies, documented in Requirements) — base tools on any system Noctalia runs on, used only for the niri config read.hyprctlunchanged. No new network access, no files written beyond the existing cache/preferences.Testing
Real config (~800 lines, 130 bindings, includes): panel open/close repeatedly, refresh via IPC and the header button, search typing, edit mode, colors view — no budget errors. The plugin's own self-test (
noctalia msg plugin kenn/keybind-cheatsheet:data all self-test) passes:hypr_conf 5/5, hypr_lua 4/4, mango 9/9, niri 5/5. The awk parser was diffed againsttests/fixtures/niriand returns the same five bindings with the same descriptions and categories. Render costs were measured with temporary instrumentation (removed before this PR), which is where the numbers above come from.Hyprland/Mango paths are untouched apart from the tokenizer speedups, so they would benefit from a second pair of eyes on hardware I don't have.
Screenshots / Videos
The change is not visual — same panel, same layout. For reference, the fully populated panel on the config that used to hang (the descriptions are user-supplied via the plugin's own edit mode, hence Russian):
Checklist
Ready-for-review requirement: Every box in this section must be checked. If any statement is not true, keep the
pull request as Draft. An explanation does not replace a required check.
idafter the/inplugin.tomlexactly.plugin.toml,README.md,thumbnail.webp, andtranslations/en.json.README.mdfollows theREADME template, documents
every entry id and dependency, and includes exact panel IPC commands and launcher prefixes where applicable.
thumbnail.webpwith the thumbnail generator.versionfollows semver and is bumped in this PR;plugin_apiis the oldest API level this plugin requires.understand that language well enough to review and maintain it (no unreviewed machine/LLM translations).
catalog.toml; CI generates it.Code review attestation
Plugins run as trusted, unsandboxed Luau in the user's session. Confirm:
Ready-for-review requirement: Every attestation below must be checked.
licensedeclared inplugin.toml.@kenn — this is your plugin, so it needs your call. The thumbnail and translations are untouched; if you would rather keep the whole parse in Luau, or split this into "service fix" and "panel fix" PRs, I am glad to rework it.