Add command 'list-keyboard-macros' that works like 'list-buffers'.
The command 'list-keyboard-macros' allows editing and re-arranging macros using 'tabulated-list-mode'. Existing keyboard macros can be duplicated or deleted. Macro counters and counter formats can take new values read from the minibuffer. Macro keys can be edited using 'edit-kbd-macro'. * doc/emacs/kmacro.texi (Kmacro Menu): Document the new command and the menu's commands. * etc/NEWS (Kmacro Menu Mode): Mention the new mode and command. * lisp/kmacro.el (kmacro-menu-mark, kmacro-menu-marked) (kmacro-menu-flagged): Add faces for marks and flags. * lisp/kmacro.el (kmacro-menu-mode-map, kmacro-menu-mode): Add mode and map. * lisp/kmacro.el (list-keyboard-macros, kmacro-menu): Add command. * lisp/kmacro.el (kmacro-menu--deletion-flags, kmacro-menu--marks) (kmacro-menu--id-kmacro, kmacro-menu--id-position, kmacro-menu--kmacros) (kmacro-menu--refresh, kmacro-menu--map-ids, kmacro-menu--replace-all) (kmacro-menu--replace-at, kmacro-menu--query-revert, kmacro-menu--assert-row) (kmacro-menu--propertize-keys, kmacro-menu--do-region) (kmacro-menu--marks-exist-p): Add utility functions of mode and commands. * lisp/kmacro.el (kmacro-menu-mark, kmacro-menu-flag-for-deletion) (kmacro-menu-unmark, kmacro-menu-unmark-backward) (kmacro-menu-unmark-all): Add commands for marks and flags. * lisp/kmacro.el (kmacro-menu-do-flagged-delete, kmacro-menu-do-copy) (kmacro-menu-do-delete): Add commands that modify the ring. * lisp/kmacro.el (kmacro-menu-edit-position, kmacro-menu-transpose) (kmacro-menu-edit-format, kmacro-menu-edit-counter) (kmacro-menu-edit-keys, kmacro-menu-edit-column): Add commands that modify a keyboard macro.
This commit is contained in:
parent
b2842b25bf
commit
7add47337b
3 changed files with 730 additions and 0 deletions
|
@ -42,6 +42,8 @@ intelligent or general. For such things, Lisp must be used.
|
|||
* Edit Keyboard Macro:: Editing keyboard macros.
|
||||
* Keyboard Macro Step-Edit:: Interactively executing and editing a keyboard
|
||||
macro.
|
||||
* Kmacro Menu:: An interface for listing and editing
|
||||
keyboard macros and the keyboard macro ring.
|
||||
@end menu
|
||||
|
||||
@node Basic Keyboard Macro
|
||||
|
@ -616,3 +618,163 @@ including the final @kbd{C-j}), and appends them at the end of the
|
|||
keyboard macro; it then terminates the step-editing and replaces the
|
||||
original keyboard macro with the edited macro.
|
||||
@end itemize
|
||||
|
||||
@node Kmacro Menu
|
||||
@section Listing and Editing Keyboard Macros
|
||||
@cindex Kmacro Menu
|
||||
|
||||
@cindex listing current keyboard macros
|
||||
@kindex M-x list-keyboard-macros @key{RET}
|
||||
@findex kmacro-menu
|
||||
@findex list-keyboard-macros
|
||||
To display a list of existing keyboard macros, type @kbd{M-x
|
||||
list-keyboard-macros @key{RET}}. This pops up the @dfn{Kmacro Menu} in
|
||||
a buffer named @file{*Keyboard Macro List*}. Each line in the list
|
||||
shows one macro's position, counter value, counter format, that counter
|
||||
value using that format, and macro keys. Here is an example of a macro
|
||||
list:
|
||||
|
||||
@smallexample
|
||||
Position Counter Format Formatted Keys
|
||||
0 8 %02d 08 N : SPC <F3> RET
|
||||
1 0 %d 0 l o n g SPC p h r a s e
|
||||
@end smallexample
|
||||
|
||||
@noindent
|
||||
The macros are listed with the current macro at the top in position
|
||||
number zero and the older macros in the order in which they are found in
|
||||
the keyboard macro ring (@pxref{Keyboard Macro Ring}). Using the Kmacro
|
||||
Menu, you can change the order of the macros and change their counters,
|
||||
counter formats, and keys. The Kmacro Menu is a read-only buffer, and
|
||||
can be changed only through the special commands described in this
|
||||
section. After a command is run, the Kmacro Menu displays changes to
|
||||
reflect the new values of the macro properties and the macro ring. You
|
||||
can use the usual cursor motion commands in this buffer, as well as
|
||||
special motion commands for navigating the table. To view a list of the
|
||||
special commands, type @kbd{C-h m} or @kbd{?} (@code{describe-mode}) in
|
||||
the Kmacro Menu.
|
||||
|
||||
You can use the following commands to change a macro's properties:
|
||||
|
||||
@table @kbd
|
||||
@item #
|
||||
@findex kmacro-menu-edit-position
|
||||
@kindex # @r{(Kmacro Menu)}
|
||||
Change the position of the macro on the current line
|
||||
(@pxref{Keyboard Macro Ring}).
|
||||
|
||||
@item C-x C-t
|
||||
@findex kmacro-menu-transpose
|
||||
@kindex C-x C-t @r{(Kmacro Menu)}
|
||||
Move the macro on the current line to the line above, like in
|
||||
@code{transpose-lines}.
|
||||
|
||||
@item c
|
||||
@findex kmacro-menu-edit-counter
|
||||
@kindex c @r{(Kmacro Menu)}
|
||||
Change the counter value of the macro on the current line
|
||||
(@pxref{Keyboard Macro Counter}).
|
||||
|
||||
@item f
|
||||
@findex kmacro-menu-edit-format
|
||||
@kindex f @r{(Kmacro Menu)}
|
||||
Change the counter format of the macro on the current line.
|
||||
|
||||
@item e
|
||||
@findex kmacro-menu-edit-keys
|
||||
@kindex e @r{(Kmacro Menu)}
|
||||
Change the keys of the macro on the current line using
|
||||
@code{edit-kbd-macro} (@pxref{Edit Keyboard Macro}).
|
||||
|
||||
@item @key{RET}
|
||||
@findex kmacro-menu-edit-column
|
||||
@kindex @key{RET} @r{(Kmacro Menu)}
|
||||
Change the value in the current column of the macro on the current line
|
||||
using commands above.
|
||||
@end table
|
||||
|
||||
The following commands delete or duplicate macros in the list:
|
||||
|
||||
@table @kbd
|
||||
@item d
|
||||
@findex kmacro-menu-flag-for-deletion
|
||||
@item d @r{(Kmacro Menu)}
|
||||
Flag the macro on the current line for deletion, then move point to the
|
||||
next line (@code{kmacro-menu-flag-for-deletion}). The deletion flag is
|
||||
indicated by the character @samp{D} at the start of line. The deletion
|
||||
occurs only when you type the @kbd{x} command (see below).
|
||||
|
||||
If the region is active, this command flags all of the macros in the
|
||||
region.
|
||||
|
||||
@item x
|
||||
@findex kmacro-menu-do-flagged-delete
|
||||
@item x @r{(Kmacro Menu)}
|
||||
Delete the macros in the list that have been flagged for deletion
|
||||
(@code{kmacro-menu-do-flagged-delete}).
|
||||
|
||||
@item m
|
||||
@findex kmacro-menu-mark
|
||||
@item m @r{(Kmacro Menu)}
|
||||
Mark the macro on the current line, then move point to the next line
|
||||
(@code{kmacro-menu-mark}). Marked macros are indicated by the character
|
||||
@samp{*} at the start of line. Marked macros can be operated on by the
|
||||
@kbd{C} and @kbd{D} commands (see below).
|
||||
|
||||
If the region is active, this command marks all of the macros in the
|
||||
region.
|
||||
|
||||
@item C
|
||||
@findex kmacro-menu-do-copy
|
||||
@item C @r{(Kmacro Menu)}
|
||||
This command copies macros by duplicating them at their current
|
||||
positions in the list (@code{kmacro-menu-do-copy}). For example,
|
||||
running this command on the macro at position number zero will insert a
|
||||
copy of that macro into position number one and move the remaining
|
||||
macros down.
|
||||
|
||||
If the region is active, this command duplicates the macros in the
|
||||
region. Otherwise, if there are marked macros, this command duplicates
|
||||
the marked macros. If there is no region nor are there marked macros,
|
||||
this command duplicates the macro on the current line. In the first two
|
||||
cases, the command prompts for confirmation before duplication.
|
||||
|
||||
@item D
|
||||
@findex kmacro-menu-do-delete
|
||||
@item D @r{(Kmacro Menu)}
|
||||
This command deletes macros, removing them from the ring
|
||||
(@code{kmacro-menu-do-delete}). For example, running this command on
|
||||
the macro at position number zero will delete the current macro and then
|
||||
make the first macro in the macro ring (previously at position number
|
||||
one) the new current macro, popping it from the ring.
|
||||
|
||||
If the region is active, this command deletes the macros in the
|
||||
region. Otherwise, if there are marked macros, this command deletes the
|
||||
marked macros. If there is no region nor are there marked macros, this
|
||||
command deletes the macro on the current line. In all cases, the
|
||||
command prompts for confirmation before deletion.
|
||||
|
||||
This command is an alternative to the @kbd{d} and @kbd{x} commands
|
||||
(see above).
|
||||
|
||||
@item u
|
||||
@findex kmacro-menu-unmark
|
||||
@item u @r{(Kmacro Menu)}
|
||||
Unmark and unflag the macro on the current line, then move point down
|
||||
to the next line (@code{kmacro-menu-unmark}). If there is an active
|
||||
region, this command unmarks and unflags all of the macros in the
|
||||
region.
|
||||
|
||||
@item @key{DEL}
|
||||
@findex kmacro-menu-unmark-backward
|
||||
@item @key{DEL} @r{(Kmacro Menu)}
|
||||
Like the @kbd{u} command (see above), but move point up to the previous
|
||||
line when there is no active region
|
||||
(@code{kmacro-menu-unmark-backward}).
|
||||
|
||||
@item U
|
||||
@findex kmacro-menu-unmark-all
|
||||
@item U @r{(Kmacro Menu)}
|
||||
Unmark and unflag all macros in the list
|
||||
(@code{kmacro-menu-unmark-all}).
|
||||
@end table
|
||||
|
|
10
etc/NEWS
10
etc/NEWS
|
@ -1510,6 +1510,16 @@ macros with many lines, such as from 'kmacro-edit-lossage'.
|
|||
The user option 'proced-auto-update-flag' can now be set to 2 additional
|
||||
values, which control automatic updates of Proced buffers that are not
|
||||
displayed in some window.
|
||||
** Kmacro Menu Mode
|
||||
|
||||
+++
|
||||
*** New mode 'kmacro-menu-mode' and new command 'list-keyboard-macros'.
|
||||
The new command 'list-keyboard-macros' is the keyboard-macro version
|
||||
of commands like 'list-buffers' and 'list-processes', creating a listing
|
||||
of the currently existing keyboards macros using the new mode
|
||||
'kmacro-menu-mode'. It allows rearranging the macros in the ring,
|
||||
duplicating them, deleting them, and editing their counters, formats,
|
||||
and keys.
|
||||
|
||||
** Miscellaneous
|
||||
|
||||
|
|
558
lisp/kmacro.el
558
lisp/kmacro.el
|
@ -1388,6 +1388,564 @@ To customize possible responses, change the \"bindings\" in
|
|||
(let ((executing-kbd-macro nil))
|
||||
(redisplay))))
|
||||
|
||||
;;; Mode and commands for working with the ring in a table
|
||||
|
||||
(defface kmacro-menu-mark '((t (:inherit font-lock-constant-face)))
|
||||
"Face used for the Keyboard Macro Menu marks."
|
||||
:group 'kmacro
|
||||
:version "30.1")
|
||||
|
||||
(defface kmacro-menu-flagged '((t (:inherit error)))
|
||||
"Face used for keyboard macros flagged for deletion."
|
||||
:group 'kmacro
|
||||
:version "30.1")
|
||||
|
||||
(defface kmacro-menu-marked '((t (:inherit warning)))
|
||||
"Face used for keyboard macros marked for duplication."
|
||||
:group 'kmacro
|
||||
:version "30.1")
|
||||
|
||||
(defvar-keymap kmacro-menu-mode-map
|
||||
:doc "Keymap for `kmacro-menu-mode'."
|
||||
:parent tabulated-list-mode-map
|
||||
"#" #'kmacro-menu-edit-position
|
||||
"c" #'kmacro-menu-edit-counter
|
||||
"e" #'kmacro-menu-edit-keys
|
||||
"f" #'kmacro-menu-edit-format
|
||||
"RET" #'kmacro-menu-edit-column
|
||||
|
||||
"C" #'kmacro-menu-do-copy
|
||||
"D" #'kmacro-menu-do-delete
|
||||
"m" #'kmacro-menu-mark
|
||||
|
||||
"d" #'kmacro-menu-flag-for-deletion
|
||||
"x" #'kmacro-menu-do-flagged-delete
|
||||
|
||||
"u" #'kmacro-menu-unmark
|
||||
"U" #'kmacro-menu-unmark-all
|
||||
"DEL"#'kmacro-menu-unmark-backward
|
||||
|
||||
"<remap> <transpose-lines>" #'kmacro-menu-transpose)
|
||||
|
||||
(define-derived-mode kmacro-menu-mode tabulated-list-mode
|
||||
"Keyboard Macro Menu"
|
||||
"Major mode for listing and editing keyboard macros."
|
||||
(make-local-variable 'kmacro-menu--marks)
|
||||
(make-local-variable 'kmacro-menu--deletion-flags)
|
||||
(setq-local tabulated-list-format
|
||||
[("Position" 8 nil)
|
||||
("Counter" 8 nil :right-align t :pad-right 2)
|
||||
("Format" 8 nil)
|
||||
("Formatted" 10 nil)
|
||||
("Keys" 1 nil)])
|
||||
(setq-local tabulated-list-padding 2)
|
||||
(add-hook 'tabulated-list-revert-hook #'kmacro-menu--refresh nil t)
|
||||
(tabulated-list-init-header)
|
||||
(unless (kmacro-ring-empty-p)
|
||||
(kmacro-menu--refresh)
|
||||
(tabulated-list-print)))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'kmacro-menu #'list-keyboard-macros)
|
||||
;;;###autoload
|
||||
(defun list-keyboard-macros ()
|
||||
"List the keyboard macros."
|
||||
(interactive)
|
||||
(let ((buf (get-buffer-create "*Keyboard Macro List*")))
|
||||
(with-current-buffer buf
|
||||
(kmacro-menu-mode))
|
||||
(pop-to-buffer buf)))
|
||||
|
||||
;;;; Utility functions and mode data
|
||||
|
||||
(defvar kmacro-menu--deletion-flags nil
|
||||
"Alist of entries flagged for deletion.")
|
||||
|
||||
(defvar kmacro-menu--marks nil
|
||||
"Alist of entries marked for copying and duplication.")
|
||||
|
||||
(defun kmacro-menu--id-kmacro (entry-id)
|
||||
"Return the keyboard macro that is part of the ENTRY-ID."
|
||||
(car entry-id))
|
||||
|
||||
(defun kmacro-menu--id-position (entry-id)
|
||||
"Return the ordinal position that is part of the ENTRY-ID."
|
||||
(cdr entry-id))
|
||||
|
||||
(defun kmacro-menu--kmacros ()
|
||||
"Return the list of the existing keyboard macros or nil, if none are defined."
|
||||
(when last-kbd-macro
|
||||
(cons (kmacro-ring-head)
|
||||
kmacro-ring)))
|
||||
|
||||
(defun kmacro-menu--refresh ()
|
||||
"Reset the list of keyboard macros."
|
||||
(setq-local tabulated-list-entries
|
||||
(seq-map-indexed (lambda (km idx)
|
||||
(let ((cnt (kmacro--counter km))
|
||||
(fmt (kmacro--format km)))
|
||||
`((,km . ,idx)
|
||||
[,(format "%d" idx)
|
||||
,(format "%d" cnt)
|
||||
,fmt
|
||||
,(format fmt cnt)
|
||||
,(format-kbd-macro (kmacro--keys km))])))
|
||||
(kmacro-menu--kmacros))
|
||||
kmacro-menu--deletion-flags nil
|
||||
kmacro-menu--marks nil)
|
||||
(tabulated-list-clear-all-tags))
|
||||
|
||||
(defun kmacro-menu--map-ids (function)
|
||||
"Apply FUNCTION to the current table's entry IDs in order.
|
||||
|
||||
Return a list of the output of FUNCTION."
|
||||
(mapcar function
|
||||
(mapcar #'car
|
||||
(seq-sort-by #'cdar #'< tabulated-list-entries))))
|
||||
|
||||
(defun kmacro-menu--replace-all (kmacros)
|
||||
"Replace the existing keyboard macros with those in KMACROS.
|
||||
|
||||
The first element in the list overwrites the values of `last-kbd-macro',
|
||||
`kmacro-counter', and `kmacro-counter-format'. The remaining elements
|
||||
become the value of `kmacro-ring'.
|
||||
|
||||
KMACROS is a list of `kmacro' objects."
|
||||
(if (null kmacros)
|
||||
(setq last-kbd-macro nil
|
||||
kmacro-counter-format kmacro-default-counter-format
|
||||
kmacro-counter 0
|
||||
kmacro-ring nil)
|
||||
(if (not (seq-every-p #'kmacro-p kmacros))
|
||||
(error "All elements must satisfy `kmacro-p'")
|
||||
(kmacro-split-ring-element (car kmacros))
|
||||
(setq kmacro-ring (cdr kmacros)))))
|
||||
|
||||
(defun kmacro-menu--replace-at (kmacro n)
|
||||
"Replace the keyboard macro at position N with KMACRO.
|
||||
|
||||
This function replaces all of the existing keyboard macros via
|
||||
`kmacro-menu--replace-all'. Except for the macro at position N, which will
|
||||
be KMACRO, the replacement macros are the existing macros identified in
|
||||
the table."
|
||||
(kmacro-menu--replace-all
|
||||
(kmacro-menu--map-ids (lambda (id)
|
||||
(if (= n (kmacro-menu--id-position id))
|
||||
kmacro
|
||||
(kmacro-menu--id-kmacro id))))))
|
||||
|
||||
(defun kmacro-menu--query-revert ()
|
||||
"If the table differs from the existing macros, ask whether to revert table."
|
||||
(when (and (not (equal (kmacro-menu--kmacros)
|
||||
(kmacro-menu--map-ids #'kmacro-menu--id-kmacro)))
|
||||
(yes-or-no-p "Table does not match existing keyboard macros. Stop and revert table?"))
|
||||
(tabulated-list-revert)
|
||||
(signal 'quit nil)))
|
||||
|
||||
(defun kmacro-menu--assert-row (&optional id)
|
||||
"Signal an error if point is not on a table row.
|
||||
|
||||
ID is the tabulated list id of the supposed entry at point."
|
||||
(unless (or id (tabulated-list-get-id))
|
||||
(user-error "Not on a table row")))
|
||||
|
||||
(defun kmacro-menu--propertize-keys (face)
|
||||
"Redisplay the macro keys on the current line with FACE."
|
||||
(tabulated-list-set-col 4 (propertize (aref (tabulated-list-get-entry) 4)
|
||||
'face face)))
|
||||
|
||||
(defun kmacro-menu--do-region (function)
|
||||
"Run FUNCTION on macros in the region or on the current line at the line start.
|
||||
|
||||
If there is an active region, for each line in the region, move to the
|
||||
beginning of the line and apply FUNCTION to the table entry ID of the
|
||||
line. If there is no region, apply FUNCTION only to the table entry ID
|
||||
of the current line.
|
||||
|
||||
When there is no active region, advance to the beginning of the next
|
||||
line after applying FUNCTION."
|
||||
(if (use-region-p)
|
||||
(save-excursion
|
||||
(let* ((reg-beg (region-beginning))
|
||||
(reg-end (region-end))
|
||||
(line-beg (progn
|
||||
(goto-char reg-beg)
|
||||
(pos-bol)))
|
||||
(line-end (progn
|
||||
(goto-char reg-end)
|
||||
(if (bolp)
|
||||
reg-end
|
||||
(pos-bol 2)))))
|
||||
(goto-char line-beg)
|
||||
(let ((id))
|
||||
(while (and (< (point) line-end)
|
||||
(setq id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(funcall function id)
|
||||
(forward-line 1)))))
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(goto-char (pos-bol))
|
||||
(funcall function id)
|
||||
(forward-line 1))))
|
||||
|
||||
(defun kmacro-menu--marks-exist-p ()
|
||||
"Return non-nil if markers exist for any table entries."
|
||||
(let ((tag (gensym)))
|
||||
(catch tag
|
||||
(kmacro-menu--map-ids (lambda (id)
|
||||
(when (alist-get (kmacro-menu--id-position id)
|
||||
kmacro-menu--marks)
|
||||
(throw tag t))))
|
||||
nil)))
|
||||
|
||||
;;;; Commands for Marks and Flags
|
||||
|
||||
(defun kmacro-menu-mark ()
|
||||
"Mark macros in the region or on the current line.
|
||||
|
||||
If there's an active region, mark macros in the region; otherwise mark
|
||||
the macro on the current line. If marking the current line, move point
|
||||
to the next line when done.
|
||||
|
||||
Marked macros can be operated on by `kmacro-menu-do-copy' and
|
||||
`kmacro-menu-do-delete'."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(kmacro-menu--do-region
|
||||
(lambda (id)
|
||||
(setf (alist-get (kmacro-menu--id-position id)
|
||||
kmacro-menu--marks)
|
||||
t)
|
||||
(kmacro-menu--propertize-keys 'kmacro-menu-marked)
|
||||
(tabulated-list-put-tag #("*" 0 1 (face kmacro-menu-mark))))))
|
||||
|
||||
(defun kmacro-menu-flag-for-deletion ()
|
||||
"Flag macros in the region or on the current line.
|
||||
|
||||
If there's an active region, flag macros in the region; otherwise flag
|
||||
the macro on the current line. If there is no active region, move point
|
||||
to the next line when done.
|
||||
|
||||
Flagged macros can be deleted via `kmacro-menu-do-flagged-delete'."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(kmacro-menu--do-region
|
||||
(lambda (id)
|
||||
(setf (alist-get (kmacro-menu--id-position id)
|
||||
kmacro-menu--deletion-flags)
|
||||
t)
|
||||
(kmacro-menu--propertize-keys 'kmacro-menu-flagged)
|
||||
(tabulated-list-put-tag #("D" 0 1 (face kmacro-menu-mark))))))
|
||||
|
||||
(defun kmacro-menu-unmark ()
|
||||
"Unmark and unflag macros in the region or on the current line.
|
||||
|
||||
If there's an active region, unmark and unflag macros in the region;
|
||||
otherwise unmark and unflag the macro on the current line. If there is
|
||||
no active region, move point to the next line when done."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(kmacro-menu--do-region
|
||||
(lambda (id)
|
||||
(let ((pos (kmacro-menu--id-position id)))
|
||||
(setf (alist-get pos kmacro-menu--deletion-flags) nil
|
||||
(alist-get pos kmacro-menu--marks) nil))
|
||||
(kmacro-menu--propertize-keys 'default)
|
||||
(tabulated-list-put-tag " "))))
|
||||
|
||||
(defun kmacro-menu-unmark-backward ()
|
||||
"Like `kmacro-menu-unmark', but move backwards instead of forwards."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(let ((go-back (not (use-region-p))))
|
||||
(kmacro-menu-unmark)
|
||||
(when go-back
|
||||
(forward-line -2))))
|
||||
|
||||
(defun kmacro-menu-unmark-all ()
|
||||
"Unmark and unflag all listed keyboard macros."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(setq-local kmacro-menu--deletion-flags nil
|
||||
kmacro-menu--marks nil)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(while (tabulated-list-get-id)
|
||||
(kmacro-menu--propertize-keys 'default)
|
||||
(forward-line 1))
|
||||
(tabulated-list-clear-all-tags)))
|
||||
|
||||
;;;; Commands that Modify the Ring
|
||||
|
||||
(defun kmacro-menu-do-flagged-delete ()
|
||||
"Delete keyboard macros flagged via `kmacro-menu-flag-for-deletion'."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(let ((res)
|
||||
(num-deletes 0))
|
||||
(kmacro-menu--map-ids (lambda (id)
|
||||
(if (alist-get (kmacro-menu--id-position id)
|
||||
kmacro-menu--deletion-flags)
|
||||
(setq num-deletes (1+ num-deletes))
|
||||
(push (kmacro-menu--id-kmacro id) res))))
|
||||
(when (yes-or-no-p (if (= 1 num-deletes)
|
||||
"Delete 1 flagged keyboard macro?"
|
||||
(format "Delete %d flagged keyboard macros?"
|
||||
num-deletes)))
|
||||
(kmacro-menu--replace-all
|
||||
(nreverse res))
|
||||
(tabulated-list-revert))))
|
||||
|
||||
(defun kmacro-menu-do-copy ()
|
||||
"Duplicate macros in the region, those with markers, or the one at point.
|
||||
|
||||
Macros are duplicated at their current position in the macro ring.
|
||||
|
||||
If there's an active region, duplicate macros in the region; otherwise
|
||||
duplicate the marked macros or, if there are no marks, the macro on the
|
||||
current line."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(let* ((region-exists (use-region-p))
|
||||
(mark-exists (kmacro-menu--marks-exist-p))
|
||||
(id-alist (if (or region-exists
|
||||
(not mark-exists))
|
||||
(let ((region-alist))
|
||||
(kmacro-menu--do-region
|
||||
(lambda (id)
|
||||
(push (cons (kmacro-menu--id-position id)
|
||||
t)
|
||||
region-alist)))
|
||||
region-alist)
|
||||
kmacro-menu--marks))
|
||||
(num-duplicates 0))
|
||||
(let ((res))
|
||||
(kmacro-menu--map-ids (lambda (id)
|
||||
(let ((pos (kmacro-menu--id-position id))
|
||||
(km (kmacro-menu--id-kmacro id)))
|
||||
(push km res)
|
||||
(when (alist-get pos id-alist)
|
||||
(push km res)
|
||||
(setq num-duplicates (1+ num-duplicates))))))
|
||||
;; Confirm the action if we operated on marks or the region, but
|
||||
;; don't confirm if operating on a single line without a region.
|
||||
(when (if (or mark-exists region-exists)
|
||||
(yes-or-no-p (if (= 1 num-duplicates)
|
||||
"Copy (duplicate) 1 keyboard macro?"
|
||||
(format "Copy (duplicate) %d keyboard macros?"
|
||||
num-duplicates)))
|
||||
t)
|
||||
(kmacro-menu--replace-all (nreverse res))
|
||||
(tabulated-list-revert)))))
|
||||
|
||||
(defun kmacro-menu-do-delete ()
|
||||
"Delete macros in the region, those with markers, or the one at point.
|
||||
|
||||
If there's an active region, delete macros in the region; otherwise
|
||||
delete the marked macros or, if there are no marks, the macro on the
|
||||
current line."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--query-revert)
|
||||
(let ((num-deletes 0)
|
||||
(id-alist (if (or (use-region-p)
|
||||
(not (kmacro-menu--marks-exist-p)))
|
||||
(let ((region-alist))
|
||||
(kmacro-menu--do-region
|
||||
(lambda (id)
|
||||
(push (cons (kmacro-menu--id-position id)
|
||||
t)
|
||||
region-alist)))
|
||||
region-alist)
|
||||
kmacro-menu--marks)))
|
||||
(let ((res))
|
||||
(kmacro-menu--map-ids (lambda (id)
|
||||
(if (alist-get (kmacro-menu--id-position id)
|
||||
id-alist)
|
||||
(setq num-deletes (1+ num-deletes))
|
||||
(push (kmacro-menu--id-kmacro id) res))))
|
||||
(when (yes-or-no-p (if (= 1 num-deletes)
|
||||
"Delete 1 keyboard macro?"
|
||||
(format "Delete %d keyboard macros?"
|
||||
num-deletes)))
|
||||
(kmacro-menu--replace-all (nreverse res))
|
||||
(tabulated-list-revert)))))
|
||||
|
||||
;;;; Commands that Modify a Keyboard Macro
|
||||
|
||||
(defun kmacro-menu-edit-position ()
|
||||
"Move the keyboard macro at point to a new position.
|
||||
|
||||
See the Info node `(emacs) Keyboard Macro Ring' for more information."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(kmacro-menu--query-revert)
|
||||
(let* ((new-position (min (length tabulated-list-entries)
|
||||
(max 0
|
||||
(read-number "New position: " 0))))
|
||||
(old-km (kmacro-menu--id-kmacro id))
|
||||
(old-pos (kmacro-menu--id-position id)))
|
||||
(unless (= old-pos new-position)
|
||||
(kmacro-menu--replace-all
|
||||
(let ((res)
|
||||
(true-new-pos (if (> new-position old-pos)
|
||||
(1+ new-position)
|
||||
new-position)))
|
||||
(kmacro-menu--map-ids (lambda (this-id)
|
||||
(let ((this-km (kmacro-menu--id-kmacro this-id))
|
||||
(this-pos (kmacro-menu--id-position this-id)))
|
||||
(unless (= old-pos this-pos)
|
||||
(when (= this-pos true-new-pos)
|
||||
(push old-km res))
|
||||
(push this-km res)))))
|
||||
(when (>= true-new-pos
|
||||
(length tabulated-list-entries))
|
||||
(push old-km res))
|
||||
(nreverse res)))
|
||||
(tabulated-list-revert)))))
|
||||
|
||||
(defun kmacro-menu-transpose ()
|
||||
"Swap the keyboard macro at point with the one above, then move to the next line.
|
||||
|
||||
If point is on the first line (position number 0), then swap the macros
|
||||
at position numbers 0 and 1, then move point to the third line.
|
||||
|
||||
Note that this is the earlier position in the ring, not the sorted
|
||||
table."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(kmacro-menu--query-revert)
|
||||
(let* ((old-pos (kmacro-menu--id-position id))
|
||||
(first-line (= 0 old-pos))
|
||||
(end-lines-forward (if first-line
|
||||
2
|
||||
(+ 3 old-pos))))
|
||||
;; When transposing the first two macros, we don't use
|
||||
;; `kmacro-swap-ring' here because it is possible for the user to
|
||||
;; choose to not refresh the table when it is out of date.
|
||||
(kmacro-menu--replace-all
|
||||
(let ((res))
|
||||
(kmacro-menu--map-ids
|
||||
(if first-line
|
||||
(let ((old-km (kmacro-menu--id-kmacro id)))
|
||||
(lambda (this-id)
|
||||
(let ((this-pos (kmacro-menu--id-position this-id)))
|
||||
(unless (= 0 this-pos)
|
||||
(push (kmacro-menu--id-kmacro this-id) res)
|
||||
(when (= 1 this-pos)
|
||||
(push old-km res))))))
|
||||
(let ((new-pos (1- old-pos)))
|
||||
(lambda (this-id)
|
||||
(let ((this-pos (kmacro-menu--id-position this-id)))
|
||||
(unless (= old-pos this-pos)
|
||||
(when (= new-pos this-pos)
|
||||
(push (kmacro-menu--id-kmacro id) res))
|
||||
(push (kmacro-menu--id-kmacro this-id) res)))))))
|
||||
(nreverse res)))
|
||||
(tabulated-list-revert)
|
||||
(goto-char (point-min))
|
||||
(forward-line end-lines-forward))))
|
||||
|
||||
(defun kmacro-menu-edit-format ()
|
||||
"Edit the counter format of the keyboard macro at point.
|
||||
|
||||
Valid counter formats are those for integers accepted by the function
|
||||
`format'.
|
||||
|
||||
See the command `kmacro-set-format' and the Info node `(emacs) Keyboard
|
||||
Macro Counter' for more information."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(kmacro-menu--query-revert)
|
||||
(let ((km (kmacro-menu--id-kmacro id)))
|
||||
(kmacro-menu--replace-at
|
||||
(kmacro (kmacro--keys km)
|
||||
(kmacro--counter km)
|
||||
(read-string "New format: " nil nil
|
||||
(list kmacro-default-counter-format
|
||||
(kmacro--format km))))
|
||||
(kmacro-menu--id-position id))
|
||||
(tabulated-list-revert))))
|
||||
|
||||
(defun kmacro-menu-edit-counter ()
|
||||
"Edit the counter of the keyboard macro at point.
|
||||
|
||||
See Info node `(emacs) Keyboard Macro Counter' for more
|
||||
information."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(kmacro-menu--query-revert)
|
||||
(let ((km (kmacro-menu--id-kmacro id)))
|
||||
(kmacro-menu--replace-at
|
||||
(kmacro (kmacro--keys km)
|
||||
(read-number "New counter: "
|
||||
(list 0
|
||||
(kmacro--counter
|
||||
(kmacro-menu--id-kmacro id))))
|
||||
(kmacro--format km))
|
||||
(kmacro-menu--id-position id))
|
||||
(tabulated-list-revert))))
|
||||
|
||||
(defun kmacro-menu-edit-keys ()
|
||||
"Edit the keys of the keyboard macro at point via `edmacro-mode'.
|
||||
|
||||
See Info node `(emacs) Edit Keyboard Macro' for more
|
||||
information."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(let ((id (tabulated-list-get-id)))
|
||||
(kmacro-menu--assert-row id)
|
||||
(kmacro-menu--query-revert)
|
||||
(let* ((old-km (kmacro-menu--id-kmacro id)))
|
||||
(edit-kbd-macro (kmacro--keys old-km)
|
||||
nil
|
||||
nil
|
||||
(lambda (mac)
|
||||
(kmacro-menu--replace-at
|
||||
(kmacro mac
|
||||
(kmacro--counter old-km)
|
||||
(kmacro--format old-km))
|
||||
(kmacro-menu--id-position id))
|
||||
(tabulated-list-revert))))))
|
||||
|
||||
(defun kmacro-menu-edit-column ()
|
||||
"Edit the value in the current column of the keyboard macro at point."
|
||||
(declare (modes kmacro-menu-mode))
|
||||
(interactive nil kmacro-menu-mode)
|
||||
(kmacro-menu--assert-row)
|
||||
(kmacro-menu--query-revert)
|
||||
(pcase (get-text-property (point) 'tabulated-list-column-name)
|
||||
('nil (let ((pos (point)))
|
||||
;; If we didn't find a column, try moving forwards or
|
||||
;; backwards to the nearest column.
|
||||
(tabulated-list-next-column 1)
|
||||
(when (= pos (point))
|
||||
(tabulated-list-previous-column 1))
|
||||
(if (null (get-text-property (point) 'tabulated-list-column-name))
|
||||
(user-error "No column at point")
|
||||
(kmacro-menu-edit-column))))
|
||||
("Position" (call-interactively #'kmacro-menu-edit-position))
|
||||
("Counter" (call-interactively #'kmacro-menu-edit-counter))
|
||||
("Format" (call-interactively #'kmacro-menu-edit-format))
|
||||
("Formatted" (user-error "Formatted counter is not editable"))
|
||||
("Keys" (call-interactively #'kmacro-menu-edit-keys))))
|
||||
|
||||
(provide 'kmacro)
|
||||
|
||||
;;; kmacro.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue