;;; highlight-symbol.el --- Highlight symbol at point -*- lexical-binding: t; -*-

(defface highlight-symbol-face
  '((t (:background "brightyellow" :foreground "black")))
  "Face used to highlight symbols matching the one at point."
  :group 'highlight-symbol)

(defvar-local highlight-symbol--regexp nil
  "Regexp matching the currently highlighted symbol.")

(defvar highlight-symbol--timer nil
  "Idle timer for automatic highlighting.")

(defcustom highlight-symbol-idle-delay 0.5
  "Seconds of idle time before highlighting the symbol at point."
  :type 'number
  :group 'highlight-symbol)

(defcustom highlight-symbol-excluded-modes
  '(vterm-mode dired-mode vc-dir-mode log-view-mode vc-annotate-mode diff-mode)
  "List of major modes in which `highlight-symbol-mode' should not be enabled."
  :type '(repeat symbol)
  :group 'highlight-symbol)

(defun highlight-symbol-unhighlight ()
  "Remove the current symbol highlighting."
  (interactive)
  (when highlight-symbol--regexp
    (unhighlight-regexp highlight-symbol--regexp)
    (setq highlight-symbol--regexp nil)))

(defun highlight-symbol-highlight ()
  "Highlight all occurrences of the symbol at point."
  (interactive)
  (highlight-symbol-unhighlight)
  (let ((symbol (thing-at-point 'symbol)))
    (when symbol
      (setq highlight-symbol--regexp (concat "\\_<" (regexp-quote symbol) "\\_>"))
      (highlight-regexp highlight-symbol--regexp 'highlight-symbol-face))))

(defun highlight-symbol--idle-highlight ()
  "Highlight symbol at point if `highlight-symbol-mode' is active."
  (when highlight-symbol-mode
    (highlight-symbol-highlight)))

(define-minor-mode highlight-symbol-mode
  "Minor mode to automatically highlight the symbol at point after idle time."
  :lighter " hl-sym"
  (unless highlight-symbol-mode
    (highlight-symbol-unhighlight)))

(defun global-highlight-symbol-mode--enable ()
  "Start the idle timer for highlight-symbol."
  (unless highlight-symbol--timer
    (setq highlight-symbol--timer
          (run-with-idle-timer highlight-symbol-idle-delay t
                               #'highlight-symbol--idle-highlight))))

(defun global-highlight-symbol-mode--disable ()
  "Stop the idle timer for highlight-symbol."
  (when highlight-symbol--timer
    (cancel-timer highlight-symbol--timer)
    (setq highlight-symbol--timer nil)))

(define-globalized-minor-mode global-highlight-symbol-mode
  highlight-symbol-mode
  (lambda ()
    (unless (or (minibufferp)
                (apply #'derived-mode-p highlight-symbol-excluded-modes))
      (highlight-symbol-mode 1)))
  :after-hook
  (if global-highlight-symbol-mode
      (global-highlight-symbol-mode--enable)
    (global-highlight-symbol-mode--disable)))

(provide 'highlight-symbol)

;;; highlight-symbol.el ends here
