├── .gitignore ├── images ├── dmacro-demo.gif └── dmacro-header.png ├── Keg ├── LICENSE ├── .github └── workflows │ └── test.yml ├── Makefile ├── dmacro-tests.el ├── README.org ├── dmacro.el └── doc └── doc.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ## .gitignore 2 | 3 | *-autoloads.el 4 | *.elc 5 | /.keg 6 | -------------------------------------------------------------------------------- /images/dmacro-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-jp/dmacro/HEAD/images/dmacro-demo.gif -------------------------------------------------------------------------------- /images/dmacro-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emacs-jp/dmacro/HEAD/images/dmacro-header.png -------------------------------------------------------------------------------- /Keg: -------------------------------------------------------------------------------- 1 | ;; Keg 2 | 3 | (source gnu melpa) 4 | 5 | (package 6 | (dmacro 7 | (recipe . (dmacro :fetcher github :repo "emacs-jp/dmacro")))) 8 | 9 | (dev-dependency cort) 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copying and distribution of this file, with or without modification, 2 | are permitted in any medium without royalty provided the copyright 3 | notice and this notice are preserved. This file is offered as-is, 4 | without any warranty. 5 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | ## test.yml 2 | 3 | name: Main workflow 4 | on: [push, pull_request] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | emacs_version: 12 | - '24.5' 13 | - '25.3' 14 | - '26.3' 15 | - '27.2' 16 | - '28.2' 17 | - '29.4' 18 | - 'snapshot' 19 | include: 20 | - emacs_version: 'snapshot' 21 | allow_failure: true 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: actions/setup-python@v5 25 | with: 26 | python-version: '3.11' 27 | architecture: 'x64' 28 | - uses: purcell/setup-emacs@master 29 | with: 30 | version: ${{ matrix.emacs_version }} 31 | - uses: conao3/setup-keg@master 32 | 33 | - name: Run tests 34 | if: matrix.allow_failure != true 35 | run: 'make test' 36 | 37 | - name: Run tests (allow failure) 38 | if: matrix.allow_failure == true 39 | run: 'make test || true' 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## Makefile 2 | 3 | all: 4 | 5 | REPO_USER := emacs-jp 6 | PACKAGE_NAME := dmacro 7 | REPO_NAME := dmacro 8 | 9 | EMACS ?= emacs 10 | 11 | ################################################## 12 | 13 | .PHONY: all help build test lint clean 14 | 15 | all: help 16 | 17 | help: 18 | $(info ) 19 | $(info Commands) 20 | $(info ========) 21 | $(info - make # Show this help) 22 | $(info - make build # Compile Elisp files) 23 | $(info - make test # Compile Elisp files and test $(PACKAGE_NAME)) 24 | $(info - make lint # Lint Elisp files) 25 | $(info ) 26 | $(info Cleaning) 27 | $(info ========) 28 | $(info - make clean # Clean compiled files) 29 | $(info ) 30 | $(info This Makefile required `keg`) 31 | $(info See https://github.com/$(REPO_USER)/$(REPO_NAME)#contribution) 32 | $(info ) 33 | 34 | ############################## 35 | 36 | build: 37 | keg build 38 | 39 | lint: 40 | keg lint 41 | 42 | test: build 43 | keg exec $(EMACS) --batch -l $(PACKAGE_NAME)-tests.el -f cort-test-run 44 | 45 | clean: 46 | keg clean 47 | -------------------------------------------------------------------------------- /dmacro-tests.el: -------------------------------------------------------------------------------- 1 | ;;; dmacro-tests.el --- Test definitions for dmacro -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2019 Naoya Yamashita 4 | 5 | ;; Author: Naoya Yamashita 6 | ;; URL: https://github.com/conao3/dmacro.el 7 | 8 | ;; This program is free software: you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Test definitions for `dmacro'. 24 | 25 | 26 | ;;; Code: 27 | 28 | (require 'cort) 29 | (require 'dmacro) 30 | 31 | 32 | ;; (provide 'dmacro-tests) 33 | 34 | ;; Local Variables: 35 | ;; indent-tabs-mode: nil 36 | ;; End: 37 | 38 | ;;; dmacro-tests.el ends here 39 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+date: <2024-04-13 Sat> 2 | 3 | [[https://github.com/emacs-jp/dmacro][./images/dmacro-header.png]] 4 | [[https://github.com/emacs-jp/dmacro/blob/master/LICENSE][https://img.shields.io/github/license/emacs-jp/dmacro.svg?style=flat-square]] 5 | [[https://github.com/emacs-jp/dmacro/releases][https://img.shields.io/github/tag/emacs-jp/dmacro.svg?style=flat-square]] 6 | [[https://github.com/emacs-jp/dmacro/actions][https://github.com/emacs-jp/dmacro/workflows/Main%20workflow/badge.svg]] 7 | 8 | * Table of Contents 9 | - [[#description][Description]] 10 | - [[#install][Install]] 11 | - [[#usage][Usage]] 12 | - [[#customize][Customize]] 13 | - [[#information][Information]] 14 | - [[#contribution][Contribution]] 15 | - [[#license][License]] 16 | 17 | * Description 18 | [[./images/dmacro-demo.gif]] 19 | 20 | Repeated detection and execution of key operation. 21 | 22 | * Install 23 | Sample install code using [[https://github.com/conao3/leaf.el][leaf.el]]. 24 | 25 | #+begin_src emacs-lisp 26 | (leaf dmacro 27 | :ensure t 28 | :custom `((dmacro-key . ,(kbd "C-S-e"))) 29 | :global-minor-mode global-dmacro-mode) 30 | #+end_src 31 | 32 | * Usage 33 | First turn on =dmacro-mode= or =global-dmacro-mode=. 34 | 35 | Then press =dmacro-key= after making repeated edits. 36 | 37 | ** Example1 38 | #+begin_src fundamental 39 | abc abc _ 40 | ^(point) 41 | #+end_src 42 | 43 | after type =dmacro-key=, your buffer should be... 44 | 45 | #+begin_src fundamental 46 | abc abc abc _ 47 | #+end_src 48 | 49 | and type =dmacro-key=, then... 50 | 51 | #+begin_src fundamental 52 | abc abc abc abc _ 53 | #+end_src 54 | 55 | ** Example2 56 | #+begin_src fundamental 57 | abcdef ab_ 58 | ^(point) 59 | #+end_src 60 | 61 | after type =dmacro-key=, your buffer should be... 62 | 63 | #+begin_src fundamental 64 | abcdef abcdef _ 65 | #+end_src 66 | 67 | and type =dmacro-key=, then... 68 | 69 | #+begin_src fundamental 70 | abcdef abcdef abcdef _ 71 | #+end_src 72 | 73 | * Customize 74 | - dmacro-key :: Key sequences for dmacro 75 | 76 | [NOTE]: You have to set this variable before =dmacro-mode= is started. 77 | 78 | * Information 79 | ** Contribution 80 | We welcome PR! 81 | 82 | *** Require tools for testing 83 | - keg 84 | #+begin_src shell 85 | cd ~/ 86 | gh repo clone conao3/keg.el .keg 87 | export PATH="$HOME/.keg/bin:$PATH" 88 | #+end_src 89 | 90 | *** Running test 91 | We recommend the following operation flow: 92 | #+begin_src shell 93 | make # Install git-hooks in local .git 94 | 95 | git branch [feature-branch] # Create branch named [feature-branch] 96 | git switch [feature-branch] # Switch branch named [feature-branch] 97 | 98 | # 99 | emacs dmacro.el # Edit something you want 100 | 101 | make test # Test dmacro via multi version Emacs (but there is no test now...) 102 | git commit -am "brabra" # Commit (auto-run test before commit) 103 | # 104 | 105 | gh repo fork # Create fork at GitHub 106 | git push origin [feature-branch] # Push feature-branch to your fork 107 | gh pr create # Create pull-request 108 | #+end_src 109 | 110 | ** License 111 | #+begin_example 112 | FSFAP 113 | Copyright (c) 1993 Toshiyuki Masui 114 | https://github.com/emacs-jp/dmacro/blob/master/LICENSE 115 | #+end_example 116 | -------------------------------------------------------------------------------- /dmacro.el: -------------------------------------------------------------------------------- 1 | ;;; dmacro.el --- Repeated detection and execution of key operation -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 1993 Toshiyuki Masui 4 | 5 | ;; Author: Toshiyuki Masui 6 | ;; Makoto Owada 7 | ;; Eiji Obata 8 | ;; Nobuyuki Mine 9 | ;; Maintainer: USAMI Kenta 10 | ;; Created: 14 Apr 1993 11 | ;; Version: 2.0 12 | ;; Keywords: convenience 13 | ;; URL: https://github.com/emacs-jp/dmacro 14 | ;; Package-Requires: ((emacs "24.1") (cl-lib "0.6")) 15 | 16 | ;; License: FSFAP 17 | ;; Copying and distribution of this file, with or without modification, 18 | ;; are permitted in any medium without royalty provided the copyright 19 | ;; notice and this notice are preserved. This file is offered as-is, 20 | ;; without any warranty. 21 | 22 | ;;; Commentary: 23 | 24 | ;; Tired of performing the same editing operations again and again? 25 | ;; Using the Dynamic Macro system on GNU Emacs, you can make the system 26 | ;; perform repetitive operations automatically, only by typing the special 27 | ;; key after doing the same operations twice. 28 | 29 | ;; To use this package, simply add belo code to your init.el: 30 | ;; (setq dmacro-key (kbd "C-S-e")) 31 | ;; (dmacro-mode) 32 | 33 | ;; If you want to use `dmacro-mode' in global, you can turn on global one: 34 | ;; (global-dmacro-mode) 35 | 36 | ;; NOTE: If you change `dmacro-key', you need to restart `dmacro-mode' 37 | ;; to reflect the change. 38 | 39 | 40 | ;;; Code: 41 | 42 | (require 'cl-lib) 43 | 44 | (defgroup dmacro nil "Repeated detection and execution of key operation." 45 | :group 'convenient) 46 | 47 | (defcustom dmacro-key (kbd "C-S-e") 48 | "Key sequences for dmacro." 49 | :type 'key-sequence 50 | :group 'dmacro) 51 | 52 | ;;; Functions 53 | 54 | (defvar dmacro--input-keys) 55 | (defvar dmacro--input-subkeys) 56 | 57 | (defalias 'dmacro--user-error 58 | (eval-when-compile (if (fboundp 'user-error) #'user-error #'message))) 59 | 60 | (defun dmacro-get () 61 | "Get repeated sequence." 62 | (let* ((lce (vector last-command-event)) 63 | (keys (vconcat lce lce)) 64 | (rkeys (recent-keys)) arry) 65 | (if (equal keys (cl-subseq rkeys (- (length keys)))) 66 | (progn 67 | (setq dmacro--input-subkeys nil) 68 | dmacro--input-keys) 69 | (setq arry (dmacro-search (cl-subseq rkeys 0 (- (length lce))) lce)) 70 | (if (null arry) 71 | (setq dmacro--input-keys nil) 72 | (let ((s1 (car arry)) 73 | (s2 (cdr arry))) 74 | (setq dmacro--input-keys (vconcat s2 s1)) 75 | (setq dmacro--input-subkeys (if (equal s1 "") nil s1)) 76 | (setq last-kbd-macro dmacro--input-keys) 77 | (if (equal s1 "") dmacro--input-keys s1)))))) 78 | 79 | (defun dmacro-search (array key) 80 | "Search ARRAY for a subsequence matching KEY." 81 | (let* ((arry (reverse array)) 82 | (sptr 1) 83 | (dptr0 (dmacro-array-search (cl-subseq arry 0 sptr) arry sptr)) 84 | (dptr dptr0) 85 | maxptr) 86 | (while (and dptr0 87 | (not (dmacro-array-search key (cl-subseq arry sptr dptr0)))) 88 | (when (= dptr0 sptr) 89 | (setq maxptr sptr)) 90 | (setq sptr (1+ sptr)) 91 | (setq dptr dptr0) 92 | (setq dptr0 (dmacro-array-search (cl-subseq arry 0 sptr) arry sptr))) 93 | (if (null maxptr) 94 | (let ((predict-arry (reverse (cl-subseq arry (1- sptr) dptr)))) 95 | (if (dmacro-array-search key predict-arry) 96 | nil 97 | (cons predict-arry (reverse (cl-subseq arry 0 (1- sptr)))))) 98 | (cons "" (reverse (cl-subseq arry 0 maxptr)))))) 99 | 100 | (defun dmacro-array-search (pat arry &optional start) 101 | "Search pattern `PAT' by `ARRY' from `START'." 102 | (let* ((len (length pat)) 103 | (max (- (length arry) len)) 104 | (p (or start 0)) 105 | found) 106 | (while (and (not found) (<= p max)) 107 | (setq found (equal pat (cl-subseq arry p (+ p len)))) 108 | (unless found (setq p (1+ p)))) 109 | (if found p nil))) 110 | 111 | 112 | ;;; Main 113 | 114 | ;;;###autoload 115 | (defun dmacro-exec () 116 | "Repeated detection and execution of key operation." 117 | (interactive) 118 | (let ((keys (dmacro-get))) 119 | (if keys 120 | (execute-kbd-macro keys) 121 | (dmacro--user-error "There is no repetitive operation")))) 122 | 123 | ;;;###autoload 124 | (define-minor-mode dmacro-mode 125 | "A minor mode for executing dynamic macros." 126 | :group 'dmacro 127 | :lighter " dmac" 128 | :keymap 129 | `((,dmacro-key . dmacro-exec))) 130 | 131 | ;;;###autoload 132 | (define-globalized-minor-mode global-dmacro-mode dmacro-mode 133 | dmacro-mode 134 | :group 'dmacro) 135 | 136 | (provide 'dmacro) 137 | ;;; dmacro.el ends here 138 | -------------------------------------------------------------------------------- /doc/doc.txt: -------------------------------------------------------------------------------- 1 | This file is a backup of original file header comments. 2 | 3 | It has contact information, but the current version of dmacro is 4 | upstream from GitHub, so you can use GitHub's issue tracker or PR 5 | to contact us. 6 | 7 | https://github.com/emacs-jp/dmacro 8 | 9 | This document is not maintained and is for historical reference only. 10 | 11 | -------------------------------------------------- 12 | 13 | ;; dmacro.el - キー操作の繰返し検出 & 実行 14 | 15 | ;; Version 2.0 16 | 17 | ;; 1993 4/14 original idea by 増井俊之@シャープ 18 | ;; implemented by 太和田誠@長岡技科大 19 | ;; refinement by 増井俊之@シャープ 20 | ;; 1995 3/30 modified for Emacs19 by 増井俊之@シャープ 21 | 22 | ;; 2002 3 XEmacs対応 by 小畑英司 obata@suzuki.kuee.kyoto-u.ac.jp 23 | ;; 峰伸行 zah07175@rose.zero.ad.jp 24 | 25 | ;; dmacro.el は、繰り返されるキー操作列から次の操作を予測し実行させる 26 | ;; ためのプログラムです。操作の繰返しの検出とその実行を指令するために 27 | ;; *dmacro-key* で指定する特別の「繰返しキー」を使用します。 28 | 29 | ;; 例えばユーザが 30 | ;; abcabc 31 | ;; と入力した後「繰返しキー」を押すと、dmacro.el は "abc" の入力操作の 32 | ;; 繰返しを検出してそれを実行し、その結果テキストは 33 | ;; abcabcabc 34 | ;; となります。また、 35 | ;; abcdefab 36 | ;; と入力した後「繰返しキー」を押すと、dmacro.el はこれを "abcdef" の 37 | ;; 入力の繰返しと判断し、繰返しの残りの部分を予測実行して "cdef" を入力し、 38 | ;; テキストは 39 | ;; abcdefabcdef 40 | ;; となります。ここでもう一度「繰返しキー」を押すと、"abcdef" の入力 41 | ;; が繰り返されて、テキストは 42 | ;; abcdefabcdefabcdef 43 | ;; となります。 44 | 45 | ;; あらゆるキー操作の繰返しが認識、実行されるため、例えば 46 | ;; line1 47 | ;; line2 48 | ;; line3 49 | ;; line4 50 | ;; というテキストを 51 | ;; % line1 52 | ;; % line2 53 | ;; line3 54 | ;; line4 55 | ;; のように編集した後「繰返しキー」を押すとテキストは 56 | ;; % line1 57 | ;; % line2 58 | ;; % line3 59 | ;; line4 60 | ;; のようになり、その後押すたびに次の行頭に "% "が追加されていきます。 61 | 62 | ;; このような機能は、繰返しパタンの認識によりキーボードマクロを自動的に 63 | ;; 定義していると考えることもできます。キーボードマクロの場合は操作を 64 | ;; 開始する以前にそのことをユーザが認識してマクロを登録する必要があり 65 | ;; ますが、dmacro.el では実際に繰返し操作をしてしまった後でそのことに 66 | ;; 気がついた場合でも「繰返しキー」を押すだけでその操作をまた実行させる 67 | ;; ことができます。またマクロの定義方法(操作の後で「繰返しキー」を押す 68 | ;; だけ)もキーボードマクロの場合(マクロの開始と終了を指定する)に比べて 69 | ;; 単純になっています。 70 | 71 | ;; ● 使用例 72 | 73 | ;; ・文字列置換 74 | 75 | ;; テキスト中の全ての「abc」を「def]に修正する場合を考えてみます。 76 | ;; 「abc」を検索するキー操作は "Ctrl-S a b c ESC" で、これは 77 | ;; "DEL DEL DEL d e f" で「def」に修正することができます。 78 | ;; 引き続き次の「abc」を検索する "Ctrl-S a b c ESC" を入力した後で 79 | ;; 「繰返しキー」を押すと "DEL DEL DEL d e f" が予測実行され、新たに 80 | ;; 検索された「abc」が「def」に修正されます。ここでまた「繰返しキー」 81 | ;; を押すと次の「abc」が「def」に修正されます。 82 | ;; このように「繰返しキー」を押していくことにより順々に文字列を 83 | ;; 置換していくことができます。 84 | 85 | ;; ・罫線によるお絵書き 86 | 87 | ;; 繰返しを含む絵を簡単に書くことができます。例えば、 88 | ;; ─┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐ 89 | ;; └┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘ 90 | ;; のような絵を書きたい場合は、keisen.el などを使って 91 | ;; ─┐┌┐ 92 | ;; └┘ 93 | ;; と書いた後で「繰返し」キーを押すと、 94 | ;; ─┐┌┐ 95 | ;; └┘└┘ 96 | ;; となり、もう一度「繰返しキー」を押すと 97 | ;; ─┐┌┐┌┐ 98 | ;; └┘└┘└┘ 99 | ;; となります。同様に 100 | ;; ┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐┌─┐ 101 | ;; └─┘└─┘└─┘└─┘└─┘└─┘└─┘└─┘ 102 | ;; のような絵も 103 | ;; ┌─┐ ─ 104 | ;; └─┘ 105 | ;; だけ入力した後「繰返しキー」を連続して押すだけで描くことができます。 106 | 107 | ;; ● 繰返し予測の方法 108 | 109 | ;; 入力の繰返しの予測手法はいろいろ考えられますが、dmacro.elでは 110 | ;; 以下のような優先度をもたせています。 111 | 112 | ;; (1) 同じ入力パタンが予測の直前に2度繰返されている場合はそれを 113 | ;; 優先する。繰返しパタンが複数ある場合は長いものを優先する。 114 | 115 | ;; 例えば、「かわいいかわいい」という入力では「かわいい」と 116 | ;; いうパタンが繰り返されたという解釈と、「い」というパタンが 117 | ;; 繰り返されたという解釈の両方が可能ですが、この場合 118 | ;; 「かわいい」を優先します。 119 | 120 | ;; (2) (1)の場合にあてはまらず、直前の入力列がそれ以前の入力列の 121 | ;; 一部になっている場合(直前の入力が のような形に 122 | ;; なっている場合)は、まずを予測し、その次から を予測 123 | ;; する。このときの長いものを優先し、その中ではが短いもの 124 | ;; を優先する。 125 | 126 | ;; 例えば「abracadabra」という入力では、=「abra」が最長なので 127 | ;; =「cadabra」の予測が優先されます。 128 | 129 | ;; ● XEmacs 対応、Super, Hyper, Alt キーの対応について 130 | 131 | ;; この版では XEmacs にも対応しました。 132 | ;; 現在のところ GNU Emacs 18, 19, 20, 21, XEmacs 21 で 133 | ;; 動作することが確認できています。 134 | ;; また従来の dmacro では Super, Hyper, Alt のキー入力を 135 | ;; 正しく扱うことができませんでしたが、このバージョンでは 136 | ;; 扱えるようになっています。 137 | ;; 繰り返しのキーとして *dmacro-key* に Super, Hyper, Alt, Meta 138 | ;; を含めたキーを使うこともできますが、ただしその際は 139 | ;; 以下の注意に従って下さい。 140 | 141 | ;; ● *dmacro-key* の指定 142 | 143 | ;; GNU Emacs の場合 144 | ;; Modifier key として Control のみが使われる場合は "\C-t" のような 145 | ;; 文字列として指定できます。Meta, Super, Hyper, Alt を利用する場合には 146 | ;; それぞれ [?\M-t], [?\s-t], [?\H-t], [?\A-t] のように指定して下さい。 147 | 148 | ;; XEmacs の場合 149 | ;; Meta key を使う場合でも上記のような制限はありません。Super 等を使う 150 | ;; 場合には [(super t)] のように指定して下さい。 151 | 152 | ;; ● 設定方法 153 | 154 | ;; .emacsなどに以下の行を入れて下さい。 155 | 156 | ;; (defconst *dmacro-key* "\C-t" "繰返し指定キー") 157 | ;; (global-set-key *dmacro-key* 'dmacro-exec) 158 | ;; (autoload 'dmacro-exec "dmacro" nil t) 159 | 160 | ;; オリジナルの連絡先: 161 | ;; 増井俊之 162 | ;; シャープ株式会社 ソフトウェア研究所 163 | ;; masui@shpcsl.sharp.co.jp 164 | 165 | ;; 2002/6/3現在の連絡先: 166 | ;; 増井俊之 167 | ;; (株)ソニーコンピュータサイエンス研究所 168 | ;; masui@acm.org 169 | --------------------------------------------------------------------------------