カビパン男と私

HOME/横書き縦書き

micro エディタで Emacs 風のマーク&リージョン

micro は楽しいエディタだ。まず、なにしろ軽快である。打鍵した文字がすぐ画面に 出てくることでは随一の速さだ。シェルとの連携も簡単だ。連続した複数のアクションに 対して一つのキーバインディングを割り当てたりもできる。Lua を使ってユーザーが拡張 を書くこともできる。

もっとも、日本語を扱うにはかなり心許ない。文字幅の処理が不十分なせいで、行最 後の一文字が隠れてしまったり、曖昧幅文字のところで表示が乱れたりする。だから例え ば、曖昧幅文字(矢印とか三角とか)を使わず、80 字ごとに改行を入れる整形したりし するようにしなくてはならない。太古の世界に還ったかの如くである。ところがEmacs で いう fill のコマンドがないから、段落全体を外部コマンドに流して書き換えるように改 造する必要が出てきたりする(たぶんそれが一番手数が少なくてすむ)。最初に楽しいと 言ったのには、そういう手間をかけることも含まれている(これはあまり賛成してもらえ ないかもしれない)。

さて、私は、キーバインディングを Emacs 風に設定して使っているが、少し困ったこ とがあった。micro エディタにはマークという概念がないのだ。おかげでテキストの範囲 を選択するときにシフトキーを押しながら矢印キーを使わなくてはならない。他のことは 我慢したが、これらには慣れることができそうもない。(それに、Ctr-x Ctrl-x で折々 マークにジャンプもしたかった)。仕方なく、拡張を書くことにした。エディタじたいに も、Go にも Lua にも不案内であるから、まあ動けばいいやというレベルだが、だれかも っとマシなものを書いてくれるまで、これで我慢することにする。

まず、~/.config/micro/plug/mark ディレクトリを作成し、次のような内容のファイ ル(mark.lua)を置いて、エディタを再起動。

定義されている関数は 4 つで、マークをつける(mark)、リージョンをmicro の通常 の選択範囲に変換(region_to_selection)、マークとポイントの交換(exchange_point_ and_mark)、micro の通常の選択範囲の解消(cancel_selection)である。

なお、私が使っている micro のバージョンは、2.0.8。

local micro = import("micro")
local config = import("micro/config")

--コマンドの登録。MakeCommand の第1引数がコマンド名、第2引数が関数名
function init()
    config.MakeCommand("mark", mark , config.NoComplete)
    config.MakeCommand("region_to_selection", region_to_selection , config.NoComplete)
    config.MakeCommand("exchange_point_and_mark", exchange_point_and_mark , config.NoComplete)
    config.MakeCommand("cancel_selection", cancel_selection , config.NoComplete)
end	

--カーソルの現在位置をマークする関数
--登録された関数は呼ばれるときバッファペイン(bp)を引数にもらえるお約束
function mark(bp)
	local buf = bp.Buf
	local cursor = buf:GetActiveCursor()
	markx = cursor.Loc.X
	marky = cursor.Loc.Y
	micro.InfoBar():Message("mark")
end

--選択範囲がないならマークとカーソル位置にはさまれた部分を選択範囲にする
function region_to_selection(bp)
	local buf = bp.Buf
	local cursor = buf:GetActiveCursor()
	if not cursor:HasSelection() then
		cursor:SetSelectionStart({X=markx, Y=marky})
		cursor:SetSelectionEnd({X=cursor.Loc.X, Y=cursor.Loc.Y})
	end
end

--マークが存在するなら、マークとカーソルの現在位置を交換する
function exchange_point_and_mark(bp)
	if markx and marky then
		local buf = bp.Buf
		local cursor = buf:GetActiveCursor()
		local tmpx = markx
		local tmpy = marky
		markx = cursor.Loc.X
		marky = cursor.Loc.Y
		cursor:GotoLoc{X=tmpx,Y=tmpy}
	end
end

--選択範囲を解消する
function cancel_selection(bp)
	local buf = bp.Buf
	local cur = buf:GetActiveCursor()
	cur:ResetSelection()
end							

--既知の不具合
--(1) マークがバッファローカルでないので他のバッファから参照される
--(2) マーク箇所に文字がなくなってもマークはそのままになる
--(3) これを書いた作者は、このひどいコードを読んだ優秀で善良な人が
--    猛然と良いコードで書き直して公開してくれるはずだと信じている

さて、こうして作ったコマンドのコマンド名を直接エディタに入力して使うこともで きるが、もちろんキーバインディングをしておきたいのが人情というか当然のことである。 キーバインディングは ~/.config/micro/bindings.json に書き足せけばよい。次は、私 がやってるマーク関連のキーバインディング。(私が使ったのは、バージョン 2.0.8。古 いバージョンだと2打鍵に対するキーバインディングに対応していない)

{
    "<Ctrl-x><Ctrl-x>": "command:exchange_point_and_mark",
    "Alt-w": "command:region_to_selection,Copy,command:cancel_selection",
    "Ctrl-Space": "command:mark",
    "Ctrl-a": "command:mark,StartOfLine",
    "Ctrl-e": "command:mark,EndOfLine",
    "Ctrl-s": "command:mark,Find",
    "Ctrl-w": "command:region_to_selection,Cut",
    "Ctrl-y": "command:mark,Paste",
}

@kabipanotoko