Open45

Neovim ことはじめ

nukopynukopy

Vim の重要な機能:オペレータ、モーション、テキストオブジェクト、リピート

  • オペレーター
    • 変更、削除、ヤンク、選択といった操作のこと
  • モーション
    • カーソル移動のこと
  • テキストオブジェクト
  • リピート

例えば、現在のカーソルから行の最後尾のテキストを削除したい場合、削除のオペレーター d と行最後尾に移動するモーション $ を組み合わせることで実現できる。

nukopynukopy

テキストオブジェクト

テキストオブジェクトはテキストを一つのまとまりとして扱います。テキストオブジェクトはオペレーターもしくは選択モードの後にのみ使用できます。例えば、単語というテキストオブジェクトiwで、削除のオペレーターであるdと組み合わせて、diwと入力するだけで1単語を削除できます。

nukopynukopy

初歩的なミス集

存在しないディレクトリで 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 をした時点でエディタを起動したときはバッファが確保されるだけで、この時点ではファイルは作成されていなかった。(これまでたしかに vivim コマンドでカレントディレクトリ以外にファイル作成するってなかったかもしれない。)

当然だけど、存在するディレクトリに対して nvim /path/to/newfile.txt を実行した場合はファイルが新規作成される。

解決方法

ディレクトリを作成する

:!mkdir -p ~/.config/nvim/lua/config

再度保存を実行

:w

いけた

nukopynukopy

モード

よく使いそうなコマンドを覚書

nukopynukopy

ノーマルモード

  • カーソルの移動
    • 上下左右:hjkl
      • h: 左
      • j: 下
      • k: 上
      • l: 右
    • コピー & ペースト(yank & put)
      • 現在カーソル位置から行末までコピー
        • y$
      • ソースコードの全てを選択してクリップボードへコピー
        • gg でコード先頭行へ移動 → V で Visual line モード起動 → G でコード最終行へ移動(コードが全て選択される) → y でクリップボードへ選択範囲をコピー
          • nvim 内へペースト:カーソルを移動して p
          • nvim 外(ブラウザなど)へペースト:システムのペーストショートカットを利用する。macOS の場合 cmd + p。
        • ただし、config で vim.opt.clipboard = "unnamed" を設定しておく必要がある
    • ジャンプ系
      • gg
        • ファイルの先頭行への移動
        • 先頭行の 1 文字目ではなく、現在の行上のカーソル位置は保存される
      • G
        • ファイルの末尾行への移動
        • 末尾行の以下略
  • 削除
    • 文字の削除
      • x
        • 現在のカーソル位置の文字を削除する
  • インサートモードへの切り替え
    • i
      • カーソルの現在位置の直前から挿入モードを開始する
    • I(大文字の I)
      • カーソルがある行の最初の非空白文字の前から挿入モードを開始する
    • a
      • カーソルの現在位置から一文字右に移動してから挿入モードを開始する
    • A
      • カーソルを行末に移動して挿入モードを開始する
    • o (小文字の o, "open a line below" (下に行を開く))
      1. カーソルがある行の下に新しい行を作成する
      2. カーソルを新しい行に移動させる
      3. インサートモードに入る
      • 使用例: 現在の行の下に新しい内容を追加したい場合に使用する
    • O (大文字の O, "open a line above" (上に行を開く)
      1. カーソルがある行の上に新しい行を作成する
      2. カーソルを新しい行に移動させる
      3. インサートモードに入る
      • 使用例: 現在の行の上に新しい内容を追加したい場合に使用する
  • undo / redo
    • u
      • undo
    • ctrl + r
      • redo(コマンドラインモード :redo でも)
  • 行削除
    • dd
      • カーソル位置の行を削除する
    • 3dd
      • カーソル位置から 3 行を削除する
    • d2j
      • カーソル位置から下に 2 行(計 3 行)を削除する(まだ使えなそう)

合わせ技

  • コメントアウト / コメントイン(プラグイン Comment.nvim が必要)
    • 一行をラインコメントアウト
      • normal mode gcc
    • 一行をブロックコメントアウト
      • normal mode gbc
    • 複数行をラインコメントアウト
      • visual mode で選択 → gc
    • 複数行をブロックコメントアウト
      • visual mode でで複数行選択 → gb
  • インデントを増やす / 減らす
    • 一行のインデントを増やす / 減らす
      • インサートモード
        • 増やす:insert mode: <C-t>
        • 減らす:insert mode: <C-d>(decrease indent)
      • normal mode
        • 増やす:normal mode >>shift + . x 2)
        • 減らす:insert mode: <<shift + , x 2)
    • 複数行のインデントを増やす / 減らす
      • 増やす:visual mode で複数行選択 → >shift + .
      • 減らす:visual mode で複数行選択 → <shift + .
