morisolog

ソフトウェア関連の技術書まとめ:新卒2年目エンジニア

【技術書2】『実践Vim』 を読んだ

実践Vim を読んだ

この本では、Vimでの作業効率を上げるTipsが121個紹介されている。

以下は特に便利だと感じた8個のTipsを書いておく。

1. *コマンドで単語検索の効率を上げる

*コマンドは、その時点でカーソル位置にある単語を検索する。

今まで、wordを検索するときは、検索用のプロンプトを表示して/wordとしていたが、単にカーソルをこの単語の上に置いて、*キーを叩くだけで同様のことが実現できるようになった。

2. 繰り返しコマンド

.ドットコマンドを使うと、直前の変更を繰り返すことができる。

3. 数字の加算には<C-a>コマンドを使う

<C-a>コマンドは、カーソル位置もしくはカーソル以降にある数に数を加算する。

<C-x>コマンドは逆に、数を減算する。

10<C-a>とすれば、「5」が「15」に変わる。

4. Vimのテキスト操作は全て オペレータ + モーション で構成されている

Vimのテキスト操作は オペレータ(操作の種類) x モーション(操作の対象) の組み合わせでできている。

たとえば、dawは削除するオペレーションコマンドのdと単語全体を意味するモーションawの組み合わせ。

オペレータやモーションを新たに学習することで、確実により多くの考えを表現できるようになる。

ちなみにオペレータを2回繰り返すと、オペレーションが現在の行に適用される。

5. 挿入モードでヤンクしたばかりのテキストを貼り付ける

Vimが記憶している文字列、コマンドはレジスタと呼ばれる場所に格納されていて、挿入モードでは<C-r>を押してアクセスできる。

ヤンクしたテキストは0(zero)レジスタに入っているので、<C-r>0を叩くと、さっきヤンクしたばかりのテキストを貼り付けることができる。

レジスタの中身は:regまたは:registersコマンドで覗く。

6. 置換モードで既存のテキストを上書き

ノーマルモードからRコマンドを入力することで置換モードに移行できる。r{char}コマンドを使うと、1文字だけを上書きしてノーマルモードに戻る。

置換モードではカーソル上の文字列を入力する文字で置き換えることが可能。

7. 3種類のビジュアルモードを使いこなす

Vimには3種類のビジュアルモードがある。

vコマンドは文字指向のビジュアルモード。

Vコマンドで行単位のビジュアルモードが有効になる。

<C-v>コマンドが痒い所に手が届くブロック指向のビジュアルモードを呼び出す。

<C-v>コマンドはいわゆる、矩形選択が可能になるモードで、行頭に一括でコメントアウト文字列を入力するときや、各行末に,コンマなどの文字を入力するときに使える。

詳しくは「矩形選択 vim」などで検索。

8. レジスタを扱えるようになる

Vimではカット/コピー/ペースト操作の全てで単一のクリップボードを使うのではなく、複数のレジスタが用意されている。

コマンドに"{register}を前置すると使いたいレジスタを指定できる。aレジスタにヤンクしたければ"ayiwを実行すればいいし、"apでそこからペーストできる。

覚えておきたいレジスタはこれ。

まとめ

『実践Vim』はVimのデフォルト機能に絞って解説されているので、ssh先でVimを使う時でも活用することができる。

まだまだ全てのTipを理解したわけではないので、時々見返したい。

HammerspoonでAlacrittyにHotkeyを設定してみた。

最近メインで使うターミナルエミュレータをiTerm2からAlacrittyに移した。

iTerm2ではHotkeyを設定してCtrlキー2回押しで表示/非表示を切り替えていたが、 AlacrittyはHotkeyを標準搭載していないようだった。

AlacrittyでもHotkeyを使えないか調べていると、HammerspoonというMacの自動化ツールが使えそうだったので採用してみる。

なお、環境は macOS Monterey 12.2 です。

Hammerspoon

Hammerspoonは、macの自動化ツール。Luaというスクリプト言語を用いて、キーボードショートカットと一連のウィンドウ操作などのアクションを紐付けることができる。

他にも、Wifiの接続先が切り替わったタイミングで任意のコマンドを実行したり、バッテリーが一定以下になるとアラームを飛ばすなんてこともできるらしい。

まずはhomebrewでインストール。

brew install hammerspoon --cask

Hammerspoonはインストールしただけでは何もしてくれないので、~/.hammerspoon/init.luaLuaスクリプトを作成。

この中に設定を記述していく。

設定: ちょっと寄り道

公式の Getting Started のページ見たら、「とりあえずHelloWorldしてみなはれ」と言われたので、そうしてみた。

~/.hammerspoon/init.lua

hs.hotkey.bind({"cmd", "alt", "ctrl"}, "W", function()
  hs.alert.show("Hello World!")
end)

ファイルを保存して、macメニューバーのHammerspoonアイコンをクリック > Reload Configを選択する。

++ctrl+wを押すと、画面にHelloWorldが通知された!

Hello World 通知のようす(3回連打した)
Hello World 通知のようす(3回連打した)

これだけでもう、Hammerspoonで未来が開けそう感!

他にもかなりの数のチュートリアルが用意されているので、あさってみるだけでも楽しそう。

Hotkeyを設定する

キーイベントを検知して Alacrittyの起動 & 表示 / 非表示をやってくれるスクリプトを書く。

hs.hotkey.bind({ "cmd", "ctrl" }, "e", function()
  local appName = "Alacritty"
  local app = hs.application.get(appName)

  if app == nil or app:isHidden() then
    hs.application.launchOrFocus(appName)
  else
    app:hide()
  end
end)

アプリケーションは、Hammerspoon APIhs.application で操作できるようになっている。

