122 lines
4 KiB
Lua
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
|