dmacro.elのvimスクリプトでの再現に挑戦

前日からの続きでやってみた。

;; 例えばユーザが
;; abcabc
;; と入力した後「繰返しキー」を押すと、dmacro.el は "abc" の入力操作の
;; 繰返しを検出してそれを実行し、その結果テキストは
;; abcabcabc
;; となります。また、
;; abcdefab
;; と入力した後「繰返しキー」を押すと、dmacro.el はこれを "abcdef" の
;; 入力の繰返しと判断し、繰返しの残りの部分を予測実行して "cdef" を入力し、
;; テキストは
;; abcdefabcdef
;; となります。ここでもう一度「繰返しキー」を押すと、"abcdef" の入力
;; が繰り返されて、テキストは
;; abcdefabcdefabcdef
;; となります。


dmacro.el - キー操作の繰返し検出 & 実行
http://pitecan.com/papers/JSSSTDmacro/dmacro.el

いろいろ試したらdmacro.elの↑の説明の機能は、↓のようなスクリプトを組んだらできるようになった。
下のソースをdmacro.vimという名前で保存、vimエディタのpluginディレクトリに入れておくと、
入力中にCtrl-Bで繰り返しを検出、補完できる。

" vim:set et ff=unix fenc=cp932 :
scriptencoding cp932

" [CASE A]
" 例えばユーザが
"     abcabc
" と入力した後「繰返しキー」を押すと、dmacro.el は "abc" の入力操作の
" 繰返しを検出してそれを実行し、その結果テキストは
"     abcabcabc
" となります。
"
" [CASE B]
" また、
"     abcdefab
" と入力した後「繰返しキー」を押すと、dmacro.el はこれを "abcdef" の
" 入力の繰返しと判断し、繰返しの残りの部分を予測実行して "cdef" を入力し、
" テキストは
"     abcdefabcdef
" となります。
"
" [CASE C]
" ここでもう一度「繰返しキー」を押すと、"abcdef" の入力
" が繰り返されて、テキストは
"     abcdefabcdefabcdef
" となります。

if exists("g:loaded_dmacro")
    finish
endif
let g:loaded_dmacro = 1

let s:save_cpo = &cpo
set cpo&vim

" 入力中に<Ctrl-B>で機能を呼び出します。
imap <C-B> <C-O>:call Dmacro()<CR>

let g:dmacro_register = ""

function! Dmacro()
    " 入力が無い場合は前回の入力を使用する。
    " これはimapで一時的に入力を抜けてしまうので、
    " @.レジスタがクリアされてしまうためだ。
"    echo "@. [". @. ."], dr [". g:dmacro_register ."]"
"    sleep 2
    if @. != ""
        let g:dmacro_register = @.
    endif

    " [CASE A]
    " [CASE C]
    " 正規表現による繰り返しの単純検出。
    let l:matched = match(g:dmacro_register, '\(..*\)\(\1\)$')
    if l:matched > -1
        let l:token = substitute(g:dmacro_register, '.*\(..*\)\(\1\)$', '\1', "")
        let g:dmacro_register = g:dmacro_register . l:token
        silent execute ":normal a" . l:token
        " @.レジスタをクリア。
        " 入力が残ったり残らなかったりするため、挙動を統一させる。
        " @.は読み取り専用なので、データを残す方での統一は無理。
        silent execute ":normal a"
        return
    endif

    " [CASE B]
    " 並びをひっくり返した後、正規表現で不完全な繰り返しを検出する。
    " reverseするのは、より長い文字列を最後の入力とマッチさせるため。
    let l:reversed = join(reverse(split(g:dmacro_register, '\zs')), "")
    let l:matched = match(l:reversed, '^\(..*\)\(.\{-\}\)\1')
    if l:matched > -1
        let l:token = substitute(l:reversed, '^\(..*\)\(.\{-\}\)\1.*', '\2', "")
        let l:token = join(reverse(split(l:token, '\zs')), "")
        let g:dmacro_register = g:dmacro_register . l:token
        silent execute ":normal a" . l:token
        " @.レジスタをクリア。
        silent execute ":normal a"
        return
    endif

    :echohl WarningMsg | echo "操作の繰返しが見つかりません" | echohl None
endfunction

let &cpo = s:save_cpo
finish


しかし、dmacro.elの繰り返し機能はもうちょっとすごくて、
検索操作とかも含めた繰り返しの検出までできるらしい。

;; ・文字列置換
;;
;; テキスト中の全ての「abc」を「def]に修正する場合を考えてみます。
;; 「abc」を検索するキー操作は "Ctrl-S a b c ESC" で、これは
;; "DEL DEL DEL d e f" で「def」に修正することができます。
;; 引き続き次の「abc」を検索する "Ctrl-S a b c ESC" を入力した後で
;; 「繰返しキー」を押すと "DEL DEL DEL d e f" が予測実行され、新たに
;; 検索された「abc」が「def」に修正されます。ここでまた「繰返しキー」
;; を押すと次の「abc」が「def」に修正されます。
;; このように「繰返しキー」を押していくことにより順々に文字列を
;; 置換していくことができます。


dmacro.el - キー操作の繰返し検出 & 実行
http://pitecan.com/papers/JSSSTDmacro/dmacro.el

この機能の再現は難しいな。。