blob: cff25b390d77e05795a3e2f6e39b45a98e94b3ba (
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
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)))))
|