blob: 63b4692708cc3e344b7aef2b262b17ce08437664 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
;;; keyboard-macros.el --- Keyboard Macro Management -*- lexical-binding: t; coding: utf-8; -*-
;; 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.
;;
;; 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 are lazy-loaded when needed.
;; When you first use keyboard macro functionality or open the macros file,
;; all your previously saved macros are loaded into the current session.
;;
;;; Code:
(require 'user-constants) ;; definitions of org-dir and macros-file
(defvar cj/macros-loaded nil
"Whether saved keyboard macros have been loaded from file.")
(defun cj/ensure-macros-loaded ()
"Load keyboard macros from file if not already loaded.
This function is idempotent and fast when macros are already loaded."
(when (and (not cj/macros-loaded)
(file-exists-p macros-file))
(condition-case err
(progn
(load macros-file)
(setq cj/macros-loaded t))
(error
(message "Error loading keyboard macros file: %s"
(error-message-string err))))))
(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"))))
(defun cj/kbd-macro-start-or-end ()
"Toggle start/end of keyboard macro definition."
(interactive)
;; Lazy load macros on first use
(cj/ensure-macros-loaded)
(if defining-kbd-macro
(end-kbd-macro)
(start-kbd-macro nil)))
(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)
(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)
(defun cj/open-macros-file ()
"Open the keyboard macros file."
(interactive)
;; Ensure macros are loaded before opening the file
(cj/ensure-macros-loaded)
(ensure-macros-file macros-file)
(find-file macros-file))
;; Set up key bindings
(global-set-key (kbd "C-<f3>") #'cj/kbd-macro-start-or-end)
(global-set-key (kbd "<f3>") #'call-last-kbd-macro)
(global-set-key (kbd "M-<f3>") #'cj/save-maybe-edit-macro)
(global-set-key (kbd "s-<f3>") #'cj/open-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))))
(add-hook 'kill-emacs-hook #'cj/save-last-kbd-macro-on-exit)
(provide 'keyboard-macros)
;;; keyboard-macros.el ends here
|