mtmenu/syntax.lua
2026-02-10 21:46:25 -05:00

122 lines
4 KiB
Lua

local keywords = {
"and", "break", "do", "else", "elseif", "end", --[["false",]] "for", "function",
"goto", "if", "in", --[["local", "nil",]] "not", "or", "repeat", "return",
"then", --[["true",]] "until", "while"
}
local keyword_set = {}
for _, kw in ipairs(keywords) do
keyword_set[kw] = true
end
local keyword_value_set = {
["true"] = true,
["false"] = true,
["nil"] = true,
["local"] = true,
["math.huge"] = true
}
local function colorize(str, color)
return "<style color="..(theme.styles["syntax_"..color] or "#fff")..">"..str.."</style>"
end
function syntax_highlight(code)
local out = ""
local pos = 1
while pos <= #code do
local matched = false
-- Whitespace
local ws_start, ws_end = code:find("^%s+", pos)
if ws_start then
out = out..colorize(code:sub(ws_start, ws_end), "whitespace")
pos = ws_end + 1
matched = true
end
-- Single-line comment
if not matched then
local com_start, com_end = code:find("^%-%-[^\n]*", pos)
if com_start then
out = out..colorize(code:sub(com_start, com_end), "comment")
pos = com_end + 1
matched = true
end
end
-- String (double-quoted, simple escape handling)
if not matched then
local str_start, str_end = code:find('^"[^"]*"', pos)
if str_start then
out = out..colorize(code:sub(str_start, str_end), "string")
pos = str_end + 1
matched = true
end
end
-- String (single-quoted, similar)
if not matched then
local str_start, str_end = code:find("^'[^']*", pos)
if str_start then
out = out..colorize(code:sub(str_start, str_end), "string")
pos = str_end + 1
matched = true
end
end
-- Documentation placeholders. These aren't in Lua, but that means they
-- can be highlighted without worrying about confusing alternative meanings.
if not matched then
local str_start, str_end = code:find('^\\?<[^>]*>', pos)
if str_start then
out = out..colorize(code:sub(str_start, str_end), "placeholder")
pos = str_end + 1
matched = true
end
end
-- Number (basic integer/float)
if not matched then
local num_start, num_end = code:find("^%d+%.?%d*[eE]?[+-]?%d*", pos)
if num_start then
out = out..colorize(code:sub(num_start, num_end), "number")
pos = num_end + 1
matched = true
end
end
-- Identifier/Keyword
if not matched then
local id_start, id_end, id_text = code:find("^([%a_][%w_]*)", pos)
if id_start then
if keyword_set[id_text] then
out = out..colorize(id_text, "keyword")
elseif keyword_value_set[id_text] then
out = out..colorize(id_text, "keyword_value")
else
out = out..colorize(id_text, "identifier")
end
pos = id_end + 1
matched = true
end
end
-- Operators/Punctuation (catch-all for non-alphanumeric)
if not matched then
local op_start, op_end = code:find("^[^%s%a%d_\"'{}(),%][]", pos)
if op_start then
local op = code:sub(op_start, op_end)
out = out..colorize(op, op == "-" and code:sub(op_end +1, op_end +2):find "^%d" and "number" or "operator")
pos = op_end + 1
matched = true
end
end
-- If nothing matched, advance to avoid infinite loop (e.g., invalid char)
if not matched then
out = out..colorize(code:sub(pos, pos), "generic")
pos = pos + 1
end
end
return out
end