-- Lua Mode for Lumacs -- Extracted from init.lua -- Define highlighting function locally so it can be used in setup local function highlight_lua() local buf = editor.buffer buf:clear_styles() -- Keywords to highlight local keywords = { "function", "local", "end", "if", "then", "else", "elseif", "for", "while", "do", "return", "break", "and", "or", "not", "true", "false", "nil", "in", "repeat", "until" } -- Highlight each line for line_num = 0, buf:line_count() - 1 do local line_text = buf:line(line_num) local len = #line_text local pos = 1 -- Parse line to handle priority (Comments > Strings > Keywords) while pos <= len do -- Find nearest tokens local comment_start = string.find(line_text, "--", pos, true) local string_dq_start = string.find(line_text, '"', pos, true) local string_sq_start = string.find(line_text, "'", pos, true) -- Determine the first token local mode = "code" local token_start = len + 2 -- Sentinel if comment_start and comment_start < token_start then token_start = comment_start mode = "comment" end if string_dq_start and string_dq_start < token_start then token_start = string_dq_start mode = "string_dq" end if string_sq_start and string_sq_start < token_start then token_start = string_sq_start mode = "string_sq" end -- 1. Process code/keywords BEFORE the token local code_end = token_start - 1 if code_end >= pos then local code_chunk = string.sub(line_text, pos, code_end) for _, keyword in ipairs(keywords) do local s = 1 while true do -- Match whole words only local p_start, p_end = string.find(code_chunk, "%f[%w]" .. keyword .. "%f[%W]", s) if not p_start then break end local abs_start = pos + p_start - 1 local abs_end = pos + p_end - 1 local range = lumacs.Range( lumacs.Position(line_num, abs_start - 1), lumacs.Position(line_num, abs_end) ) buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Keyword, 0)) s = p_end + 1 end end end -- 2. Process the token if mode == "comment" then -- Comment goes to end of line local range = lumacs.Range( lumacs.Position(line_num, token_start - 1), lumacs.Position(line_num, len) ) buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.Comment, 0)) break -- Done with this line elseif mode == "string_dq" then local _, s_end = string.find(line_text, '"', token_start + 1, true) if not s_end then s_end = len end local range = lumacs.Range( lumacs.Position(line_num, token_start - 1), lumacs.Position(line_num, s_end) ) buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.String, 0)) pos = s_end + 1 elseif mode == "string_sq" then local _, s_end = string.find(line_text, "'", token_start + 1, true) if not s_end then s_end = len end local range = lumacs.Range( lumacs.Position(line_num, token_start - 1), lumacs.Position(line_num, s_end) ) buf:set_style(range, lumacs.TextAttribute(lumacs.ColorType.String, 0)) pos = s_end + 1 else -- No more tokens pos = len + 1 end end end end define_major_mode("lua-mode", { file_patterns = {"%.lua$"}, comment_syntax = "--", highlight = highlight_lua, setup = function() editor:message("[lua-mode] Lua mode activated") -- Register a hook to re-highlight on change -- Note: This adds a listener every time the mode is setup. -- Ideally, we'd track and remove it, but for now this ensures responsiveness. editor.buffer:on_buffer_event(function(data) if data.event == lumacs.BufferEvent.AfterChange then -- Only run if we are still in lua-mode (simple check) -- Using a protected call in case global state changes local status, mode_name = pcall(current_major_mode) if status and mode_name == "lua-mode" then highlight_lua() end end end) end, cleanup = function() editor:message("[lua-mode] Lua mode deactivated") end, keybindings = { -- Fix: Explicitly bind space since empty keybindings table might be causing issues [" "] = function() editor:execute_command("self-insert-command", {" "}) end, } })