Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 51 additions & 28 deletions keybind-cheatsheet/panel.luau
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ local refreshing = false
local render
local refresh

local env = getfenv()
local dynamicCallbacks = {}
local callbackIndex = 0

local function tr(key, values)
return noctalia.tr(key, values)
end
Expand All @@ -56,6 +60,22 @@ local function startsWith(value, prefix)
return value:sub(1, #prefix) == prefix
end

local function resetCallbacks()
for _, name in ipairs(dynamicCallbacks) do
env[name] = nil
end
dynamicCallbacks = {}
callbackIndex = 0
end

local function callback(prefix, fn)
callbackIndex += 1
local name = "keybindCheatsheet_" .. prefix .. "_" .. callbackIndex
env[name] = fn
table.insert(dynamicCallbacks, name)
return name
end

local KEY_LABELS = {
XF86AudioRaiseVolume = "Vol Up",
XF86AudioLowerVolume = "Vol Down",
Expand Down Expand Up @@ -546,23 +566,23 @@ local function bindingRow(binding, occurrence)
local description = effectiveDescription(binding)
local identity = binding.id .. "#" .. occurrence
if view == "edit" and editingId == identity then
local visibilityChanged = function()
local visibilityChanged = callback("visibility", function()
if hidden then
preferences.hidden[binding.id] = nil
else
preferences.hidden[binding.id] = true
end
savePreferences()
render()
end
local inputChanged = function(value) editDraft = value end
local submit = function(value) saveCustomDescription(binding, value) end
local save = function() saveCustomDescription(binding, editDraft) end
local cancel = function()
end)
local inputChanged = callback("editChange", function(value) editDraft = value end)
local submit = callback("editSubmit", function(value) saveCustomDescription(binding, value) end)
local save = callback("editSave", function() saveCustomDescription(binding, editDraft) end)
local cancel = callback("editCancel", function()
editingId = nil
editDraft = ""
render()
end
end)
return ui.row({ key = "edit-" .. identity, gap = 6, align = "center", paddingV = 0 }, {
ui.row({ minWidth = 188, gap = 3, align = "center", opacity = contentOpacity }, pills),
ui.input({ key = "description-" .. identity, value = editDraft, placeholder = tr("edit_description"), focus = true, controlSize = "sm", flexGrow = 1, onChange = inputChanged, onSubmit = submit }),
Expand All @@ -589,20 +609,20 @@ local function bindingRow(binding, occurrence)
ui.column({ flexGrow = 1, gap = 0, opacity = contentOpacity }, labels),
}
if view == "edit" then
local edit = function()
local edit = callback("edit", function()
editingId = identity
editDraft = description
render()
end
local visibilityChanged = function()
end)
local visibilityChanged = callback("visibility", function()
if hidden then
preferences.hidden[binding.id] = nil
else
preferences.hidden[binding.id] = true
end
savePreferences()
render()
end
end)
table.insert(row, ui.button({ glyph = "pencil", width = 22, height = 22, glyphSize = 12, variant = "ghost", controlSize = "sm", tooltip = tr("edit_description"), onClick = edit }))
table.insert(row, ui.button({ glyph = hidden and "eye-off" or "eye", width = 22, height = 22, glyphSize = 13, variant = "ghost", controlSize = "sm", selected = hidden, tooltip = hidden and tr("show_binding") or tr("hide_binding"), onClick = visibilityChanged }))
end
Expand Down Expand Up @@ -647,16 +667,16 @@ local function hiddenCount()
end

local function colorControl(bucket, property)
local choose = function() chooseColor(bucket.id, property) end
local paste = function() pasteColor(bucket.id, property) end
local reset = function()
local choose = callback("chooseColor", function() chooseColor(bucket.id, property) end)
local paste = callback("pasteColor", function() pasteColor(bucket.id, property) end)
local reset = callback("resetColor", function()
if preferences.colors[bucket.id] ~= nil then
preferences.colors[bucket.id][property] = nil
if next(preferences.colors[bucket.id]) == nil then preferences.colors[bucket.id] = nil end
savePreferences()
render()
end
end
end)
return ui.row({ gap = 5, align = "center", flexGrow = 1 }, {
ui.box({ width = 24, height = 24, radius = 5, fill = colorValue(bucket.id, property), border = "outline", borderWidth = 1 }),
ui.button({ text = property == "background" and tr("background") or tr("text"), variant = "ghost", controlSize = "sm", flexGrow = 1, onClick = choose }),
Expand All @@ -678,11 +698,11 @@ local function appearanceBody()
}),
}))
end
local resetAll = function()
local resetAll = callback("resetAllColors", function()
preferences.colors = {}
savePreferences()
render()
end
end)
table.insert(rows, ui.separator({ spacing = 6 }))
table.insert(rows, ui.row({ gap = 8, align = "center" }, {
ui.button({ glyph = "restore", text = tr("reset_colors"), onClick = resetAll }),
Expand All @@ -691,19 +711,19 @@ local function appearanceBody()
end

local function renderHeader()
local refreshClick = function() refresh() end
local editMode = function()
local refreshClick = callback("refresh", function() refresh() end)
local editMode = callback("editMode", function()
view = view == "edit" and "bindings" or "edit"
editingId = nil
editDraft = ""
render()
end
local appearance = function()
end)
local appearance = callback("appearance", function()
view = view == "appearance" and "bindings" or "appearance"
editingId = nil
editDraft = ""
render()
end
end)
local title = tr("title")
if currentCompositor ~= "" then
title = (currentCompositor == "mango" and "Mango" or (currentCompositor == "niri" and "Niri" or "Hyprland")) .. " Keymap"
Expand All @@ -715,16 +735,16 @@ local function renderHeader()
}),
}
if (view == "bindings" or view == "edit") and not loading and panelError == nil then
local searchChanged = function(value)
local searchChanged = callback("search", function(value)
query = value
editingId = nil
render()
end
local clearSearch = function()
end)
local clearSearch = callback("clearSearch", function()
query = ""
searchRevision += 1
render()
end
end)
table.insert(children, ui.input({ key = "search-" .. searchRevision, value = query, placeholder = tr("search_placeholder"), controlSize = "sm", width = 300, onChange = searchChanged }))
table.insert(children, ui.button({ glyph = "x", width = 26, variant = "ghost", controlSize = "sm", enabled = query ~= "", tooltip = tr("clear"), onClick = clearSearch }))
end
Expand All @@ -733,11 +753,11 @@ local function renderHeader()
end
if view == "edit" then
local count = hiddenCount()
local restoreHidden = function()
local restoreHidden = callback("restoreHidden", function()
restoreHiddenBindings(bindings, preferences.hidden)
savePreferences()
render()
end
end)
table.insert(children, ui.label({ text = tr("hidden_count", { count = count }), color = "on_surface_variant", fontSize = 12 }))
table.insert(children, ui.button({ glyph = "eye", variant = "ghost", controlSize = "sm", enabled = count > 0, tooltip = tr("restore_hidden"), onClick = restoreHidden }))
end
Expand All @@ -749,6 +769,7 @@ end