nukopynukopy

ビジュアルモード(選択モード)

矩形選択が行えるモード

v の方は Emacs の mark モードと同じ感じ。
(VSCode での設定項目は以下)

nukopynukopy

コマンドラインモード

  • :pwd
    • 現在作業中のディレクトリ(ワーキングディレクトリ)を表示する(neovim に作業ディレクトリの概念がある)
      • nvim で起動するとカレントディレクトリがワーキングディレクトリに設定される
      • nvim ~/.config/nvim/lua のように neovim 起動コマンドにディレクトリを渡すとそのディレクトリがワーキングディレクトリに設定される
      • nvim ~/.config/nvim/init.lua のように neovim 起動コマンドにファイルパスを渡すとそのファイルが存在するディレクトリがワーキングディレクトリに設定される
  • :f
    • 現在起動中のバッファに対応するファイル名を表示する。バッファに対応するファイルが存在しない場合は [No Name] と表示される
    • 例えば、nvim だけでウィンドウを起動すると、対応するファイルが存在しないバッファがウィンドウに表示される。
  • :ls:buffers:
    • 現在開いているバッファ(≠ ウィンドウに表示されているバッファ)の一覧を表示する
  • :b <バッファ番号>
    • 指定した番号のバッファをウィンドウで表示する
      • :ls に続いて使えば OK
  • :e <path to file>
    • バッファを追加し、現在のウィンドウで開く。ファイルへのパスは相対パスも絶対パスを使える。tab 補完も有効。tab を連打すると、一般的なターミナルの tab 補完みたいに次の候補を表示してくれるのも有効。
    • :w /path/to/filename にも書いたが、存在しないファイルへのパスを書いても OK。ただし、存在するディレクトリを含むファイルパスならバッファの内容をファイルに保存できるが、存在しないディレクトリを含むファイルパスだとバッファの内容をファイルに保存できない。
  • :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>
    • シェルコマンドを実行する
    • 好きなシェルコマンドを実行できる
nukopynukopy

nvim の起動コマンド

無名のバッファを開く

nvim

特定のファイルのバッファを開く(存在しないファイルでも OK)

nvim /path/to/filename

特定のディレクトリ配下のファイルのバッファを開く(サブディレクトリのファイルは含めない)

nvim /path/to/**

サブディレクトリを含む、特定のディレクトリは以下のすべてのファイルのバッファを開く

nvim /path/to/**/**
nukopynukopy

