| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- -- rainbow-delimiters.lua
- -- ============================================================================
- -- Colorize nested delimiters (parentheses, brackets, braces) with different
- -- colors based on nesting depth. Similar to Emacs rainbow-delimiters.
- -- ============================================================================
- local rainbow = {}
- -- Configuration
- rainbow.config = {
- enabled = true,
- max_depth = 9, -- Number of colors to cycle through
- delimiters = {
- ["("] = ")",
- ["["] = "]",
- ["{"] = "}",
- },
- }
- -- Default rainbow colors (can be customized per theme)
- rainbow.colors = {
- "#ff6b6b", -- Red
- "#ffa94d", -- Orange
- "#ffd43b", -- Yellow
- "#69db7c", -- Green
- "#38d9a9", -- Teal
- "#4dabf7", -- Blue
- "#748ffc", -- Indigo
- "#da77f2", -- Violet
- "#f783ac", -- Pink
- }
- -- Alternative color schemes
- rainbow.presets = {
- default = {
- "#ff6b6b", "#ffa94d", "#ffd43b", "#69db7c",
- "#38d9a9", "#4dabf7", "#748ffc", "#da77f2", "#f783ac",
- },
- pastel = {
- "#ffb3ba", "#ffdfba", "#ffffba", "#baffc9",
- "#bae1ff", "#d4baff", "#ffbae1", "#baffff", "#e1ffba",
- },
- neon = {
- "#ff0080", "#ff8000", "#ffff00", "#00ff00",
- "#00ffff", "#0080ff", "#8000ff", "#ff00ff", "#ff0040",
- },
- monochrome = {
- "#ffffff", "#e0e0e0", "#c0c0c0", "#a0a0a0",
- "#808080", "#606060", "#404040", "#303030", "#202020",
- },
- nord = {
- "#bf616a", "#d08770", "#ebcb8b", "#a3be8c",
- "#88c0d0", "#81a1c1", "#5e81ac", "#b48ead", "#bf616a",
- },
- dracula = {
- "#ff5555", "#ffb86c", "#f1fa8c", "#50fa7b",
- "#8be9fd", "#bd93f9", "#ff79c6", "#6272a4", "#ff5555",
- },
- }
- -- State
- rainbow.active = true
- -- Setup faces for each depth level
- function rainbow.setup_faces()
- for i, color in ipairs(rainbow.colors) do
- local face_name = "rainbow-delimiters-depth-" .. i
- -- Use lumacs.face helper which handles hex conversion
- local attrs = lumacs.face({
- foreground = color,
- weight = "bold",
- })
- if editor.theme_manager then
- local theme = editor.theme_manager:active_theme()
- if theme then
- theme:set_face(face_name, attrs)
- end
- end
- end
- end
- -- Get face name for a given depth
- function rainbow.get_face(depth)
- local idx = ((depth - 1) % #rainbow.colors) + 1
- return "rainbow-delimiters-depth-" .. idx
- end
- -- Check if char is an opener
- function rainbow.is_opener(char)
- return rainbow.config.delimiters[char] ~= nil
- end
- -- Check if char is a closer
- function rainbow.is_closer(char)
- for opener, closer in pairs(rainbow.config.delimiters) do
- if closer == char then
- return true, opener
- end
- end
- return false, nil
- end
- -- Highlight delimiters in the current buffer
- function rainbow.highlight_buffer()
- if not rainbow.active or not rainbow.config.enabled then
- return
- end
- local buf = editor.buffer
- local line_count = buf:line_count()
- local depth = 0
- local delimiter_positions = {}
- -- First pass: find all delimiters and calculate their depths
- for line_num = 0, line_count - 1 do
- local line = buf:line(line_num)
- local in_string = false
- local string_char = nil
- local escaped = false
- for col = 0, #line - 1 do
- local char = line:sub(col + 1, col + 1)
- -- Handle escape sequences
- if escaped then
- escaped = false
- elseif char == "\\" then
- escaped = true
- -- Handle strings
- elseif not in_string and (char == '"' or char == "'") then
- in_string = true
- string_char = char
- elseif in_string and char == string_char then
- in_string = false
- string_char = nil
- -- Handle delimiters (outside strings)
- elseif not in_string then
- if rainbow.is_opener(char) then
- depth = depth + 1
- table.insert(delimiter_positions, {
- line = line_num,
- col = col,
- depth = depth,
- char = char,
- })
- elseif rainbow.is_closer(char) then
- table.insert(delimiter_positions, {
- line = line_num,
- col = col,
- depth = depth,
- char = char,
- })
- depth = math.max(0, depth - 1)
- end
- end
- end
- end
- -- Second pass: apply styles
- for _, pos in ipairs(delimiter_positions) do
- local face = rainbow.get_face(pos.depth)
- local range = lumacs.Range(
- lumacs.Position(pos.line, pos.col),
- lumacs.Position(pos.line, pos.col + 1)
- )
- local attr = lumacs.TextAttribute(face)
- buf:set_style(range, attr)
- end
- end
- -- Toggle rainbow delimiters
- function rainbow.toggle()
- rainbow.active = not rainbow.active
- if rainbow.active then
- rainbow.highlight_buffer()
- editor:message("Rainbow delimiters enabled")
- else
- -- Clear rainbow styles
- editor.buffer:clear_styles()
- editor:message("Rainbow delimiters disabled")
- end
- end
- -- Set color preset
- function rainbow.set_preset(name)
- local preset = rainbow.presets[name]
- if not preset then
- editor:message("Unknown preset: " .. name, "warning")
- return
- end
- rainbow.colors = preset
- rainbow.setup_faces()
- if rainbow.active then
- rainbow.highlight_buffer()
- end
- editor:message("Rainbow preset: " .. name)
- end
- -- Register commands
- editor:register_command("rainbow-delimiters-mode", "Toggle rainbow delimiter coloring", function(args)
- rainbow.toggle()
- return {success = true}
- end, {"rainbow"})
- editor:register_command("rainbow-preset", "Set rainbow delimiter color preset", function(args)
- if #args == 0 then
- local presets = {}
- for name, _ in pairs(rainbow.presets) do
- table.insert(presets, name)
- end
- table.sort(presets)
- return {success = false, message = "Available presets: " .. table.concat(presets, ", ")}
- end
- rainbow.set_preset(args[1])
- return {success = true}
- end, {}, true, "s")
- editor:register_command("rainbow-highlight", "Re-apply rainbow highlighting to current buffer", function(args)
- rainbow.highlight_buffer()
- return {success = true, message = "Rainbow highlighting applied"}
- end)
- -- Define minor mode
- lumacs.define_minor_mode("rainbow-delimiters-mode", {
- lighter = "Rainbow",
- global = false,
- setup = function()
- rainbow.active = true
- rainbow.setup_faces()
- rainbow.highlight_buffer()
- editor:message("Rainbow delimiters enabled")
- end,
- cleanup = function()
- rainbow.active = false
- editor.buffer:clear_styles()
- editor:message("Rainbow delimiters disabled")
- end
- })
- -- Initialize faces
- rainbow.setup_faces()
- -- Store in lumacs namespace
- lumacs.rainbow_delimiters = rainbow
- print("[rainbow-delimiters] Package loaded")
- return rainbow
|