;;; rainbow-brackets.el --- Colorize nested brackets by depth -*- lexical-binding: t; -*-

(defgroup rainbow-brackets nil
  "Highlight matching brackets with colors based on nesting depth."
  :group 'faces)

(defface rainbow-brackets-depth-1 '((t (:foreground "#e6194b"))) "Depth 1." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-2 '((t (:foreground "#f58231"))) "Depth 2." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-3 '((t (:foreground "#ffe119"))) "Depth 3." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-4 '((t (:foreground "#3cb44b"))) "Depth 4." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-5 '((t (:foreground "#4363d8"))) "Depth 5." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-6 '((t (:foreground "#911eb4"))) "Depth 6." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-7 '((t (:foreground "#42d4f4"))) "Depth 7." :group 'rainbow-brackets)
(defface rainbow-brackets-depth-8 '((t (:foreground "#f032e6"))) "Depth 8." :group 'rainbow-brackets)

(defvar rainbow-brackets--faces
  [rainbow-brackets-depth-1
   rainbow-brackets-depth-2
   rainbow-brackets-depth-3
   rainbow-brackets-depth-4
   rainbow-brackets-depth-5
   rainbow-brackets-depth-6
   rainbow-brackets-depth-7
   rainbow-brackets-depth-8]
  "Vector of faces used for successive nesting depths.")

(defvar-local rainbow-brackets--match-face nil
  "Face to apply to the current match, set by the matcher function.")

(defun rainbow-brackets--depth-face (depth)
  "Return the face for nesting DEPTH (0-indexed)."
  (aref rainbow-brackets--faces (mod depth (length rainbow-brackets--faces))))

(defun rainbow-brackets--match (limit)
  "Search for the next bracket before LIMIT and set match data.
Returns non-nil if a bracket is found, setting the face in
`rainbow-brackets--match-face' for use by font-lock.
Calls `syntax-ppss' once at the start of the region to seed depth,
then tracks depth manually to avoid per-bracket overhead."
  (let* ((ppss (syntax-ppss (point)))
         (depth (nth 0 ppss))
         (in-str (nth 3 ppss))
         (comment-depth (nth 4 ppss)))
    (catch 'found
      (while (re-search-forward "[][(){}\"';#]\\|\\b\\(//\\|/\\*\\|\\*/\\)" limit t)
        (let* ((pos (match-beginning 0))
               (char (char-after pos))
               (syn (char-syntax char)))
          (cond
           ;; Track string delimiters
           ((eq syn ?\")
            (setq in-str (if in-str nil char)))
           ;; Skip everything inside strings
           (in-str nil)
           ;; Track line comments (;, //)
           ((or (eq char ?\;)
                (and (eq char ?/) (eq (char-after (1+ pos)) ?/)))
            (goto-char (line-end-position)))
           ;; Track block comment start
           ((and (eq char ?/) (eq (char-after (1+ pos)) ?*))
            (setq comment-depth t)
            (forward-char))
           ;; Track block comment end
           ((and (eq char ?*) (eq (char-after (1+ pos)) ?/))
            (setq comment-depth nil)
            (forward-char))
           ;; Skip brackets inside comments
           (comment-depth nil)
           ;; Opening bracket
           ((memq syn '(?\( ?<))
            (setq rainbow-brackets--match-face
                  (rainbow-brackets--depth-face depth))
            (setq depth (1+ depth))
            (throw 'found t))
           ;; Closing bracket
           ((memq syn '(?\) ?>))
            (setq depth (max 0 (1- depth)))
            (setq rainbow-brackets--match-face
                  (rainbow-brackets--depth-face depth))
            (throw 'found t)))))
      nil)))

(defvar rainbow-brackets--font-lock-keywords
  '((rainbow-brackets--match 0 rainbow-brackets--match-face prepend))
  "Font-lock keywords for rainbow brackets.")

;;;###autoload
(define-minor-mode rainbow-brackets-mode
  "Colorize brackets by nesting depth."
  :lighter " RB"
  (if rainbow-brackets-mode
      (progn
        (font-lock-add-keywords nil rainbow-brackets--font-lock-keywords 'append)
        (when font-lock-mode (font-lock-flush)))
    (font-lock-remove-keywords nil rainbow-brackets--font-lock-keywords)
    (when font-lock-mode (font-lock-flush))))

(provide 'rainbow-brackets)
;;; rainbow-brackets.el ends here
