2011-03-26

Dired で Spotlight コメントの表示、編集、タグ付け

Mac OS X では Spotlight コメント (Finder コメント) にタグを付けてスマートフォルダでファイルの管理ができます。

Spotlight コメントは Finder の「情報を見る」から編集できますが、Emacs 使いなら Dired でマークして一括タグ追加、削除、等と楽がしたいものです。

そんな Elisp パッケージをグーグル検索しても見つからなかったので、自分で書いてみました。

spotlight-comment.el
https://gist.github.com/888153

動作条件

GNU Emacs 22 と 23。 Mac OS X 10.4 以上。

インストール

上の gist から raw をクリックして spotlight-comment.el をダウンロードする。
load-path の通った場所へ置き、以下を ~/.emacs.el~/.emacs.d/init.el に追加。

(require 'spotlight-comment)
(define-key dired-mode-map "\M-c" spotlight-comment-dired-prefix-map)

使い方

キーバインドは何でもいいのですが M-c を例に使えるコマンドを説明すると、

M-c y
Spotlight コメントを表示。 C-u 付きでキルリングにコピー。
M-c e
Spotlight コメントを編集。
M-c D
マークしたファイルの Spotlight コメントを削除。
M-c s
マークしたファイルの Spotlight コメントを上書き。
M-c a
マークしたファイルの Spotlight コメントにタグを追加。
M-c d
マークしたファイルの Spotlight コメントからタグを削除。
M-c m
Spotlight コメントにタグが含まれるファイルにマークを付ける。

その他

  • タグ入力する際には、 TAB での補完が効きます。
    補完候補はミニバッファの履歴と変数 spotlight-comment-user-tags に設定してあるタグです。
    空欄のまま RET でタグ入力が終了します。
  • タグの区切り文字は変数 spotlight-comment-separator で設定し、デフォルトでは ", " (カンマとスペース)。
  • Spotlight コメントは全て Finder によって AppleScript 経由で書き換えられます。
    関数 do-applescript を使っていないので X11版 Emacs や Cocoa Emacs の -nw 起動でも使えるはずです。
  • 設定したタグは Spotlight の検索欄から comment:MyTag で MyTag を持つファイルを表示できます。


動作確認: GNU Emacs 23 (Mac port), Mac OS X 10.6

2011-03-22

Emacs Mac port に追加されている機能

Emacs Mac port についてはこちら Emacs Mac port を使う

Emacs Mac port に追加されている機能ですが、パッチに含まれる README-mac に詳細が書いてあります。
ここではその一部をまとめてみました。

パッチのバージョン emacs-23.3-mac-1.9992 を元に書いています。

  • 変数 window-system の値は mac
  • Command-Control-D でカーソル位置の単語の意味を辞書で表示
  • AppleEvent に対応
    Emacs に設定してあるメーラーが開く。 QuickCursor も使えるらしい
    osascript -e 'tell application "Emacs" to open location "mailto:foo@example.com"'
    
  • Emacs がビジーな時は、タイトルバー右側のインジケータがグルグル回る
  • fullscreen フレイムパラメータが認識される
    (set-frame-parameter nil 'fullscreen 'fullboth) でメニューバーと Dock が消えてフルスクリーンに!
    こんなコマンドを作って手軽に切り替えられるようにしてます
    (defun my-toggle-fullscreen ()
      "Toggle fullscreen."
      (interactive)
      (if (eq (frame-parameter nil 'fullscreen) 'fullboth)
          (progn
            (set-frame-parameter nil 'fullscreen nil)
            (display-time-mode 0))
        (set-frame-parameter nil 'fullscreen 'fullboth)
        (display-time-mode 1)))
    
    (global-set-key "\C-cf" 'my-toggle-fullscreen)
    
  • sticky フレイムパラメータが認識される
    (set-frame-parameter nil 'sticky t) で Spaces で全てのスクリーンに表示されるようになる
  • 関数 system-move-file-to-trash が定義されている
    変数 delete-by-moving-to-trash に設定することで、 Emacs からファイルを消去する際にゴミ箱に移動するようになる
    (setq delete-by-moving-to-trash 'system-move-file-to-trash) としてしまうと一時ファイル等でゴミ箱が溢れるので、 Dired 等特定のコマンドでのみ有効にするのが良いかもしれない
    ;; Dired の x や D でゴミ箱に捨てる。Finder での「取り消し」「戻す」は不可。
    (when (fboundp 'system-move-file-to-trash)
      (defadvice dired-do-flagged-delete
        (around move-to-trash activate)
        "Use `system-move-file-to-trash'."
        (let ((delete-by-moving-to-trash 'system-move-file-to-trash))
          ad-do-it))
    
      (defadvice dired-do-delete
        (around move-to-trash activate)
        "Use `system-move-file-to-trash'."
        (let ((delete-by-moving-to-trash 'system-move-file-to-trash))
          ad-do-it)))
    
  • M-x menu-bar-open でメニューバーが開く (menu-bar-mode が non-nil の場合)
    (global-set-key "\M-`" 'menu-bar-open) ; tmm-menubar
    
  • mac-mouse-wheel-mode. 行単位のカクついたスクロールではなく、ピクセル単位の滑らかなスクロール
  • トラックパッドのジェスチャーを認識する。 magnify-up, magnify-down, rotate-left, rotate-right
    デフォルトではテキストスケールの変更と、フルスクリーンの切り替えに割り当てられている
  • システム環境設定 → アピアランス の「クリックされた場所にジャンプ」が Emacs でも有効
  • システム環境設定 → キーボード の「キーボードショートカット (キーボードと文字入力)」が Emacs でも有効。
    例えば ^F2 でメニューバーが開く。 ^F4 でウィンドウ切り替え等
  • 他のアプリで画像をコピーした場合、 yank-pop でその画像を挿入できる。(変数 yank-excluded-properties で display を除外してない場合に限る)
  • Mac OS X 10.6 以上で、メニューバーのヘルプの検索語入力欄から info ノードを検索可能
  • Mac OS X 10.6 以上で、他のアプリにて選択したテキストがファイルのパスならば、 Emacs で開くためのコンテキストメニューとサービスが追加される
  • ことえりで、かなキー2回連打でカーソル位置の単語を再変換する機能が Emacs でも有効
  • 関数 mac-convert-property-list の追加
    Mac OS X の plist を lisp 等に変換できる。 plutil や xml.el とかでも似たようなものは実装できそうですが
    使用例としてはこんな感じで
    (mac-convert-property-list
     (let ((coding-system-for-read 'no-conversion))
       (with-temp-buffer
         (insert-file-contents "~/Library/Preferences/.GlobalPreferences.plist")
         (buffer-string))))
    

他に、 SVG やマルチページ TIFF の表示、 関数 mac-file-alias-p の追加、 Option-Command-T で「文字ビューアを表示」、 Resolution independence, 等々。

あと、私の環境では lookup.el で辞書を引くスピードが Cocoa Emacs では異様に遅かったのですが、 Emacs Mac port では軽快に動きました。

その他の注意点

  • mac-allow-anti-aliasing は削除された
    システム環境設定 -> アピアランス で設定するか、Terminal から以下を実行
    defaults write "org.gnu.Emacs" AppleAntiAliasingThreshold 12
  • ことえりの半角英字モードで C-S-a (Shift を押しながらの C-a) が効かなくなることがある。再現性はいまいち

do-applescript 関数について

  • Emacs Mac port の do-applescript は Carbon Emacs のそれと挙動は同じで、数点注意が必要です
    • do-applescript へ渡す文字列、返す文字列は変数 mac-system-coding-system でエンコード、デコードする必要がある
    • 変数 mac-system-coding-system は端末から Emacs を -nw 起動した時には初期化されていない
    • バックスラッシュは全て \200 (0x80) に置き換えられるので変換が必要
    • do-applescript が返す文字列はダブルクオートで囲まれているので、除去する必要がある
  • Cocoa Emacs の do-applescript (ns-do-applescript) は上記の面倒は無いのですが、端末で起動した時には do-applescript 関数は使えません
  • X11 用にビルドした Emacs では do-applescript は恐らく使えません
  • 外部コマンドの osascript を使う場合次の利点があります
    • Emacs Mac port, Carbon Emacs に特有の面倒が無い
    • 端末からの -nw 起動でも X11 版でも AppleScript を使える
    • on run argv で引数渡しができる
    • スクリプトの実行が失敗したかどうかが call-process の返り値でわかる

上記の理由から当ブログで紹介している Dired 関連の tips では do-applescript は使わず osascript を使っています

Emacs Mac port を使う

Emacs Mac port とは

Carbon Emacs (GNU Emacs 22) をベースとして、 GUI 部分に Cocoa API を取り入れ GNU Emacs 23 に対応させたものです。64bit 版もビルドできます。

Carbon Emacs のメインテナーをされていた YAMAMOTO Mitsuharu 氏によってパッチという形で提供されています。
パッチはここで入手できます。
ftp://ftp.math.s.chiba-u.ac.jp/emacs/

Cocoa Emacs (GNU Emacs 23 を –with-ns でビルドしたもの) と比べ、色々と機能が追加されています。
詳しくはパッチに同梱されている README-mac に書いてあります。
Emacs Mac port に追加されている機能 にもまとめてみました。

Emacs Mac port という呼称が適切なのか自信はないですが、 README-mac やソースでは "Mac port", "GNU Emacs Mac port" という名前が使われているので、当ブログでは Emacs Mac port で統一しました。

それにしても、 Mac port と MacPorts が似ていてウェブ検索での情報収集がしにくいです。

Emacs Mac port のインストール

詳しい手順はパッチに含まれる README-mac に載っています。
以下は 2011年3月現在の Emacs 23.3 をビルドする手順です。(Xcode が必要です)

  1. http://www.gnu.org/prep/ftp.html の最寄りのサーバから emacs-23.3.tar.bz2 を落とす。
  2. ftp://ftp.math.s.chiba-u.ac.jp/emacs/ から emacs-23.3-mac-1.9992.tar.gz を落とす。 1.9992 の部分は最新のバージョンで。
  3. ターミナルで
    tar jxf emacs-23.3.tar.bz2
    tar zxf emacs-23.3-mac-1.9992.tar.gz
    cd emacs-23.3
    patch -p0 < ../emacs-23.3-mac-1.9992/patch-mac
    cp -r ../emacs-23.3-mac-1.9992/mac .
    cp ../emacs-23.3-mac-1.9992/src/* ./src/
    cp ../emacs-23.3-mac-1.9992/lisp/term/mac-win.el ./lisp/term/
    ./configure --with-mac --without-x
    make
    sudo make install
    mv ./mac/Emacs.app /Applications/
    

    configure の --prefix オプションによっては sudo make install の部分は make install でも可。
    Cocoa Emacs とは違い、 self-contained ではないので、 --prefix を指定しないかぎり /usr/local 以下にインストールされます。

Emacs Mac port の設定

文字コードの設定

(set-language-environment 'Japanese)
(prefer-coding-system 'utf-8)
;; クリップボードの文字コード
(set-selection-coding-system 'utf-8)
;; 端末の文字コード
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
;; ファイル名の文字コード
(require 'ucs-normalize)
(set-file-name-coding-system 'utf-8-hfs)

Emacs Mac port の場合、クリップボード用の文字コード selection-coding-system がシステムのデフォルト言語で初期化されます。
日本語ならば japanese-shift-jis 。上の例では utf-8 にしてますが、 japanese-shift-jis でも問題は無いです。
ただ、これを japanese-shift-jis 以外の Shift_JIS (例えば sjis, sjis-mac 等) にすると、バックスラッシュが半角の円記号に化けたりと不具合が出ます。関数 mac-string-to-pasteboard-string でその辺りの処理をしています。

フォントの設定

基本的には Cocoa Emacs の設定の仕方と同じです。
Emacs Mac port は Carbon Emacs がベースですが、 carbon-font.el は使えません。

http://macemacsjp.sourceforge.jp/index.php?MacFontSetting より引用

(create-fontset-from-ascii-font "Menlo-14:weight=normal:slant=normal" nil "menlokakugo")
(set-fontset-font "fontset-menlokakugo" 'unicode (font-spec :family "Hiragino Kaku Gothic ProN" ) nil 'append)
(add-to-list 'default-frame-alist '(font . "fontset-menlokakugo"))
(setq face-font-rescale-alist '((".*Hiragino.*" . 1.2) (".*Menlo.*" . 1.0)))
  • アンチエイリアスに関して
    Carbon Emacs にあった変数 mac-allow-anti-aliasing は削除されています。
    システム環境設定のアピアランスで設定するか、Terminal から defaults で設定します。
    defaults write "org.gnu.Emacs" AppleAntiAliasingThreshold 12
    また、関数 font-spec の :antialias プロパティに nil か t をセットすることで、フォント毎の指定もできます。

  • フォント設定に関するバグ
    http://d.hatena.ne.jp/setoryohei/20110113
    3つの条件が重なると、バグが出るらしい。
    当方でも inhibit-startup-screen (inhibit-startup-message, inhibit-splash-screen) を t にしていると、□○△の記号類が半角サイズで表示される等、フォントが正しく設定されないのを確認しました。 Emacs Mac port でも再現性があります。

使えるフォントは *scratch*

(insert (prin1-to-string (x-list-fonts "*")))

これを評価すれば全て表示されます。
Cocoa Emacs よりも太字が使えるフォントが増えていたりと、選択肢が多くなっているはずです。

  • Monaco 10
    Emacs Mac port で Monaco 10 に設定すると、斜体字は太字よりも太くなり、太字はぼやけてしまいます。
    アンチエイリアス無しで Carbon Emacs と同じようなフォント設定にするのは難しいようです。
    ちなみに Cocoa Emacs では Monaco の太字は使えません。
  • M+とIPAの合成フォント
    無料で使えます。 MigMix 1M, MigMix 2M, Migu 1M が等幅フォントで太字も含む。
    http://mix-mplus-ipa.sourceforge.jp/

その他の設定

Emacs Mac port のマウススクロールは非常に滑らかです。が、何らかの事情でマウススクロールを無効にしたい場合はこのようにします。

(add-hook 'window-setup-hook
          (lambda ()
            (when (fboundp 'mac-mouse-wheel-mode)
              (mac-mouse-wheel-mode 0))))

単に ~/.emacs.d/init.el(mac-mouse-wheel-mode 0) と書き込んでも無効にはなりません。
詳しくは mac-win.el の after-init-hook に追加している個所を参照。

2011-03-20

Emacs 22 から Emacs 23 への変更点

GNU Emacs 23.1 は 2009年7月にリリースされましたが、2011年3月 GNU Emacs 23.3 がリリースされたのを機に、重い腰を上げ 22 から移行することにしました。

今さら感がありますが、 22.3 から 23.3 への移行の個人的なメモです。
追加・変更されたメジャーモードについては量が多いので省略しました。

詳しくは C-h n で表示される NEWS に全て書いてあります。


リージョン (選択範囲) 関連

transient-mark-mode
マイナーモード。デフォルトで t になった。
shift-select-mode
マイナーモード。デフォルトで t 。シフト押しながらカーソル移動で範囲選択。
set-mark-default-inactive
変数。デフォルトで nil。 t ならば C-@ 2回で範囲をハイライト。 Emacs 22 では t の挙動がデフォルトだったが、 23 ではその挙動を変える変数として用意された。
select-active-regions
変数。デフォルトで nil 。 t ならば範囲選択するだけでキルリングにコピー。
use-empty-active-region
変数。デフォルトで nil 。 t ならば範囲に対して実行するコマンドが、空の範囲に対しても実行されるようになる。
use-region-p
関数。範囲指定済みなら t を返す。空の範囲の場合は use-empty-active-region の値に依存。
region-active-p
関数。範囲指定済みなら t を返す。
(interactive "^")
interactive で ^ を指定できるようになった。 shift-select-mode が non-nil の場合、関数 handle-shift-selection を呼ぶ。
^ が使われている C-M-f (forward-sexp) を例にすると、以下の4パターンで範囲の変化の仕方に違いが出る。
  • 範囲を選択してから C-M-f (範囲を拡大する)
  • 範囲を選択してから shift + C-M-f (範囲の開始位置を変更)
  • shift + カーソルで範囲を選択してから C-M-f (範囲を無効にする)
  • shift + カーソルで範囲を選択してから shift + C-M-f (範囲を拡大する)
C-@ C-@
2回で、 Mark, Deactivate mark の繰り返し
範囲選択してからTAB
indent-region

ヘルプ関連

help-window-select
変数。デフォルトで 'other。 'always (t) ならば *Help* 表示時に常にウィンドウを選択する。
help-downcase-arguments
変数。デフォルトで nil。 t ならば *Help* の引数を以前と同じ小文字表記にする。
help-go-forward
コマンド。 [back] で戻った時に [forward] で進む。
This variable is potentially risky when used as a file local variable.
ファイルローカル変数として危険かどうかを表示するようになった。
ファイルローカル変数とはファイルの1行目の -*- 変数: 値 -*- や、最後の Local Variables: 等のこと。
This variable was introduced, or its default value was changed, in version 23.1 of Emacs.
変数が追加・変更された Emacs のバージョンを表示するようになった。
これは defcustom で :version プロパティを追加した変数のみ有効。
例:
(defcustom select-active-regions nil
  "If non-nil, an active region automatically sets the primary selection."
  :type 'boolean
  :group 'killing
  :version "23.1")
;; 現在読み込み済の (intern された) シンボルで、 defcustom が :version
;; プロパティを付加したものを列挙する。下の例では 23 から始まるものだけを抽出。
(let (symbols version)
  (mapatoms
   (lambda (x)
     (setq version (get x 'custom-version))
     (when (and version
                (string-match "^23" version))
       (setq symbols (cons x symbols)))))
  (insert (mapconcat 'symbol-name (sort symbols 'string<) "\n")))

Dired 関連

M-s f C-s
dired-isearch-filenames
M-s f C-M-s
dired-isearch-filenames-regexp
M-s a C-s
dired-do-isearch. buffer-menu や ibuffer でも似た挙動。
M-s a C-M-s
dired-do-isearch-regexp. buffer-menu や ibuffer でも似た挙動。
C-x C-q
wdired
その他
Dired からシェルコマンドを実行 (!, &) する時に、候補 (guess) を M-n で選ぶようになった。

追加されたマイナーモード

goto-address-mode
デフォルトでオフ。バッファの URL をボタン化する。
minibuffer-depth-indicate-mode
デフォルトでオフ。 minibuffer の深度を表示する。 enable-recursive-minibuffers が non-nil の場合のみ有効。
visual-line-mode, global-visual-line-mode
デフォルトでオフ。 C-a, C-e が見た目の行で実行される。オンにした時、フリンジの矢印を消さずに元のままにする場合は、変数 visual-line-fringe-indicators の値を '(left-curly-arrow right-curly-arrow) にする。
whitespace-mode, global-whitespace-mode
デフォルトでオフ。タブやスペースの視覚化等。以前のものを一新したらしい。

コマンド

M-s o
occur
M-s w
isearch-forward-word
M-s h f
hi-lock-find-patterns
M-s h l
highlight-lines-matching-regexp
M-s h p
highlight-phrase
M-s h r
highlight-regexp
M-s h u
unhighlight-regexp
M-s h w
hi-lock-write-interactive-patterns
isearch 中に M-s o
occur に切り替える。
minibuffer で isearch
履歴を検索する (minibuf-isearch.el みたいなもの)。
completion-at-point (M-TAB)
カーソル位置の単語を補完。
C-l, M-r
連打で middle, top, bottom と変化するようになった。 変数 recenter-positions で順番を指定。
async-shell-command (M-&)
非同期(バックグラウンド)でシェルコマンドを実行。 M-! で末尾に & を付加したものと同じ。
zrgrep
再帰的に gzip ファイルの中身を grep。
emacs-uptime
Emacsの稼働時間を返す。
emacs-init-time
Emacsの起動にかかった時間を返す。
display-time-world
世界の時刻を表示。
butterfly
バタフライ効果でHDDにビットを立てる。 http://xkcd.com/378/

変数 (ユーザーオプション)

line-move-visual
デフォルトで t。C-n, C-p が見た目の行で実行される。
word-wrap
デフォルトで nil。t ならば単語単位で折り返しをする。スペース区切りなので日本語ではあまり効果がない。
initial-buffer-choice
デフォルトで nil。Emacs 起動時のバッファを指定。
user-emacs-directory
デフォルトは ~/.emacs.d/
yank-pop-change-selection
デフォルトで nil。 t ならば yank-pop した時に他アプリのコピーバッファを同期させる。
save-interprogram-paste-before-kill
デフォルトで nil。 t ならば yank だけでなく kill した時にも他アプリのコピーバッファを kill-ring に保存する。
kill-do-not-save-duplicates
デフォルトで nil。 t ならば同じ文字列を連続でコピーした場合、重複を保存しない。
tab-always-indent
デフォルトで t。 complete を指定できるようになった。
completions-format
デフォルトで nil。 vertical ならば *Completions* の並び方が縦に。

Elisp 関連

内部コード
utf-8-emacs に変更。
display-buffer
コマンド。 split-window-preferred-function を使うようになった。
split-window-preferred-function
変数。デフォルトで split-window-sensibly 。
split-height-threshold
変数。デフォルトで 80。nil またはフレームの高さがこの値より小さい場合、 split-window-sensibly は上下分割しない。
split-width-threshold
変数。デフォルトで 160。nil またはフレームの幅がこの値より小さい場合、 split-window-sensibly は左右分割しない。
read-shell-command
関数。ミニバッファで外部プログラムを読み取る。補完が効く。 M-!, M-&, M-| や、Dired の !, & 等で補完が効くようになったのも、この関数のおかげ。
process-lines
関数。外部プログラムを実行し、出力の一行ずつをリストにして返す。
initial-environment
変数。環境変数の初期値。
check-coding-systems-region
関数。文字列または範囲の文字コードをチェック。

2011-03-03

Mac OS X 上の Emacs で M-x doctor に音声でしゃべらせる

Mac OS X には say というテキストを読み上げるコマンドがあります。
それを M-x doctor で利用すれば実際に声でしゃべってくれます。

;; M-x doctor
(defadvice doctor-read-print
  (after say activate)
  "Let the psychotherapist speak with audio voice."
  (start-process "say" nil "say" "-v" "Agnes"
                 (save-excursion
                   (buffer-substring-no-properties
                    (progn
                      (re-search-backward "\n\n" nil t 2)
                      (forward-char 2)
                      (point))
                    (progn
                      (re-search-forward "\n\n" nil t)
                      (forward-char -2)
                      (point))))))

doctor-mode のヘルプによると doctor の名前が Eliza (女性)なので、声は女性の Agnes を選んでみました。
声は他に Alex, Trinoids, Zarvox, Hysterical, Bad News, Good News 等があり、システム環境設定のスピーチで確認できます。

声を Hysterical にするとどちらが医者なのかわからなくなります。
リターン連打で何人もの医者から詰問を受けている気分になります。

いずれにせよ鬱陶しさ3割増といったところでしょうか。

2011-03-02

replace-regexp に入力する手間を省く replace-multi-pairs

Emacs で M-x replace-string や M-x replace-regexp はテキスト編集では日常的に使います。
しかし、複雑な正規表現や、同一の検索置換を何度か実行していると一つ不満が出てきました。

検索文字列と置換文字列を毎回入力せねばならない。

ミニバッファの履歴を辿ることもできますが、検索文字列と置換文字列で履歴を共有していたりと微妙な面倒くささがあります。

よく使う検索文字列と置換文字列の組み合わせを保存しておいて、一発で呼び出したい。できれば複数の検索置換を一回で済ませたい。

そこで書いたものが
replace-multi-pairs
(defgroup replace-multi-pairs nil
  "Replace multiple pairs of strings."
  :group 'editing)

(defcustom replace-multi-pairs-alist
  '( ;; default pairs
    ("default"
     ("[ \t]+$" "" t))
    ;; Text to XML, HTML.
    ;; Similar to M-x sgml-quote
    ("html-quote"
     ("&"   "&amp;")
     ("<"   "&lt;")
     (">"   "&gt;")
     ("'"   "&apos;")
     ("\""  "&quot;"))
    ;; XML, HTML to Text.
    ;; Similar to C-u M-x sgml-quote
    ("html-unquote"
     ("&lt;"        "<")
     ("&gt;"        ">")
     ("&apos;"      "'")
     ("&quot;"      "\"")
     ("&nbsp;"      " ")
     ("&amp;"       "&"))
    ;; <br />
    ("br"
     ("\n" "<br />\n"))
    ;; Flush blank lines.
    ;; Similar to M-x delete-blank-lines (C-x C-o)
    ("no-blank"
     ("^[ \t]*\n" "" t))
    ;; Delete spaces and tabs at the end of line.
    ;; Similar to M-x delete-trailing-whitespace
    ("no-trailing"
     ("[ \t]+$" "" t))
    ;; Delete indentations.
    ;; Same as C-u 0 M-x indent-region (C-M-\)
    ("no-indent"
     ("^[ \t]+" "" t))
    ;; Delete line breaks. Like M-x join-line
    ;;     )
    ;;   )
    ;; )
    ;; -->
    ;;     )))
    ("no-break"
     ("[ \t]*\n[ \t]*" "" t))
    ;; Replace consecutive spaces and tabs with just one space.
    ;; Similar to M-x just-one-space (M-SPC)
    ("one-space"
     ("[ \t]+" " " t))
    ;; Extract URLs and delete other lines. For HTML source, etc.
    ("url"
     ("^" " " t)
     ("\\([^h]\\)ttp\\(s?\\)://" "\\1http\\2://" t)
     ("\\([^ft]\\)tp\\(s?\\)://" "\\1http\\2://" t)
     ("h?[ft]tps?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+" "\n\\&\n " t)
     ("^hftp" " " t)
     ("^ .*$" "" t)
     ("^\n" "" t)
     ("'$" "" t))                       ; not correct. for HTML source
    ;; \ -> \\ and " -> \"
    ("quote-string"
     ("\\\\\\|\"" "\\\\\\&" t))
    ;; \" -> " and \\ -> \
    ("unquote-string"
     ("\\\"" "\"")
     ("\\\\" "\\"))
    ;; a "b" \c -> "a \"b\" \\c\n"
    ("quote-lines"
     ("\\\\\\|\"" "\\\\\\&" t)
     (".*" "\"\\&\\\\n\"" t))
    ;; "0001: "
    ;; "0002: "
    ;; "0003: "
    ("number-lines"
     ("^" "\\,(format \"%04d: \" (1+ \\#))" t))
    ;; M-: (regexp-quote "")
    ("regexp-quote"
     (".+" "\\,(regexp-quote \\&)" t))
    ;; 全角英数のみ半角に
    ;; C-u C-x japanese-hankaku-region では ー が - に変換される
    ("hankaku"
     ("\\cA" "\\,(japanese-hankaku \\& t)" t)))

  "Alist of pairs of search string and replacement.
Each element looks like:

\(KEY (SEARCH-STRING REPLACEMENT REGEXP-FLAG DELIMITED-FLAG) ...)

KEY is a string which is a name of pairs.

SEARCH-STRING and REPLACEMENT are strings as described in
`replace-string' and `replace-regexp'. REPLACEMENT may contain
`\\,' followed by a Lisp expression and some other letters
that `replace-regexp' accepts in interactive calls.

When REGEXP-FLAG is non-nil, SEARCH-STRING and REPLACEMENT
are treated as regexp.
DELIMITED-FLAG is as in `replace-string' and `replace-regexp'."
  :type '(alist :key-type (string :tag "Key")
                :value-type
                (repeat :tag "Pairs of search string and replacement"
                        (group
                         (string  :tag "Search string")
                         (string  :tag "Replacement")
                         (boolean :tag "Regexp flag")
                         (boolean :tag "Delimited flag"))))
  :group 'replace-multi-pairs)

(defcustom replace-multi-pairs-require-prefix nil
  "Non-nil means prompt for key with \\[universal-argument].
If nil, always prompt unless \\[universal-argument] is given.

Without prompt, the first element of `replace-multi-pairs-alist'
is used as key."
  :type 'boolean
  :group 'replace-multi-pairs)

(defvar replace-multi-pairs-history nil
  "History list for `replace-multi-pairs'.")

(defun replace-multi-pairs-count ()
  (let ((message (current-message)))
    (if (and message
             (string-match "^Replaced \\([0-9]+\\) " message))
        (string-to-number (match-string 1 message))
      0)))

(defun replace-multi-pairs-1 (beg end key)
  "Perform replacement starting from BEG to END using KEY.
Return the total number of replaced occurrences."
  (let ((limit (copy-marker (max beg end)))
        (count 0)
        (orig-push-mark (symbol-function 'push-mark))
        (orig-undo-boundary (symbol-function 'undo-boundary))
        (orig-deactivate-mark (symbol-function 'deactivate-mark)))
    (fset 'push-mark (symbol-function 'ignore))
    (fset 'undo-boundary (symbol-function 'ignore))
    (fset 'deactivate-mark (symbol-function 'ignore))
    (unwind-protect
        (dolist (args (cdr (assoc key replace-multi-pairs-alist)) count)
          (unless args
            (error "Bad key: %s" key))
          (perform-replace (nth 0 args)
                           (query-replace-compile-replacement
                            (nth 1 args) (nth 2 args))
                           nil (nth 2 args) (nth 3 args)
                           nil nil (min beg end) limit)
          (setq count (+ count (replace-multi-pairs-count))))
      (fset 'push-mark orig-push-mark)
      (fset 'undo-boundary orig-undo-boundary)
      (fset 'deactivate-mark orig-deactivate-mark))))

(defun replace-multi-pairs-read ()
  (let ((completion-ignore-case t)
        (default (or (car replace-multi-pairs-history)
                     (caar replace-multi-pairs-alist))))
    (completing-read
     (format "Which regexp? (default %s): " default)
     replace-multi-pairs-alist nil t nil
     'replace-multi-pairs-history default)))

(defun replace-multi-pairs (beg end &optional key)
  "Replace text between BEG and END using KEY.
KEY is one of keys of `replace-multi-pairs-alist', which defines
multiple pairs of search string and replacement.

In interactive calls, when the region is active, operate on the region;
otherwise operate from the current point to the end of the buffer.

Prompt for KEY depending on the prefix argument and the value of
`replace-multi-pairs-require-prefix'.
If KEY is not specified, the first element of
`replace-multi-pairs-alist' is used."
  (interactive
   (let (beg end key)
     (barf-if-buffer-read-only)
     (when (or (and replace-multi-pairs-require-prefix
                    current-prefix-arg)
               (and (null replace-multi-pairs-require-prefix)
                    (null current-prefix-arg)))
       (setq key (replace-multi-pairs-read)))
     (if (and transient-mark-mode mark-active)
         (setq beg (region-beginning)
               end (region-end))
       (setq beg (point)
             end (point-max)))
     (list beg end key)))
  (let (count)
    (setq key (or key (caar replace-multi-pairs-alist)))
    (push-mark)
    (when (and transient-mark-mode mark-active)
      (deactivate-mark))
    (setq count (replace-multi-pairs-1 beg end key))
    (message "Replaced %d occurrence%s in total (%s)"
             count (if (= 1 count) "" "s") key)))

;; (global-set-key "\C-cr" 'replace-multi-pairs)


動作確認:GNU Emacs 22

<使い方>
キーに設定した文字列で置換を行うには、範囲選択して
M-x replace-multi-pairs RET キー RET

キーはタブで補完が効き、変数 replace-multi-pairs-alist で設定します。
replace-multi-pairs-alist は M-x customize の Editing からでも編集できます。
replace-multi-pairs-alist を直接編集する場合、各要素は以下のような構成になります。

("キー"
 ("検索文字列1" "置換文字列1" 正規表現フラグ ワード区切りフラグ)
 ("検索文字列2" "置換文字列2" 正規表現フラグ ワード区切りフラグ)
 ...)

・キーは任意の文字列です。
・検索文字列と置換文字列は replace-regexp, replace-string で使う書式がそのまま使えます。
・正規表現フラグは non-nil ならば、 replace-regexp と同じ動作。nil または省略されたならば、 replace-string。
・ワード区切りフラグは non-nil ならば C-u 付きで replace-regexp, replace-string を実行した時と同じ挙動に。

default と html-quote を設定した例。
(setq replace-multi-pairs-alist
      '(("default"
         ("[ \t]+$" "" t))
        ("html-quote"
         ("&"   "&amp;")
         ("<"   "&lt;")
         (">"   "&gt;")
         ("'"   "&apos;")
         ("\""  "&quot;"))))
default は正規表現で行末のスペースとタブを削除。
M-x replace-multi-pairs RET default RET
html-quote は & 等をHTMLタグに一括置換。
M-x replace-multi-pairs RET html-quote RET

replace-multi-pairs-alist の初期値として色々設定してあるので、参考程度にはなるかと思います。






以下余談。

replace-multi-pairs では検索置換する関数として perform-replace を使っています。
perform-replace は query-replace, query-replace-regex, replace-string, replace-regexp の下請け関数です。
ヘルプにはこう書いてあります。
Subroutine of `query-replace'.  Its complexity handles interactive queries.
Don't use this in your own program unless you want to query and set the mark
just as `query-replace' does.  Instead, write a simple loop like this:

  (while (re-search-forward "foo[ \t]+bar" nil t)
    (replace-match "foobar" nil nil))

which will run faster and probably do exactly what you want.  Please
see the documentation of `replace-match' to find out how to simulate
`case-replace'.

要約すると、自作のプログラムからはこの関数を使わずに、 re-search-forward のループを書きなさい、ということ。
しかし、それを承知した上で replace-multi-pairs では perform-replace を使ってます。
理由は replace-regexp の検索文字列と置換文字列の書式がそのまま使えるからです。

例えば行末に foobar と付け加えるためこのようなコマンドを使います。
M-x replace-regexp RET $ RET foobar RET

それと同じことをしようとして、以下のようなコードを書くと無限ループになります。

(while (re-search-forward "$" nil t)
  (replace-match "foobar" nil nil))

同様に、全角英数を半角英数に変換する場合、 \, を使った Lisp の式を書けますが、
M-x replace-regexp RET \cA RET \,(japanese-hankaku \& t) RET

それと同じことをしようとしても、意図した通りには動きません。

(while (re-search-forward "\\cA" nil t)
  (replace-match "\\,(japanese-hankaku \\& t)" nil nil))

ただ perform-replace を使う注意点として、その関数から呼ばれる push-mark, undo-boundary, deactivate-mark の3つの関数は、実行されると不自然な挙動になるので、一時的に ignore に置き換えています。