From 4904553ae7d809286ed8993b17e37d3ec34e75d9 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 6 Sep 2025 23:03:27 -0500 Subject: refactor(keyboard-macros): Improve structure and error handling Reorganized the module to improve code structure and readability: - Removed duplicate definition of ensure-macros-file - Added explicit file creation before saving macros - Added proper error handling when loading macros file - Added function to save unnamed macros on exit - Grouped keybindings together - Cleaned up documentation formatting --- modules/keyboard-macros.el | 69 +++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/modules/keyboard-macros.el b/modules/keyboard-macros.el index cff25b39..a7605cfd 100644 --- a/modules/keyboard-macros.el +++ b/modules/keyboard-macros.el @@ -8,21 +8,20 @@ ;; All commands are built on top of the built-in =kmacro= machinery, but ;; add a lightweight workflow and persistence across sessions. ;; -;; User Workflow: +;; Workflow: ;; ;; 1. Start recording with C-F3 (or M-x cj/kbd-macro-start-or-end) ;; This toggles macro recording on. ;; Now you can perform all the edits you want recorded in the macro. ;; ;; 2. Stop recording with C-F3 (or M-x cj/kbd-macro-start-or-end) -;; This stops recording and the macro becomes the “last keyboard macro.” +;; This stops recording and the macro becomes the "last keyboard macro." ;; ;; 3. Replay your macro (or M-x call-last-kbd-macro) ;; ;; 4. Name your macro with M- ;; You will be prompted for a short name (e.g. =align-comments=, -;; =cleanup-trail-spaces=). This name is how you’ll refer to it later. -;; +;; =cleanup-trail-spaces=). This name is how you'll refer to it later. ;; ;; 5. Recall that macro later with M-x [the name you gave the macro] ;; @@ -36,7 +35,7 @@ (require 'user-constants) ;; definition of sync-dir constant is here. -(defvar macros-file (concat sync-dir "macros.el") +(defvar macros-file (concat sync-dir "macros.el") "The location of the macros file for recorded saved macros via M-f3.") (defun ensure-macros-file (file) @@ -45,22 +44,18 @@ (with-temp-file file (insert ";;; -*- lexical-binding: t -*-\n")))) -(when (file-exists-p macros-file) - (load macros-file)) - (defun cj/kbd-macro-start-or-end () "Toggle start/end of keyboard macro definition." (interactive) (if defining-kbd-macro (end-kbd-macro) (start-kbd-macro nil))) -(global-set-key (kbd "C-") #'cj/kbd-macro-start-or-end) -(global-set-key (kbd "") #'call-last-kbd-macro) (defun cj/save-maybe-edit-macro (name) "Save last macro as NAME in `macros-file'; edit if prefix arg." (interactive "SName of macro: ") (kmacro-name-last-macro name) + (ensure-macros-file macros-file) (find-file macros-file) (goto-char (point-max)) (newline) @@ -72,40 +67,34 @@ (find-file macros-file) (goto-char (point-max))) name) + +(defun cj/open-macros-file () + "Open the keyboard macros file." + (interactive) + (ensure-macros-file macros-file) + (find-file macros-file)) + +;; Set up key bindings +(global-set-key (kbd "C-") #'cj/kbd-macro-start-or-end) +(global-set-key (kbd "") #'call-last-kbd-macro) (global-set-key (kbd "M-") #'cj/save-maybe-edit-macro) +(global-set-key (kbd "s-") #'cj/open-macros-file) -(global-set-key (kbd "s-") (lambda () (interactive) (find-file macros-file))) +;; Add hook to save any unnamed macros on exit if desired +(defun cj/save-last-kbd-macro-on-exit () + "Save the last keyboard macro before exiting Emacs if it's not saved." + (when (and last-kbd-macro (not (kmacro-name-last-macro))) + (when (y-or-n-p "Save last keyboard macro before exiting? ") + (call-interactively #'cj/save-maybe-edit-macro)))) -(defun ensure-macros-file (file) - "Ensure FILE exists and its first line enables lexical-binding." - (unless (file-exists-p file) - (with-temp-file file - (insert ";;; -*- lexical-binding: t -*-\n")))) +(add-hook 'kill-emacs-hook #'cj/save-last-kbd-macro-on-exit) +;; Load existing macros file with proper error handling (when (file-exists-p macros-file) - (load macros-file)) + (condition-case err + (load macros-file) + (error + (message "Error loading keyboard macros file: %s" (error-message-string err))))) (provide 'keyboard-macros) -;;; keyboard-macros.el ends here. - - -;; --------------------------------- ERT Tests --------------------------------- -;; Run these tests with M-x ert RET t RET - -(require 'ert) - -(ert-deftest keyboard-macros/ensure-macros-file-creates-header () - "ensure-macros-file creates FILE with the right header." - (let ((file (make-temp-file "macros-test" nil ".el"))) - (unwind-protect - (progn - (delete-file file) - (should-not (file-exists-p file)) - (ensure-macros-file file) - (should (file-exists-p file)) - (let ((contents (with-temp-buffer - (insert-file-contents file) - (buffer-string)))) - (should (string= contents ";;; -*- lexical-binding: t -*-\n")))) - (when (file-exists-p file) - (delete-file file))))) +;;; keyboard-macros.el ends here -- cgit v1.2.3