lazy.nvim

  • :Lazy sync
    • プラグインのリロード
    • plugins/*.lua を追加した時にすぐに使いたい場合これを実行する
    • 起動した lazy.nvim の UI は :q で閉じることができる
nukopynukopy

プラグインの追加方法

  1. 良さげなプラグインを探す
  2. リポジトリや公式ドキュメントで lazy.nvim 用の設定を見つける
  3. nvim ~/.config/nvim/lua/plugins/*.lua: plugins/*.lua に設定を書く
  4. コマンドラインモードで :Lazy sync を実行
  5. lazy.nvim の UI が起動し、プラグインがインストールされる
  6. lazy.nvim の UI を :q で閉じる
  7. プラグインがロードされた状態で nvim を使える
nukopynukopy

自分がエディタ(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)
  • 言語ごとのスニペットの登録 & スニペット登録の楽さ
  • SSH でリモートホストのコードを編集できること
    • Remote SSH とか Remote container
  • Git 操作が簡単
    • 特に使うのが「選択 → 右クリック → Stage Selected Ranges」で選択範囲をステージングする機能
    • あとはマージコンフリクト見やすくしてくれる
  • ターミナルが開ける
nukopynukopy

思考がまとまらないまま雑書したので暇な時に整理するか
とりあえず vim を触りたい

nukopynukopy

編集中のコードへの git ハイライト (追加、削除された行)
マルチカーソル

nukopynukopy

残り TODO

  • シンタックスハイライト
  • git 操作、表示
  • 補完
    • 型情報からの補完(LSP)
    • AI(GitHub Copilot)による補完
    • スニペット
    • タグ補完
  • 各種定義へのジャンプ
  • テーマ(onedark 以外)

  • 残りの引き継ぎたいキーマップ
    • コメントアウト / イン
    • インデント / アウトデント(アウトデントって言葉本当にある?)
  • fzf 系の fuzzy finder

ハイライト

treesitter 入れたけど全容をまだ理解できてない

補完

型情報からの補完:cmp

https://github.com/hrsh7th/nvim-cmp?tab=readme-ov-file

https://zenn.dev/vim_jp/articles/511d7982a64967#◯lsp-(サーバー管理)-%3A-mason.nvim%2Fnvim-lspconfig%2Fmason-lspconfig.nvim

AI による補完:GitHub Copilot

https://github.com/github/copilot.vim

https://zenn.dev/hirokisan/articles/8f2c9ca238d8b1

タグ補完

https://github.com/windwp/nvim-ts-autotag

https://zenn.dev/vim_jp/articles/511d7982a64967#◯auto-tag-%3A-nvim-ts-autotag

その他もろもろ

https://zenn.dev/yutakatay/articles/neovim-plugins-2022#自動補完系

Git 操作

https://zenn.dev/vim_jp/articles/511d7982a64967#◯git-%3A-git.nvim%2Fgitsigns.nvim

https://github.com/dinhhuy258/git.nvim

https://github.com/lewis6991/gitsigns.nvim

テーマ

nightfox 使ってみよう

https://github.com/EdenEast/nightfox.nvim

nukopynukopy

バッファとは

良い記事があった。まさに自分。

vimを日常的に使っておきながら、バッファの仕組みをロクに使わずに、1ファイル1プロセスで編集したまま長い時間が経過してしまった人

https://zenn.dev/sa2knight/articles/e0a1b2ee30e9ec22dea9

Vim における「バッファ」とはなんですか?

by Claude 3.5 Sonnet

Vimにおける「バッファ」の概念を定義し、説明します:

  • バッファ(Buffer)
    • バッファとは、Vimがメモリ上で編集中のテキストを保持する一時的な作業領域

詳細な説明:

  1. メモリ上の表現:
    • バッファは、ファイルの内容がメモリ上に読み込まれた状態を指します。
    • 必ずしもディスク上のファイルと一対一対応している必要はありません。
  2. 編集の単位:
    • ユーザーが実際に編集を行う対象がバッファです。
    • 変更はまずバッファ内で行われ、保存するまでディスク上のファイルには反映されません。
  3. 複数のバッファ:
    • Vimは同時に複数のバッファを保持できます。
    • これにより、複数のファイルを同時に開いて編集することが可能になります。
  4. 表示との関係:
    • バッファは必ずしも画面上に表示されている必要はありません。
    • 画面に表示されているバッファを「ウィンドウ」と呼びます。
  5. 永続性:
    • バッファの内容は、明示的に保存するまでは一時的なものです。
    • Vimを終了すると、保存されていないバッファの内容は失われます。
  6. 種類:
    • 名前付きバッファ: ファイルに関連付けられたバッファ
    • 無名バッファ: ファイルに関連付けられていないバッファ
    • スクラッチバッファ: 一時的な作業用のバッファ
  7. 操作:
    • バッファ間の切り替え、バッファの削除、バッファの保存などの操作が可能です。

バッファの概念を理解することは、Vimでの効率的な編集作業に不可欠です。複数のファイルを同時に扱う際や、変更を一時的に保持しておきたい場合に特に重要となります。

nukopynukopy

:w コマンドはメモリ上のバッファからファイルへの書き込み操作ってことだね。

nukopynukopy

Vim におけるタブとウィンドウ

Neovim(および Vim)における「タブ」と「ウィンドウ」の理解

基本

  1. ウィンドウ (Window):
    • ウィンドウは、単一のバッファ(ファイルや未保存のテキスト)を表示する領域
    • 1 つの Neovim インスタンス内で、画面を分割して複数のウィンドウを表示できる
    • 例えば、:splitn, g[) や :vsplitn, g])コマンドを使ってウィンドウを分割できる
  2. タブ (Tab):
    • タブは、ウィンドウの集合。ワークスペースのようなもの。(VSCode のワークスペースとかとおんなじ感じだけど、複数ディレクトリは表示できない。)
    • 各タブは、独自のウィンドウレイアウトを持つことができる
    • タブは :tabnew コマンドで作成でき、gt (prev) や gT (next) で切り替えられる

視覚的な例:

タブ1                      タブ2
+-------------------+     +-------------------+
|        |          |     |                   |
|ウィンドウ1|ウィンドウ2 |     |    ウィンドウ1     |
|        |          |     |                   |
|--------|          |     |-------------------|
|ウィンドウ3|          |     |                   |
|        |          |     |    ウィンドウ2     |
+-------------------+     +-------------------+

上記の例では:

  • 2 つのタブがある
  • タブ 1 には 3 つのウィンドウがあり、タブ 2 には 2 つのウィンドウがある
  • 各ウィンドウは異なるファイルやバッファを表示できる

タブとウィンドウの主な違い:

  1. 階層
    • タブはウィンドウの上位階層。1 つのタブに複数のウィンドウを含めることができる。
  2. 表示
    • 一度に表示できるのは 1 つのタブだけだが、1 つのタブ内で複数のウィンドウを同時に表示できる
  3. 用途
    • ウィンドウは通常、関連するファイルを同時に表示するために使用される
    • タブは通常、異なるプロジェクトや作業コンテキストを分離するために使用される

Neotree の設定との関連

https://github.com/nvim-neo-tree/neo-tree.nvim/blob/206241e451c12f78969ff5ae53af45616ffc9b72/doc/neo-tree.txt#L885-L912

Neotree の cwd_target 設定におけるタブとウィンドウの違いは、この構造を反映している。

"tab" を選択すると各タブで異なるディレクトリを使用でき、"window" を選択すると同じタブ内の異なるウィンドウで異なるディレクトリを使用できる。

この構造を理解することできれば Neovim で複数プロジェクトを同時に扱うことができる。

nukopynukopy

ターミナル操作

  • 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> に割り当てておくと良い。)
nukopynukopy

WezTerm の操作

  • 新規タブ(≠ Vim の「タブ」)を作成する
    • cmd + t
  • 次のタブに移動する
    • ctrl + tab
  • 前のタブに移動する
    • ctrl + shift + tab(こっちは 3 本指使うから面倒でいつも ctrl + tab でぐるっとタブを周回しちゃう)
nukopynukopy

Neotree キーマップ

メタ操作

  • Neotree window <-> prev windows: <Leader>t

操作

前提:Neotree の window にフォーカスしている状態

  • ファイルを作成
    • 移動して a (add)
  • ディレクトリを作成
    • 移動して A (Add)
  • ファイル、ディレクトリを削除
    • 移動して dy(delete, このとき立ち上がる補完だけ邪魔だから無効にしたい)
  • ファイル名、ディレクトリ名を変更
    • 移動して r (rename)
  • 直前のウィンドウに対して :vsplit してファイルを開く: s(直前のウィンドウの左側に vsplit でウィンドウが作られる)
  • 直前のウィンドウに対して :split してファイルを開く: S(直前のウィンドウの上に hsplit でウィンドウが作られる)
  • プレビュー
    • 移動して P
    • プレビューモードで表示されているファイルの操作
      • ウィンドウとして開く
        • <Enter>
      • 10 行進む
        • <C-f> (forward)
      • 10 行戻る
        • <C-b> (back)
  • ファイルのメタデータの表示
    • 移動して i
nukopynukopy

Neovim がだんだんそれっぽくなってきた

nukopynukopy

nordfox のテーマの背景が黒いなと思ってたら options.transparent = true というのが良くなかったらしい。false にしたら直った。

nukopynukopy

Git 操作

gitsigns.nvim の機能

  1. stage_hunk:
    • 特定の変更(hunk)をステージングする
    • ファイルの一部分だけを選択的にコミットに含めることができる
  2. reset_hunk:
    • 特定の変更(hunk)をリセットする
    • 不要な変更を簡単に元に戻すことができる
  3. stage_buffer:
    • 現在開いているファイル(バッファ)の全ての変更をステージングする
    • git add <ファイル名> と同等の操作
  4. undo_stage_hunk:
    • 直前にステージングした hunk のステージングを取り消す
    • 誤ってステージングした場合に便利
  5. reset_buffer:
    • 現在のファイル(バッファ)の全ての変更をリセットする
    • ファイル全体の変更を破棄したい場合に使用する
  6. preview_hunk:
    • 現在の hunk の変更内容をプレビュー表示する
    • 変更の詳細を確認するのに役立つ
  7. blame_line:
    • 現在の行の git blame 情報を表示する
    • その行が最後にいつ、誰によって変更されたかを確認できる
  8. toggle_current_line_blame:
    • 現在の行の git blame 情報の表示/非表示を切り替える
  9. diffthis:
    • 現在のファイルと Git リポジトリの最新版との差分を表示する
  10. toggle_deleted:
    • 削除された行の表示/非表示を切り替える