2023年4月15日
multiple-cursors.el は、複数のカーソルによる編集機能を提供するEmacs パッケージです。これにより、単語、フレーズに複数のカーソルを出現させ、それら全てに対して同時にアクションを実行することができます。一度に数行のテキストを編集したり、コード内の変数、関数名を一度に書き換えたりするために使用できます。
こちらが完成済みのコードです。
(use-package multiple-cursors
:ensure t
:bind (
;; カーソルを当てているキーワードと同じキーワードにカーソルを複製する
;; リージョンが選ばれている場合は、選ばれたキーワードと同じ場所にカーソルを複製する
("C-." . mc/mark-next-like-this-symbol)
("C-," . mc/mark-previous-like-this-symbol)
;; カーソルを1行下または上に複製する
;; リージョンが選ばれている場合は、厳密にそのキーワードとマッチする箇所にカーソルを複製する
("C-c C-." . mc/mark-next-symbol-like-this)
("C-c C-," . mc/mark-previous-symbol-like-this)
;; カーソルを当てているキーワードで、同一ファイル上全ての場所にカーソルを複製する
;; リージョンが選ばれている場合は、選ばれたキーワードと同じ箇所全てにカーソルを複製する
("C-c C-;" . mc/mark-all-like-this-dwim)
))
使い方
このサンプルコードを編集するという体で説明します。
def replace_file_string(filename, oldstring, newstring):
with open(filename, 'r') as f:
contents = f.read()
contents = contents.replace(oldstring, newstring)
with open(filename, 'w') as f:
f.write(contents)
replace_file_string('file.txt', 'old', 'new')
コードの
contents
変数を別のものに変更してみましょう。一番最初のcontents
変数にカーソルを合わせた後、("C-." mc/mark-next-like-this-symbol)
で同じキーワードをにカーソルを置いてゆきます。この状態では全てのカーソルでリージョン選択状態になっています。
普通のカーソルとして、操作をしたい場合は1度
C-g
で選択モードを解除します。contents
を適当にabc
みたいな変数に変更してみます。バックスペースで文字を消して、最後にabc
とタイプすれば全ての変数が変換されます。マルチカーソルを解除したい場合は
C-g
でできます。注意キャメルケースをうまく認知できない
このカーソル複製方法は、キャメルケースによる変数の違いをうまく認知できません。
コードをこのように変更してみます。
def replace_file_string(filename, oldstring, newstring):
with open(filename, 'r') as f:
contents = f.read()
contentsEdited = contents.replace(oldstring, newstring)
with open(filename, 'w') as f:
f.write(contentsEdited)
replace_file_string('file.txt', 'old', 'new')
下の画像のように、
contents
とcontentsEdited
は別の変数であるにも関わらず、どちらもcontents
が含まれているため、カーソルが複製されてしまいます。より厳密にカーソルを複製
上で利用した
mark-next-like-this-symbol
はざっくりとしたキーワードの範囲でカーソルを複製します。そうではなく、リージョンで選んだ範囲でマッチするものにカーソルを複製する方法もあります。
contents
に正確にマッチする場所にカーソルを複製してみましょう。一番上のcontents
変数をC-SPC
で選択して、("C-c C-." . mc/mark-next-symbol-like-this)
でカーソルを複製します。一気にカーソルを複製する
1つ1つ
C-.
でカーソルを増やすのが面倒な場合は、カーソルを複製したいキーワードにカーソルを当てて、"C-c C-." mc/mark-all-like-this-dwim
一括選択できます。注意としましてはバッファ内全てのキーワードにカーソルを複製するので、もしかすると意図しない場所にもカーソルが生まれているかもしれません。あとは同じように一度にカーソルを操作できます。メインカーソルを移動する
カーソルが複製されている場所が、必ずしも、現在見えているバッファの中だけに収まる訳ではありません。正しい場所にカーソルが複製できるかチェックするためにも移動はできると良いしょう。メインのカーソルは複製を始めたところにあります。私のテーマでは灰色になっているカーソルがメインで、黒色が複製されたカーソルです。メインのカーソルを次の候補に移動させるには
C-v
前に戻るにはM-v
で移動できます。マルチカーソルでよく使うパターン
例えば、マークダウンを書いていて、このようなリストがあるとします。
- 項目1
- サブ項目1
- サブ項目2
- サブ項目3
- サブ項目4
一連の
サブ項目
をトップの階層に移動させたい場合、普通に操作すると、各行のインデントを削除していく必要があります。multiple-cursorsはこれをうまい具合にやってくれます。カーソルを
- サブ項目1
の -
に合わせて、("C-c C-." mc/mark-next-symbol-like-this)
を実行します。このコマンドははリージョンを選択していない場合は、現在のカーソルの1つ下にカーソルを複製してくれます。ババっとこんな感じにカーソルを複製します。そのまま、バックスペースをタイプすると、良い感じに階層を上に移動できます。
まとめ
カーソル複製の操作は正直複雑です。このブログを書いているときも思ったような複製が出来なくて色々試していました。もしも操作に迷った時は上のサンプルコードのコメントを参考にしてください。
Ted
大学でコンセンサスアルゴリズムを研究。卒業後ベンチャー企業に入社してフルスタックでWebサービスを開発。現在は大手IT企業に転職し、プログラミングを行っている。AIにプログラマーの仕事を奪って欲しいと願っている。