Neovim ことはじめ
プラグイン
モード
モード別の代表的な機能
モードの遷移
Vim の重要な機能:オペレータ、モーション、テキストオブジェクト、リピート
- オペレーター
- 変更、削除、ヤンク、選択といった操作のこと
- モーション
- カーソル移動のこと
- テキストオブジェクト
- リピート
例えば、現在のカーソルから行の最後尾のテキストを削除したい場合、削除のオペレーター d
と行最後尾に移動するモーション $
を組み合わせることで実現できる。
テキストオブジェクト
テキストオブジェクトはテキストを一つのまとまりとして扱います。テキストオブジェクトはオペレーターもしくは選択モードの後にのみ使用できます。例えば、単語というテキストオブジェクトiwで、削除のオペレーターであるdと組み合わせて、diwと入力するだけで1単語を削除できます。
初歩的なミス集
存在しないディレクトリで neovim を起動するとファイルが保存できない
nvim ~/.config/nvim/lua/config/lazy.lua
というコマンドで nvim を実行したとき、コードを書いている途中で :w
を実行したら、E212: Can't open file for writing: no such file or directory
というエラーになった。~/.config/nvim/lua/config
というディレクトリが存在しなかったので、ファイルの新規作成 & バッファからファイルへの書き込みができなかった。
てっきりエディタが開けたからすでにファイルが作成されていると思っていたが、vim の挙動としては vim file
をした時点でエディタを起動したときはバッファが確保されるだけで、この時点ではファイルは作成されていなかった。(これまでたしかに vi
、vim
コマンドでカレントディレクトリ以外にファイル作成するってなかったかもしれない。)
当然だけど、存在するディレクトリに対して nvim /path/to/newfile.txt
を実行した場合はファイルが新規作成される。
解決方法
ディレクトリを作成する
:!mkdir -p ~/.config/nvim/lua/config
再度保存を実行
:w
いけた
モード
よく使いそうなコマンドを覚書
ノーマルモード
- カーソルの移動
- 上下左右:hjkl
-
h
: 左 -
j
: 下 -
k
: 上 -
l
: 右
-
- コピー & ペースト(yank & put)
- 現在カーソル位置から行末までコピー
y$
- ソースコードの全てを選択してクリップボードへコピー
-
gg
でコード先頭行へ移動 →V
で Visual line モード起動 →G
でコード最終行へ移動(コードが全て選択される) →y
でクリップボードへ選択範囲をコピー- nvim 内へペースト:カーソルを移動して
p
- nvim 外(ブラウザなど)へペースト:システムのペーストショートカットを利用する。macOS の場合 cmd + p。
- nvim 内へペースト:カーソルを移動して
- ただし、config で
vim.opt.clipboard = "unnamed"
を設定しておく必要がある
-
- 現在カーソル位置から行末までコピー
- ジャンプ系
-
gg
- ファイルの先頭行への移動
- 先頭行の 1 文字目ではなく、現在の行上のカーソル位置は保存される
-
G
- ファイルの末尾行への移動
- 末尾行の以下略
-
- 上下左右:hjkl
- 削除
- 文字の削除
-
x
- 現在のカーソル位置の文字を削除する
-
- 文字の削除
- インサートモードへの切り替え
-
i
- カーソルの現在位置の直前から挿入モードを開始する
-
I
(大文字の I)- カーソルがある行の最初の非空白文字の前から挿入モードを開始する
-
a
- カーソルの現在位置から一文字右に移動してから挿入モードを開始する
-
A
- カーソルを行末に移動して挿入モードを開始する
-
o
(小文字の o, "open a line below" (下に行を開く))- カーソルがある行の下に新しい行を作成する
- カーソルを新しい行に移動させる
- インサートモードに入る
- 使用例: 現在の行の下に新しい内容を追加したい場合に使用する
- O (大文字の O, "open a line above" (上に行を開く)
- カーソルがある行の上に新しい行を作成する
- カーソルを新しい行に移動させる
- インサートモードに入る
- 使用例: 現在の行の上に新しい内容を追加したい場合に使用する
-
- undo / redo
-
u
- undo
-
ctrl + r
- redo(コマンドラインモード
:redo
でも)
- redo(コマンドラインモード
-
- 行削除
-
dd
- カーソル位置の行を削除する
-
3dd
- カーソル位置から 3 行を削除する
-
d2j
- カーソル位置から下に 2 行(計 3 行)を削除する(まだ使えなそう)
-
合わせ技
- コメントアウト / コメントイン(プラグイン Comment.nvim が必要)
- 一行をラインコメントアウト
- normal mode
gcc
- normal mode
- 一行をブロックコメントアウト
- normal mode
gbc
- normal mode
- 複数行をラインコメントアウト
- visual mode で選択 →
gc
- visual mode で選択 →
- 複数行をブロックコメントアウト
- visual mode でで複数行選択 →
gb
- visual mode でで複数行選択 →
- 一行をラインコメントアウト
- インデントを増やす / 減らす
- 一行のインデントを増やす / 減らす
- インサートモード
- 増やす:insert mode:
<C-t>
- 減らす:insert mode:
<C-d>
(decrease indent)
- 増やす:insert mode:
- normal mode
- 増やす:normal mode
>>
(shift + .
x 2) - 減らす:insert mode:
<<
(shift + ,
x 2)
- 増やす:normal mode
- インサートモード
- 複数行のインデントを増やす / 減らす
- 増やす:visual mode で複数行選択 →
>
(shift + .
) - 減らす:visual mode で複数行選択 →
<
(shift + .
)
- 増やす:visual mode で複数行選択 →
- 一行のインデントを増やす / 減らす
ビジュアルモード(選択モード)
矩形選択が行えるモード
-
v
- カーソル位置から矩形選択が始まる。
-
V
- 行選択モード。カーソルがある行から行単位での矩形選択が始まる。
-
ctrl-v
- Visual Block モードに入る。マルチカーソルができる。
- https://zenn.dev/vim_jp/articles/38915175fe4648#行の先頭に文字を追加する
v
の方は Emacs の mark モードと同じ感じ。
(VSCode での設定項目は以下)
コマンドラインモード
-
:pwd
- 現在作業中のディレクトリ(ワーキングディレクトリ)を表示する(neovim に作業ディレクトリの概念がある)
-
nvim
で起動するとカレントディレクトリがワーキングディレクトリに設定される -
nvim ~/.config/nvim/lua
のように neovim 起動コマンドにディレクトリを渡すとそのディレクトリがワーキングディレクトリに設定される -
nvim ~/.config/nvim/init.lua
のように neovim 起動コマンドにファイルパスを渡すとそのファイルが存在するディレクトリがワーキングディレクトリに設定される
-
- 現在作業中のディレクトリ(ワーキングディレクトリ)を表示する(neovim に作業ディレクトリの概念がある)
-
:f
- 現在起動中のバッファに対応するファイル名を表示する。バッファに対応するファイルが存在しない場合は
[No Name]
と表示される - 例えば、
nvim
だけでウィンドウを起動すると、対応するファイルが存在しないバッファがウィンドウに表示される。
- 現在起動中のバッファに対応するファイル名を表示する。バッファに対応するファイルが存在しない場合は
-
:ls
、:buffers:
- 現在開いているバッファ(≠ ウィンドウに表示されているバッファ)の一覧を表示する
-
:b <バッファ番号>
- 指定した番号のバッファをウィンドウで表示する
-
:ls
に続いて使えば OK
-
- 指定した番号のバッファをウィンドウで表示する
-
:e <path to file>
- バッファを追加し、現在のウィンドウで開く。ファイルへのパスは相対パスも絶対パスを使える。tab 補完も有効。tab を連打すると、一般的なターミナルの tab 補完みたいに次の候補を表示してくれるのも有効。
-
:w /path/to/filename
にも書いたが、存在しないファイルへのパスを書いても OK。ただし、存在するディレクトリを含むファイルパスならバッファの内容をファイルに保存できるが、存在しないディレクトリを含むファイルパスだとバッファの内容をファイルに保存できない。
- バッファを追加し、現在のウィンドウで開く。ファイルへのパスは相対パスも絶対パスを使える。tab 補完も有効。tab を連打すると、一般的なターミナルの tab 補完みたいに次の候補を表示してくれるのも有効。
-
:w
- 現在ウィンドウに表示しているバッファに対応するファイルにバッファの内容を保存する。バッファに対応するファイルが存在しない場合、
E32: No file name
というエラーになる。
- 現在ウィンドウに表示しているバッファに対応するファイルにバッファの内容を保存する。バッファに対応するファイルが存在しない場合、
-
:w /path/to/filename
-
/path/to/filename
に内容を保存、ファイルがない場合は新規作成される(ただし、/path/to
ディレクトリが存在しないとエラーになる。ディレクトリが存在しない場合、自動でディレクトリを作成してはくれないので、:!mkdir -p /path/to
を実行してから再度:w /path/to/filename
を実行すると良い。) - ここのポイントとしては、
nvim <存在しないディレクトリ>/<ファイル>
というコマンドを実行してバッファを開くことはできても(ファイルやディレクトリの存在は必要ない)、:w
or:w <存在しないディレクトリ>/<ファイル>
で保存する時にはバッファを保存できない、ということ。バッファに名前をつけるのは自由だけど、バッファはあくまでメモリ上の概念で、ファイルシステムの実際の状態とは独立している。意図しないファイル作成等の問題を防ぐ。(ってことはバッファの名前を変更することはできるのかな?) - ちなみにプラグインを使用して、保存時に自動的にディレクトリを作成する機能を追加することはできる。
-
-
:%s/<置換前>/<置換後>/g
- 現在のバッファ内の文字列を置換する
-
:!<command>
- シェルコマンドを実行する
- 好きなシェルコマンドを実行できる
バッファについてはこちらを参照。普段 VSCode でやってるような基本的な操作(特定のバッファ(ファイル)を開くとか)がわかる。
nvim の起動コマンド
無名のバッファを開く
nvim
特定のファイルのバッファを開く(存在しないファイルでも OK)
nvim /path/to/filename
特定のディレクトリ配下のファイルのバッファを開く(サブディレクトリのファイルは含めない)
nvim /path/to/**
サブディレクトリを含む、特定のディレクトリは以下のすべてのファイルのバッファを開く
nvim /path/to/**/**
プラグインの操作
neo-tree.nvim
-
:Neotree <directory>
- 特定のディレクトリでの neo-tree の起動
これ覚えよう
lazy.nvim
-
:Lazy sync
- プラグインのリロード
-
plugins/*.lua
を追加した時にすぐに使いたい場合これを実行する - 起動した lazy.nvim の UI は
:q
で閉じることができる
プラグインの追加方法
- 良さげなプラグインを探す
- リポジトリや公式ドキュメントで lazy.nvim 用の設定を見つける
-
nvim ~/.config/nvim/lua/plugins/*.lua
:plugins/*.lua
に設定を書く - コマンドラインモードで
:Lazy sync
を実行 - lazy.nvim の UI が起動し、プラグインがインストールされる
- lazy.nvim の UI を
:q
で閉じる - プラグインがロードされた状態で nvim を使える
自分がエディタ(IDE?)に求めること
VSCode、IntelliJ IDEA で当たり前に使えている機能が最低限使えて欲しいのでそれを整理しておく。
- ショートカット、特にコード編集中に使っているショートカットと同等の機能を使えて欲しい
- 上下左右:ctrl + p / n / b / f
- delete left / right:ctrl + h / d
- 行頭、行末への移動:ctrl + a / e
- カット、行末までカット、ペースト:ctrl + w, ctrl + k, ctrl + y
- コメントアウト / 解除:ctrl + /(1 つのショートカットで out / in する)
- ページャによる上下(1 ~ 50 行まで表示されて、ctrl + v を押したら 51 ~ 100 行まで移動するやつ):ctrl + q / v
- ペイン内の編集中のファイル切り替え:ctrl + tab
- ペイン間の移動:ctrl + :
- ターミナルウィンドウの起動とターミナルフォーカス & ターミナルウィンドウ閉じるとエディタフォーカス:ctrl + ;
- インデント / アウトデント:ctrl ] / [
- 置換の起動:ctrl + r
- 文字の大きさ:cmd + shift + +/-
- undo / redo:ctrl-x-u, cmd + shift-x-u
- マークモードの起動、解除(vim でいう
v
で起動するビジュアルモード):ctrl + u(起動、解除のショートカットは同一) - 型の references / implementation へのジャンプ:F12 or 右クリックメニュー
- これはまじで必須
- コードの特定の場所へのジャンプ:ctrl + g(jumpy 使ってる)
- コード全選択:cmd + a
- エクスプローラとエディタの行き来 & エクスプローラ内を移動 & エクスプローラで選択したファイルを開く:ctrl + o & ctrl + n / p & ctrl + enter
- コード補完
- 型による補完
- general なスニペットの補完
- AI による補完(GitHub Copilot)
- 言語ごとのスニペットの登録 & スニペット登録の楽さ
- VSCode では Easy Snippet というスニペット管理ツールを使っている
- コードを選択し、cmd-k shift-cmd-s でスニペットを作成できる
- https://marketplace.visualstudio.com/items?itemName=inu1255.easy-snippet
- SSH でリモートホストのコードを編集できること
- Remote SSH とか Remote container
- Git 操作が簡単
- 特に使うのが「選択 → 右クリック → Stage Selected Ranges」で選択範囲をステージングする機能
- あとはマージコンフリクト見やすくしてくれる
- ターミナルが開ける
思考がまとまらないまま雑書したので暇な時に整理するか
とりあえず vim を触りたい
編集中のコードへの git ハイライト (追加、削除された行)
マルチカーソル
残り TODO
- シンタックスハイライト
- git 操作、表示
- 補完
- 型情報からの補完(LSP)
- AI(GitHub Copilot)による補完
- スニペット
- タグ補完
- 各種定義へのジャンプ
-
- 残りの引き継ぎたいキーマップ
- コメントアウト / イン
- インデント / アウトデント(アウトデントって言葉本当にある?)
- fzf 系の fuzzy finder
ハイライト
treesitter 入れたけど全容をまだ理解できてない
補完
型情報からの補完:cmp
AI による補完:GitHub Copilot
タグ補完
その他もろもろ
Git 操作
テーマ
nightfox 使ってみよう
バッファとは
良い記事があった。まさに自分。
vimを日常的に使っておきながら、バッファの仕組みをロクに使わずに、1ファイル1プロセスで編集したまま長い時間が経過してしまった人
Vim における「バッファ」とはなんですか?
by Claude 3.5 Sonnet
Vimにおける「バッファ」の概念を定義し、説明します:
- バッファ(Buffer)
- バッファとは、Vimがメモリ上で編集中のテキストを保持する一時的な作業領域
詳細な説明:
- メモリ上の表現:
- バッファは、ファイルの内容がメモリ上に読み込まれた状態を指します。
- 必ずしもディスク上のファイルと一対一対応している必要はありません。
- 編集の単位:
- ユーザーが実際に編集を行う対象がバッファです。
- 変更はまずバッファ内で行われ、保存するまでディスク上のファイルには反映されません。
- 複数のバッファ:
- Vimは同時に複数のバッファを保持できます。
- これにより、複数のファイルを同時に開いて編集することが可能になります。
- 表示との関係:
- バッファは必ずしも画面上に表示されている必要はありません。
- 画面に表示されているバッファを「ウィンドウ」と呼びます。
- 永続性:
- バッファの内容は、明示的に保存するまでは一時的なものです。
- Vimを終了すると、保存されていないバッファの内容は失われます。
- 種類:
- 名前付きバッファ: ファイルに関連付けられたバッファ
- 無名バッファ: ファイルに関連付けられていないバッファ
- スクラッチバッファ: 一時的な作業用のバッファ
- 操作:
- バッファ間の切り替え、バッファの削除、バッファの保存などの操作が可能です。
バッファの概念を理解することは、Vimでの効率的な編集作業に不可欠です。複数のファイルを同時に扱う際や、変更を一時的に保持しておきたい場合に特に重要となります。
:w
コマンドはメモリ上のバッファからファイルへの書き込み操作ってことだね。
LSP について学ぶ
VSCode でたまに右下に出てくるエラーポップアップの textDocument って LSP の JSON RPC メソッド名だったのか
補完
いま mini.completion だけど nvim-cmp の方が情報多いからそっちにしたい
Vim におけるタブとウィンドウ
Neovim(および Vim)における「タブ」と「ウィンドウ」の理解
基本
- ウィンドウ (Window):
- ウィンドウは、単一のバッファ(ファイルや未保存のテキスト)を表示する領域
- 1 つの Neovim インスタンス内で、画面を分割して複数のウィンドウを表示できる
- 例えば、
:split
(n
,g[
) や:vsplit
(n
,g]
)コマンドを使ってウィンドウを分割できる
- タブ (Tab):
- タブは、ウィンドウの集合。ワークスペースのようなもの。(VSCode のワークスペースとかとおんなじ感じだけど、複数ディレクトリは表示できない。)
- 各タブは、独自のウィンドウレイアウトを持つことができる
- タブは
:tabnew
コマンドで作成でき、gt
(prev) やgT
(next) で切り替えられる
視覚的な例:
タブ1 タブ2
+-------------------+ +-------------------+
| | | | |
|ウィンドウ1|ウィンドウ2 | | ウィンドウ1 |
| | | | |
|--------| | |-------------------|
|ウィンドウ3| | | |
| | | | ウィンドウ2 |
+-------------------+ +-------------------+
上記の例では:
- 2 つのタブがある
- タブ 1 には 3 つのウィンドウがあり、タブ 2 には 2 つのウィンドウがある
- 各ウィンドウは異なるファイルやバッファを表示できる
タブとウィンドウの主な違い:
- 階層
- タブはウィンドウの上位階層。1 つのタブに複数のウィンドウを含めることができる。
- 表示
- 一度に表示できるのは 1 つのタブだけだが、1 つのタブ内で複数のウィンドウを同時に表示できる
- 用途
- ウィンドウは通常、関連するファイルを同時に表示するために使用される
- タブは通常、異なるプロジェクトや作業コンテキストを分離するために使用される
Neotree の設定との関連
Neotree の cwd_target
設定におけるタブとウィンドウの違いは、この構造を反映している。
"tab" を選択すると各タブで異なるディレクトリを使用でき、"window" を選択すると同じタブ内の異なるウィンドウで異なるディレクトリを使用できる。
この構造を理解することできれば Neovim で複数プロジェクトを同時に扱うことができる。
ターミナル操作
- insert mode / normal mode からターミナルを開く
-
C-;
- デフォルト
- デフォルトの設定だと、
:terminal
でターミナルが現在のウィンドウで起動する(この時点では normal mode) -
i
を入力すると、terminal mode 起動され、コマンドが打てるようになる
- デフォルトの設定だと、
- デフォルト
-
- terminal mode → normal mode
-
<Esc>
(デフォルトのキーマップは<C-\><C-n>
だが、流石に面倒なので terminal mode における<Esc>
を<C-\><C-n>
に割り当てておくと良い。)
-
WezTerm の操作
- 新規タブ(≠ Vim の「タブ」)を作成する
cmd + t
- 次のタブに移動する
ctrl + tab
- 前のタブに移動する
-
ctrl + shift + tab
(こっちは 3 本指使うから面倒でいつもctrl + tab
でぐるっとタブを周回しちゃう)
-
Neotree キーマップ
メタ操作
- Neotree window <-> prev windows:
<Leader>t
操作
前提:Neotree の window にフォーカスしている状態
- ファイルを作成
- 移動して
a
(add)
- 移動して
- ディレクトリを作成
- 移動して
A
(Add)
- 移動して
- ファイル、ディレクトリを削除
- 移動して
d
→y
(delete, このとき立ち上がる補完だけ邪魔だから無効にしたい)
- 移動して
- ファイル名、ディレクトリ名を変更
- 移動して
r
(rename)
- 移動して
- 直前のウィンドウに対して :vsplit してファイルを開く:
s
(直前のウィンドウの左側に vsplit でウィンドウが作られる) - 直前のウィンドウに対して :split してファイルを開く:
S
(直前のウィンドウの上に hsplit でウィンドウが作られる) - プレビュー
- 移動して
P
- プレビューモードで表示されているファイルの操作
- ウィンドウとして開く
<Enter>
- 10 行進む
-
<C-f>
(forward)
-
- 10 行戻る
-
<C-b>
(back)
-
- ウィンドウとして開く
- 移動して
- ファイルのメタデータの表示
- 移動して
i
- 移動して
Neovim がだんだんそれっぽくなってきた
Formatter の設定
これ見ると良さそう
jumpy 的なやつ:hop.nvim
めっちゃよい。multi_windows = true
で全然いける。ほぼ HopChar2 と HopPattern で用足りる。
- HopChar2: n,
<Learder>hw
- HopPattern: n,
<Leader>hp
- HopWord: n,
<Learder>hw
- HopLine: n,
<Learder>hl
Git 操作
gitsigns.nvim の機能
- stage_hunk:
- 特定の変更(hunk)をステージングする
- ファイルの一部分だけを選択的にコミットに含めることができる
- reset_hunk:
- 特定の変更(hunk)をリセットする
- 不要な変更を簡単に元に戻すことができる
- stage_buffer:
- 現在開いているファイル(バッファ)の全ての変更をステージングする
-
git add <ファイル名>
と同等の操作
- undo_stage_hunk:
- 直前にステージングした hunk のステージングを取り消す
- 誤ってステージングした場合に便利
- reset_buffer:
- 現在のファイル(バッファ)の全ての変更をリセットする
- ファイル全体の変更を破棄したい場合に使用する
- preview_hunk:
- 現在の hunk の変更内容をプレビュー表示する
- 変更の詳細を確認するのに役立つ
- blame_line:
- 現在の行の git blame 情報を表示する
- その行が最後にいつ、誰によって変更されたかを確認できる
- toggle_current_line_blame:
- 現在の行の git blame 情報の表示/非表示を切り替える
- diffthis:
- 現在のファイルと Git リポジトリの最新版との差分を表示する
- toggle_deleted:
- 削除された行の表示/非表示を切り替える
autocmd について
ついに Neovim の設定が dotfiles の一員になった