これで、+ctrl+eを押すとiTerm2のHotkeyにかなり近い操作ができる。

ただし、ctrl 2回押し のキーイベントを検知してほしいので、もう一工夫必要になる。

Stack over flow によると、どうやら面倒なことやらないといけないらしい。

stackoverflow.com

hammerspoon double keyでググったら、ctrl 2回押し のキーイベントを検知して任意のfunctionを実行できるモジュールが公開されていた✌️

gist.github.com

これを、~/.hammerspoon/ctrlDoublePress.luaに保存して、init.luaから呼び出す。

init.lua

local double_press = require("ctrlDoublePress")

local open_alacritty = function()
  local appName = "Alacritty"
  local app = hs.application.get(appName)

  if app == nil or app:isHidden() then
    hs.application.launchOrFocus(appName)
  else
    app:hide()
  end
end

double_press.timeFrame = 0.5
double_press.action = open_alacritty

これでHotkeyの設定できた!

hotkey
hotkey

アクティブウィンドウ上でAlacrittyの表示 / 非表示ができるようにする

このままだと、アクティブなウィンドウが切り替わって使いにくい。どういうことかというと、

  1. まずデスクトップ 1 で Alacritty を開く
  2. その後デスクトップ 2 に移動して Chrome を起動
  3. その場でHotkeyすると、デスクトップ 2 に Alacritty を移してほしいが、実際にはデスクトップ 1 にフォーカスが移動して Alacritty が開く

これを解決するために、Hammerspoonの外部モジュールであるhs.spacesを使う。

github.com

インストール手順

$ cd ~/Downloads
$ git clone git@github.com:asmagill/hs._asm.spaces.git
$ cd hs._asm.spaces
$ make install

最後にmake installコマンドを実行することで、~/.hammerspoon配下にモジュールを作成してくれる。

最終的にはこのようなディレクトリ構成になった。 hs配下は全て、さっきのmakeコマンドで作ってくれたもの。

.hammerspoon
├── hs
│   ├── libspaces.dylib.dSYM
│   │   └── Contents
│   │       ├── Resources
│   │       │   └── DWARF
│   │       │       └── libspaces.dylib
│   │       └── Info.plist
│   ├── libspaces_watcher.dylib.dSYM
│   │   └── Contents
│   │       ├── Resources
│   │       │   └── DWARF
│   │       │       └── libspaces_watcher.dylib
│   │       └── Info.plist
│   ├── libspaces.dylib
│   ├── libspaces_watcher.dylib
│   ├── spaces.docs.json
│   └── spaces.lua
├── ctrlDoublePress.lua
└── init.lua

init.lua の中身はこう。

local application = require("hs.application")
local spaces = require("hs.spaces")

local double_press = require("ctrlDoublePress")

local open_alacritty = function()
  local appName = "Alacritty"
  local app = application.get(appName)

  if app == nil then
    application.launchOrFocus(appName)
  elseif app:isFrontmost() then
    app:hide()
  else
    local active_space = spaces.focusedSpace()
    local alacritty_win = app:focusedWindow()
    spaces.moveWindowToSpace(alacritty_win, active_space)
    app:setFrontmost()
  end
end

double_press.timeFrame = 0.5
double_press.action = open_alacritty

appNameを変えれば、Python製ターミナルエミュレータkittyとかにもHotkeyが設定できる。

僕のdotfilesはこちら

github.com

zsh: suspended が出た時の対処法

vim でファイルを編集していると、どこかのキーを押してしまい突然

❯ vim zsh_suspend.md
zsh: suspended  vim zsh_suspend.md

の表示とともにコマンドラインに戻された。

結論

❯ fg

fgコマンドを実行すると、元の vim のプロセスに戻れる。

何が起きていたのか?

ファイル編集中に Ctrl+z を押した結果、vim のプロセスがsuspend(一時停止)状態になっていた

その証拠に、現在の端末に割り当てられたプロセスをjobsコマンドで確認してみると、

❯ jobs
[1]  - suspended  vim zsh_suspend.md

さっき突然消えてしまった、Vimのプロセスがあった!

この作業をフォアグラウンド(元の vim の編集画面)に戻すには、fgコマンドを実行すればいい。

ちなみに、複数プロセスが動いている場合は、fgコマンドに引数として%1 %2などを渡せば、プロセスを指定できる。

❯ jobs
[1]  - suspended  vim sample1.md
[2]  - suspended  vim sample2.md

❯ fg %1
# sample1.mdの編集画面

❯ fg %2
# sample2.mdの編集画面

bash の場合、fg 1のように引数は数字のみでも可

参考記事

こちらの@kamyknさんの記事も読んでみてほしいです。

qiita.com

追記 (2022/03/26)

最初に偶然Ctrl-zを押してしまった日から、2ヶ月が経ちました。

今となってはCtrl-zなしでは生きていけません。

上の参考記事を読んでもらえればわかると思いますが、Ctrl-z を使うことで、vim <=> ターミナル の行き来が格段にしやすくなります。

vimでテキスト編集 => ターミナルでGit操作 => vimに戻る

といった具合です。

vimに復帰する度にfgとコマンド入力するのが面倒なので、Ctrl-zキーバインドを設定しています。

~/.zshrc

fancy-ctrl-z () {
  if [[ $#BUFFER -eq 0 ]]; then
    BUFFER="fg"
    zle accept-line
  else
    zle push-input
    zle clear-screen
  fi
}
zle -N fancy-ctrl-z
bindkey '^Z' fancy-ctrl-z

Ctrl-z のジョブサスペンド機能を使っていない方がいたら、ぜひ使ってみてほしいです!