summaryrefslogtreecommitdiff
path: root/modules/keyboard-macros.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-08-14 19:24:49 -0500
committerCraig Jennings <c@cjennings.net>2025-08-14 19:24:49 -0500
commit9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b (patch)
tree1105519cd55a4ebbb1e91609e6aae7cc3929ddaf /modules/keyboard-macros.el
parenta878e5ae99f750ecbbb723f98ef91d3404189a32 (diff)
downloaddotemacs-9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b.tar.gz
dotemacs-9278ddd4ea1a8b1a4c1edaa8894516e3f48d245b.zip
refactor(system-utils): major refactoring / adding tests
Theme: Modularize system-utilities into separate modules. Clean up any typos, buts, and unused variables. Add some initial ERT tests for new modules created. Changes: - Extract file handling into its own module (file-config) - Extract keyboard macro management into its own module (keyboard-macros) - Extract buffer burying (instead of killing) into its own module (undead-buffers) - Extract all date/time config into its own module (chrono-tools) - Moved keybinding discovery functionality and help into keybindings module - Combine flyspell and abbrev (spell-check and autocorrect) to flyspell-and-abbrev.el - Rename epa-config.el to auth-config.el for auth-source and epa settings. - Refactor `cj/kill-other-window` for more accurate buffer handling. - Include "*ert*" in the default bury (don't kill) list as killing it kills test runs. - Bind C-c M-m to inhibit-mouse-mode - Remove the unused ledger-file variable in user-constants.el. - Removed obsolete C-x x m, C-x x r, and C-x x d key mappings. - C-; b r to call cj/rename-buffer-and-file instead of typo’d function - Other purely cosmetic comment changes to system-utils.el ERT tests: - Rename ERT test definitions to include module scopes (file-config, keyboard-macros) - Add an ERT test for the timer bell's existence. - Add ERT tests to cover `cj/kill-buffer-or-bury-alive`, prefix-arg behavior, window-killing commands, and bulk operations. - Add test `authinfo-file` exists Missing authinfo triggers a debug message - Add test that `gpg2` executable is on the user’s PATH - Remove outdated authinfo test. - Add “Run these tests” note where missing.
Diffstat (limited to 'modules/keyboard-macros.el')
-rw-r--r--modules/keyboard-macros.el111
1 files changed, 111 insertions, 0 deletions
diff --git a/modules/keyboard-macros.el b/modules/keyboard-macros.el
new file mode 100644
index 00000000..cff25b39
--- /dev/null
+++ b/modules/keyboard-macros.el
@@ -0,0 +1,111 @@
+;;; keyboard-macros.el --- Keyboard Macro Management -*- lexical-binding: t; -*-
+;; author Craig Jennings <c@cjennings.net>
+
+;;; Commentary:
+;;
+;; This library provides a simple, end-user–focused interface for
+;; creating, naming, saving, and replaying keyboard macros in Emacs.
+;; All commands are built on top of the built-in =kmacro= machinery, but
+;; add a lightweight workflow and persistence across sessions.
+;;
+;; User 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.”
+;;
+;; 3. Replay your macro <f3> (or M-x call-last-kbd-macro)
+;;
+;; 4. Name your macro with M-<F3>
+;; 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.
+;;
+;;
+;; 5. Recall that macro later with M-x [the name you gave the macro]
+;;
+;; 6. View all your saved macros with s-<f3> (super-f3)
+;;
+;; 7. All macros reload at startup automatically.
+;; When this library is loaded, it will look for the save file and
+;; re-establish all your named macros in your current session.
+;;
+;;; Code:
+
+(require 'user-constants) ;; definition of sync-dir constant is here.
+
+(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)
+ "Ensure FILE exists and its first line enables lexical-binding."
+ (unless (file-exists-p file)
+ (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-<f3>") #'cj/kbd-macro-start-or-end)
+(global-set-key (kbd "<f3>") #'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)
+ (find-file macros-file)
+ (goto-char (point-max))
+ (newline)
+ (insert-kbd-macro name)
+ (newline)
+ (save-buffer)
+ (switch-to-buffer (other-buffer (current-buffer) 1))
+ (when current-prefix-arg
+ (find-file macros-file)
+ (goto-char (point-max)))
+ name)
+(global-set-key (kbd "M-<f3>") #'cj/save-maybe-edit-macro)
+
+(global-set-key (kbd "s-<f3>") (lambda () (interactive) (find-file macros-file)))
+
+(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"))))
+
+(when (file-exists-p macros-file)
+ (load macros-file))
+
+(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)))))