render = function()
if not panelOpen then return end
resetCallbacks()
local contentState = "bindings"
if view == "appearance" then
contentState = "appearance"
Expand Down Expand Up @@ -795,6 +816,7 @@ local function releasePanelState(clearModel)
query = ""
editingId = nil
editDraft = ""
resetCallbacks()
if clearModel then
bindings = {}
parseWarnings = {}
Expand Down Expand Up @@ -849,6 +871,7 @@ local function lifecycleState()
refreshing = refreshing,
bindingCount = #bindings,
snapshotLoaded = snapshot ~= nil,
callbackCount = #dynamicCallbacks,
editDraft = editDraft,
}
end
Expand Down
2 changes: 1 addition & 1 deletion keybind-cheatsheet/plugin.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
id = "kenn/keybind-cheatsheet"
name = "Keybind Cheatsheet"
version = "0.2.1"
version = "0.2.2"
plugin_api = 9
author = "kenn"
license = "MIT"
Expand Down
70 changes: 62 additions & 8 deletions keybind-cheatsheet/service.luau
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ end
local function parseNiriContent(content, sourceFile, context)
local includes = {}
for rawLine in (content .. "\n"):gmatch("(.-)\r?\n") do
local includePath = rawLine:match('^%s*include%s+"([^"]+)"')
local includePath = rawLine:match('^%s*include%s+["\']([^"\']+)["\']')
if includePath ~= nil then
table.insert(includes, { path = includePath, optional = false })
end
Expand Down Expand Up @@ -777,9 +777,42 @@ local function readConfig(rootPath, parser)
return context
end

local function extractLuaStringLiterals(expr)
local literals = {}
local index = 1
while index <= #expr do
local char = expr:sub(index, index)
if char == '"' or char == "'" then
local quote = char
index += 1
local value = {}
local escaped = false
while index <= #expr do
local c = expr:sub(index, index)
if escaped then
table.insert(value, c)
escaped = false
elseif c == "\\" then
escaped = true
elseif c == quote then
break
else
table.insert(value, c)
end
index += 1
end
table.insert(literals, table.concat(value))
end
index += 1
end
return literals
end

local function scanHyprLuaContent(content, sourceFile, context)
local category = ""
local includes = {}
local pendingDescription = false

for rawLine in (content .. "\n"):gmatch("(.-)\r?\n") do
local heading = rawLine:match("^%s*%-%-%s*%d+%.%s*(.-)%s*$")
if heading ~= nil and heading ~= "" then
Expand All @@ -796,13 +829,34 @@ local function scanHyprLuaContent(content, sourceFile, context)
table.insert(includes, { path = pathJoin(context.luaRoot, modulePath), optional = false })
end

local description = code:match('description%s*=%s*"([^"]*)"')
or code:match("description%s*=%s*'([^']*)'")
or code:match('desc%s*=%s*"([^"]*)"')
or code:match("desc%s*=%s*'([^']*)'")
if description ~= nil and description ~= "" then
local kind = code:find("%.%.") ~= nil and "prefix" or "exact"
table.insert(context.rules, { kind = kind, value = description, category = category ~= "" and category or "Other" })
local rhs = code:match('description%s*=%s*(.*)$') or code:match('desc%s*=%s*(.*)$')
if rhs ~= nil or pendingDescription then
local expr = rhs or code
local literals = extractLuaStringLiterals(expr)
local nonEmpty = {}
for _, str in ipairs(literals) do
local cleaned = trim(str)
if cleaned ~= "" then
table.insert(nonEmpty, cleaned)
end
end

if #nonEmpty > 0 then
pendingDescription = false
local combined = table.concat(nonEmpty)
local hasConcat = code:find("%.%.", 1, true) ~= nil or (rhs ~= nil and rhs:find("%.%.", 1, true) ~= nil)
local kind = hasConcat and "prefix" or "exact"
local cat = category ~= "" and category or "Other"

table.insert(context.rules, { kind = kind, value = combined, category = cat })
for _, piece in ipairs(nonEmpty) do
if piece ~= combined then
table.insert(context.rules, { kind = "exact", value = piece, category = cat })
end
end
elseif rhs ~= nil and #literals == 0 then
pendingDescription = true
end
end
end
return includes
Expand Down
Loading
Loading