;;; +kmacro.el -*- lexical-binding: t; -*-

;;; Commentary:

;; Many of these come from this Reddit thread:
;; https://old.reddit.com/r/emacs/comments/rlli0u/whats_your_favorite_defadvice/

;;; Code:

(require 'kmacro)

;; Indicate when a kmacro is being recorded in the mode-line

(defface +kmacro-modeline nil
  "Face when kmacro is active")

(set-face-attribute '+kmacro-modeline nil
                    :background "Firebrick"
                    :box '(:line-width -1 :color "salmon"
                                       :style released-button))

(defun +kmacro-change-mode-line (&rest _)
  "Remap the mode-line face when recording a kmacro."

  (add-to-list 'face-remapping-alist '(mode-line . +kmacro-modeline)))

(defun +kmacro-restore-mode-line (&rest _)
  "Restore the mode-line face after kmacro is done recording."
  (setf face-remapping-alist
        (assoc-delete-all 'mode-line face-remapping-alist)))

(define-minor-mode +kmacro-recording-indicator-mode
  "Change the mode-line's face when recording a kmacro."
  :lighter ""
  :global t
  (if +kmacro-recording-indicator-mode
      (progn
        (advice-add #'kmacro-start-macro :before #'+kmacro-change-mode-line)
        (advice-add #'kmacro-keyboard-quit :after #'+kmacro-restore-mode-line)
        (advice-add #'kmacro-end-macro :after #'+kmacro-restore-mode-line))
    (+kmacro-restore-mode-line)
    (advice-remove #'kmacro-start-macro #'+kmacro-change-mode-line)
    (advice-remove #'kmacro-keyboard-quit #'+kmacro-restore-mode-line)
    (advice-remove #'kmacro-end-macro #'+kmacro-restore-mode-line)))

;; Undo keyboard macros in a single bound (like vi!)

(defun +kmacro-block-undo (fn &rest args)
  (let ((marker (prepare-change-group)))
    (unwind-protect (apply fn args)
      (undo-amalgamate-change-group marker))))

(define-minor-mode +kmacro-block-undo-mode
  "Undo kmacros all at once (like vi)."
  :global t
  :lighter " KUndo"
  (if +kmacro-block-undo-mode
      (dolist (fn '(kmacro-call-macro
                    kmacro-exec-ring-item
                    dot-mode-execute
                    apply-macro-to-region-lines))
        (advice-add fn :around #'+kmacro-block-undo))
    (dolist (fn '(kmacro-call-macro
                 kmacro-exec-ring-item
                 dot-mode-execute
                 apply-macro-to-region-lines))
        (advice-remove fn #'+kmacro-block-undo))))

(provide '+kmacro)
;;; +kmacro.el ends here
