はじめに
この記事は,Emacs 使いに「右クリックしてメニューから選択」的な概念をもたらす selected.el
と helm-selected.el
/counsel-selected.el
の紹介です.Macユーザを前提としていますが,他のプラットフォームでも通用する話です.
簡単な(?)設定をするだけで,作業効率が劇的に向上します.
Note - ガチンコの右クリック実装には@tad3氏の「right-click-context」があります!オススメ!
選択して,実行
マウス操作の「右クリック」はとても便利で,メニューから選択領域に限定したアクションを選択するだけで,所定の処理を簡単かつ高速に行えます.Emacsにおいても,選択領域を引数とするコマンドを容易に発行できますが,各コマンドが独立していてグループ化されていません.「ユーザが領域選択した時だけ発行できるコマンド群」をグループ化し,かつ,簡単な操作でそれらを選ぶことができれば,選択領域に対する追加処理を最小の操作で実行できます.
わかりやすい例では「Emacsバッファ内の言葉をブラウザでググりたい」があります.通常のシーケンスですと,
- 領域指定:
C-<space>
+C-M-<right>
orC-<space>
+<right>
連打 orC-M-<space>
等 - 領域複製:
M-w
- ブラウザを開く(ランチャーから選ぶ,/Applications フォルダから選ぶ,
M-<tab>
でアクティブ化,等) - フォームに単語・文章をペースト:
M-v
- 検索実行:
<ENT>
と5ステップです.こんな複雑な処理を日々,何度も繰り返すのは嫌ですね.
一方,selected.el
(とhelm-selected.el
)を使えば,もっと楽です.
- 領域指定:
C-<space>
+C-M-<right>
orC-<space>
+<right>|C-f
連打 orC-M-<space>
等 - 検索実行:
g
(設定済みのシングルキー)
わずか2ステップです!
前出の処理の2-4が所定のコマンドで自動化されているので,ちょうど「右クリックして実行」の感覚です.「この英単語を辞書で調べたい!」というような,選択領域に対する同様の処理事例がたくさんあるはずです.
selected.el (とhelm, ivy/counsel拡張)の導入
インストール
selected.el
は,MELPAからインストールできます.
Githubから取り寄せる場合は,https://github.com/Kungsgeten/selected.el から.
次に,helm-selected.el
です.これは selected.el
で設定したコマンド群を helm インターフェイスで絞り込む拡張で, Githubで公開しています(拙作).
https://github.com/takaxp/helm-selected
~~MELPA登録は申請中で,しばらく先になりそうです.~~また,記事に合わせて急いで作成したので,まだバグが残っているかもしれません.
(Updated: 2017-12-23) マージされました. Many thanks to purcell!!
(Updated: 2019-05-31) counsel-selected.el を公開しました.
設定
selected.el
まずselected.el
の設定です.以下は一例で,選択領域の処理に紐付けたいコマンドをselected-keymap
に追加していくだけです.また,特定のメジャーモードに限定したコマンドの設定も可能です.
(when (require 'selected nil t)
;; コメントアウト・アンコメントアウト
(define-key selected-keymap (kbd ";") #'comment-dwim)
;; 選択領域の単語数をカウント,ミニバッファに表示
(define-key selected-keymap (kbd "=") #'count-words-region)
;; 選択した関数のヘルプを表示
(define-key selected-keymap (kbd "f") #'describe-function)
;; 選択した変数のヘルプを表示
(define-key selected-keymap (kbd "v") #'describe-variable)
;; 選択した単語・文をググる
(define-key selected-keymap (kbd "g") #'my:google-this)
;; 選択した単語をOSに発声させる
;; 要osx-lib.el (https://github.com/raghavgautam/osx-lib/blob/master/osx-lib.el)
(define-key selected-keymap (kbd "s") #'osx-lib-say-region)
;; 選択領域を評価して結果をミニバッファに表示(結構危険かも...)
(define-key selected-keymap (kbd "e") #'my:eval-region)
;; 英単語を調べて結果を表示
;; 要osx-dictionary.el (https://github.com/xuchunyang/osx-dictionary.el)
(define-key selected-keymap (kbd "w") #'osx-dictionary-search-pointer)
;; 選択した単語を一括変換する
;; 要replace-from-region.el (replace+.elでも良いらしい)
(define-key selected-keymap (kbd "5") #'query-replace-from-region)
;; 実行中止(ただし,selecte.elでは selected-offの紐付けが推奨されている)
(define-key selected-keymap (kbd "q") #'keyboard-quit)
;; メジャーモードがOrg Modeの時,選択領域をテーブルに変換する
(setq selected-org-mode-map (make-sparse-keymap))
(define-key selected-org-mode-map (kbd "t") #'org-table-convert-region)
(selected-global-mode 1))
上記設定に出てくるプライベート関数は次のようなものです.
(defun my:eval-region ()
(interactive)
(when mark-active
(eval-region (region-beginning) (region-end) t)))
;; 要google-this.el (https://github.com/Malabarba/emacs-google-this)
(with-eval-after-load "google-this"
(defun my:google-this ()
"検索確認をスキップして直接検索実行"
(interactive)
(google-this (current-word) t)))
helm-selected.el
ほぼ設定不要で,helm-selected
の発動コマンドを設定するだけです.ただし,次の章で説明しますが,IMEの切り替えをコマンドでできない環境では,ASCIIのシングルキーがIMEに取られてしまうので,その場合は <f5>
やS-<f5>
などを指定しておくべきです.
(with-eval-after-load "selected"
(when (require 'helm-selected nil t)
(define-key selected-keymap (kbd "h") 'helm-selected)))
領域選択後にh
押下すると,helmインターフェイスが出てきますので,希望のコマンドを選んで<ENT>
すればOKです.
selected.el
にたくさんのコマンドを紐付けると,どのキーに何を割り振ったのか忘れてしまいがちです.そんな時にhelm-selected
が役に立ちます.
counsel-selected.el
Ivy/counsel用のインターフェイスを準備しました.helm-selected.el と同じように使えます.
IMEユーザの一手間
selected.el
の最大の欠点は,IMEとの相性が悪いことです.つまり,IMEがオンの時,選択領域に対するコマンドを選択すると,押下キーがバッファにそのまま入力されてしまいます.
これを避ける方法は二つあります(あるいは,毎回IMEを手動で切り替えるか...Orz).
一つは,helm-selected
の発動をASCII以外のキー(S-<f5>
など)に割り当てて,各コマンドはhelm
インターフェイスから選択することです.コマンド実行までのステップが一つ増えますが,右クリック相当の処理1回分と考えれば許容範囲です.
もう一つの方法は,IMEパッチを適用して IMEのオン・オフを制御可能にする方法です.そうすることで,領域を選択し始める時にIMEをオフにして,コマンド発行後にIMEを元に戻すことができます.同制御は,マークのhook
を使って自動化できます.正しく動作すれば,任意のコマンドにシングルキーを割り当てることができ,IMEユーザも selected.el
の本来の性能を引き出せます.
(when (and (fboundp 'mac-get-current-input-source)
(fboundp 'mac-toggle-input-method))
;; IME ON/OFF ステータスフラグ
(defvar my:ime-flag nil)
;; 判定
(defun my:ime-active-p ()
(not (string-match "\\.Roman$" (mac-get-current-input-source))))
;; IME ON
(defun my:ime-on ()
(interactive)
(mac-toggle-input-method t))
;; IME OFF
(defun my:ime-off ()
(interactive)
(mac-toggle-input-method nil))
;; 領域選択開始に合わせて IMEを OFF
(add-hook 'activate-mark-hook
#'(lambda ()
(when (setq my:ime-flag (my:ime-active-p))
(my:ime-off))))
;; 選択解除で IME ONを復帰
(add-hook 'deactivate-mark-hook
#'(lambda ()
(when my:ime-flag
(my:ime-on)))))
IMEパッチの適用については,emacs-25.3 をインラインパッチをあてて使う(OSX)をご参照ください.
おわりに
本記事では,selected.el
とhelm-selected.el
を紹介しました.選択した領域に対する処理は,今回紹介した事例以外にもたくさん存在すると思います.「右クリックして実行」に相当する任意の関数を好みに合わせて設定してみましょう.作業効率が向上すること間違い無しです.
紐付けるべきオススメのコマンドがありましたら,コメント欄へお願いします m(_''_)m
また,hydra使いさんからのツッコミもお待ちしております.
<< [10日目](https://qiita.com/howking/items/bcc4e05bfb16777747fa) ・ [Emacs Advent Calendar 2017](https://qiita.com/advent-calendar/2017/emacs) ・ [11日目](http://kazkn.com/post/2017/emacs-tmux-sh/) >>