Neovimコマンドモードで役立つExコマンド集

Neovimコマンドモードで役立つExコマンド集

  • 日本語
  • Neovim

昨今のNeovim開発者は、ほぼ間違いなく何かしらのプラグインを導入している。 その結果、デフォルトのキーマッピングが上書きされていることが多い。

しかし、: で始まるExコマンドはキーマップの影響を受けない。 どんな環境でも同じように動作するため、覚えておくと確実に役立つ。

今回は、RustでWebアプリケーションを開発する際の具体的なシーンを交えながら、コマンドモードで使える便利なコマンドを紹介する。

#:%s/old/new/gc - 確認付き置換

コマンドの意味: ファイル全体で oldnew に置換。gc フラグで1つずつ確認しながら置換できる。

フラグの種類:

  • g : 行内の全てのマッチを置換(なしだと最初の1つだけ)
  • c : 置換前に確認
  • i : 大文字小文字を無視
  • I : 大文字小文字を区別

利用シーン: 変数名やメソッド名をリネームしたいが、全部ではなく一部だけ変更したいとき

rust
// user_id を account_id に変更したいが、一部はそのままにしたい
async fn get_user(user_id: i64) -> Result<User, AppError> {
    let user = sqlx::query_as!(User, "SELECT * FROM users WHERE user_id = $1", user_id)
        .fetch_one(&pool)
        .await?;
    log::info!("Fetched user_id: {}", user_id);
    Ok(user)
}
vim
:%s/user_id/account_id/gc

確認プロンプトで y で置換、n でスキップ、a で残り全部置換、q で中止。

#:g/pattern/command - グローバルコマンド

コマンドの意味: pattern にマッチする全ての行に対して command を実行する。

利用シーン: 特定のパターンを含む行をまとめて操作したいとき

#例1: マッチする行を削除

rust
// デバッグ用のprintln!を全て削除したい
async fn process_order(order: Order) -> Result<(), AppError> {
    println!("DEBUG: order = {:?}", order);
    let validated = validate_order(&order)?;
    println!("DEBUG: validated = {:?}", validated);
    let result = save_order(validated).await?;
    println!("DEBUG: result = {:?}", result);
    Ok(())
}
vim
:g/println.*DEBUG/d

d は削除コマンド。これで全てのDEBUG行が消える。

#例2: マッチする行を別の場所に移動

rust
use axum::Router;
use serde::Deserialize;
use tokio::sync::RwLock;
use axum::extract::State;
use serde::Serialize;
use axum::Json;

axum 関連のimportをファイル末尾に移動したい場合。

vim
:g/axum/m$

m$ は末尾に移動。m0 なら先頭に移動。

#例3: マッチする行にコマンドを実行

rust
struct Config {
    database_url: String,
    redis_url: String,
    api_key: String,
}

全フィールドに pub を追加したい場合。

vim
:g/: String/norm Ipub

norm でノーマルモードコマンドを実行。I は行頭で挿入モード。

#:v/pattern/command - 逆グローバルコマンド

コマンドの意味: pattern にマッチしない全ての行に対して command を実行する。:g! と同じ。

利用シーン: 特定のパターンを含まない行を削除したいとき

rust
// テスト関数だけを残して、他の行を全て削除したい
impl UserRepository {
    pub fn new(pool: PgPool) -> Self { /* ... */ }

    #[test]
    fn test_create_user() { /* ... */ }

    pub async fn find_by_id(&self, id: i64) -> Result<User> { /* ... */ }

    #[test]
    fn test_find_by_id() { /* ... */ }
}
vim
:v/test/d

test を含まない行が全て削除される。

#:sort - ソート

コマンドの意味: 選択範囲または全体をソートする。

オプション:

  • :sort : 昇順ソート
  • :sort! : 降順ソート
  • :sort u : 重複を削除してソート
  • :sort n : 数値としてソート
  • :sort i : 大文字小文字を無視

利用シーン: import文やCargo.tomlの依存関係を整理したいとき

toml
[dependencies]
tokio = { version = "1", features = ["full"] }
axum = "0.8"
serde = { version = "1", features = ["derive"] }
anyhow = "1"
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio"] }
tracing = "0.1"

依存関係をアルファベット順にしたい場合、依存関係の範囲を選択して。

vim
:'<,'>sort

結果:

toml
[dependencies]
anyhow = "1"
axum = "0.8"
serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio"] }
tokio = { version = "1", features = ["full"] }
tracing = "0.1"

#:sort u - 重複削除ソート

利用シーン: 重複したimportやuseを整理したいとき

rust
use std::collections::HashMap;
use std::sync::Arc;
use std::collections::HashMap;  // 重複
use std::sync::Arc;             // 重複
use tokio::sync::RwLock;
vim
:sort u

重複が削除されて1つずつになる。

#:%!command - 外部コマンドでフィルタ

コマンドの意味: バッファ全体を外部コマンドに渡し、結果で置き換える。

利用シーン: 外部ツールでコードをフォーマットしたいとき

#例1: JSONを整形

APIレスポンスのサンプルを整形したい場合。

json
{"id":1,"name":"John","email":"john@example.com","created_at":"2026-01-26T10:00:00Z"}
vim
:%!jq .

結果:

json
{
  "id": 1,
  "name": "John",
  "email": "john@example.com",
  "created_at": "2026-01-26T10:00:00Z"
}

#例2: 選択範囲だけをフィルタ

ビジュアルモードで選択してから。

vim
:'<,'>!sort | uniq

選択範囲だけがソートされて重複削除される。

#:read !command - 外部コマンドの出力を挿入

コマンドの意味: 外部コマンドの出力をカーソル位置に挿入する。

利用シーン: 現在の日時やコマンド結果をコードに挿入したいとき

rust
// 現在のgitコミットハッシュを定数として埋め込みたい
const GIT_HASH: &str = "";
vim
:read !git rev-parse --short HEAD

カーソル行の下にコミットハッシュが挿入される。

rust
const GIT_HASH: &str = "";
a1b2c3d

あとは手動で整形するか、マクロで自動化する。

#:w !command - バッファを外部コマンドに渡す

コマンドの意味: バッファの内容を外部コマンドの標準入力として渡す(ファイルは変更されない)。

利用シーン: 保存せずにコードをチェックしたいとき

vim
:w !rustfmt --check

現在のバッファをrustfmtに渡してフォーマットチェック。エラーがあれば表示される。

vim
:w !wc -l

バッファの行数をカウント。

#:norm - 複数行にノーマルモードコマンド実行

コマンドの意味: 指定範囲の各行にノーマルモードコマンドを実行する。

利用シーン: 複数行に同じ編集を適用したいとき

#例1: 全行の末尾にカンマを追加

rust
let items = vec![
    "apple"
    "banana"
    "cherry"
];

文字列の行を選択して。

vim
:'<,'>norm A,

A は行末で挿入モード、, を追加。

結果:

rust
let items = vec![
    "apple",
    "banana",
    "cherry",
];

#例2: 全行の先頭にテキストを追加

rust
id
name
email
created_at

これをSQLのSELECT句にしたい場合。

vim
:%norm Iuser.

結果:

rust
user.id
user.name
user.email
user.created_at

#例3: マクロとの組み合わせ

マクロを記録してから :norm で適用することも可能。

vim
:%norm @a

全行にマクロ a を実行。

#:earlier / :later - 時間ベースのundo

コマンドの意味: 指定した時間前/後の状態に戻す。

利用シーン: 「5分前の状態に戻したい」というとき

vim
:earlier 5m

5分前の状態に戻る。

vim
:later 5m

5分後の状態に進む。

vim
:earlier 10

10回分のundoを実行。

通常の u によるundoはツリー構造になっているため、分岐した編集履歴を辿るのが難しい。:earlier なら時間指定で確実に戻れる。

#:changes - 変更履歴の確認

コマンドの意味: バッファ内の変更履歴を一覧表示する。

利用シーン: どこを編集したか確認したいとき

vim
:changes
text
change line  col text
    3    15    4 async fn create_user(
    2    42   12     let user = User::new(
    1    58    8     Ok(user)
>   0    58    8     Ok(user)

> が現在位置。g;g, でこの履歴を移動できる。

#:jumps - ジャンプリストの確認

コマンドの意味: ジャンプ履歴を一覧表示する。

利用シーン: 最近どのファイルのどこにジャンプしたか確認したいとき

vim
:jumps
text
 jump line  col file/text
   4    15    0 src/handlers/user.rs
   3   142    4 src/models/user.rs
   2    38   12 src/routes.rs
   1    15    0 src/handlers/user.rs
>  0    42    8

<C-o><C-i> でこの履歴を移動できる。

#:registers - レジスタの確認

コマンドの意味: 全レジスタの内容を一覧表示する。

利用シーン: ヤンクや削除した内容を確認したいとき

vim
:registers
text
Type Name Content
  l  ""   let user = User::new(name, email);
  l  "0   async fn create_user(
  l  "1   Ok(user)
  l  "+   外部からコピーしたテキスト
  • "" : 無名レジスタ(最後にヤンク/削除した内容)
  • "0 : ヤンク専用レジスタ
  • "1-"9 : 削除履歴
  • "+ : システムクリップボード

#:argdo - 複数ファイルに対する操作

コマンドの意味: 引数リストの全ファイルに対してコマンドを実行する。

利用シーン: 複数ファイルで一括置換したいとき

vim
:args src/handlers/*.rs
:argdo %s/unwrap()/expect("Failed")/gc | update
  1. src/handlers/ 配下の全 .rs ファイルを引数リストに追加
  2. 各ファイルで置換を実行し、update で保存

update は変更があった場合のみ保存する。

#:bufdo - 全バッファに対する操作

コマンドの意味: 開いている全バッファに対してコマンドを実行する。

利用シーン: 開いている全ファイルで一括操作したいとき

vim
:bufdo %s/TODO/FIXME/g | update

開いている全ファイルで TODOFIXME に置換して保存。

#:cdo - quickfixリストに対する操作

コマンドの意味: quickfixリストの各エントリに対してコマンドを実行する。

利用シーン: grepやLSPの検索結果に対して一括操作したいとき

vim
:grep -r "old_function" src/
:cdo s/old_function/new_function/g | update
  1. grep でquickfixリストに結果を格納
  2. 各マッチ箇所で置換を実行

#:substitute の高度な使い方

#キャプチャグループを使う

利用シーン: パターンの一部を保持しながら置換したいとき

rust
fn get_user(id: i64) -> User { }
fn get_article(id: i64) -> Article { }
fn get_comment(id: i64) -> Comment { }

戻り値を Result<T, AppError> にしたい場合。

vim
:%s/-> \(\w\+\) {/-> Result<\1, AppError> {/g

\(\w\+\) でキャプチャして \1 で参照。

結果:

rust
fn get_user(id: i64) -> Result<User, AppError> { }
fn get_article(id: i64) -> Result<Article, AppError> { }
fn get_comment(id: i64) -> Result<Comment, AppError> { }

#式を使う(=)

利用シーン: 動的な値で置換したいとき

rust
const A: i32 = 0;
const B: i32 = 0;
const C: i32 = 0;

連番にしたい場合。

vim
:let n=0 | %s/= 0/\='= ' . (n) . (let n+=1)/g

少し複雑だが、動的な置換が可能。

#大文字小文字を変換

vim
:%s/\<user\>/\U&/g

userUSER に変換。

  • \U : 以降を大文字に
  • \L : 以降を小文字に
  • \u : 次の1文字を大文字に
  • \l : 次の1文字を小文字に
  • & : マッチした文字列全体

#:redir - コマンド出力をキャプチャ

コマンドの意味: コマンドの出力をファイルやレジスタにリダイレクトする。

利用シーン: :messages:version の出力を保存したいとき

vim
:redir @a
:messages
:redir END

メッセージ履歴がレジスタ a に保存される。"ap で貼り付け。

vim
:redir > output.txt
:version
:redir END

ファイルに保存する場合。

#おわりに

コマンドモードのExコマンドは、プラグインの設定に関係なくどの環境でも同じように動作する。

特に覚えておきたいのは:

  • :%s/old/new/gc - 確認付き置換の基本
  • :g/pattern/d - パターンにマッチする行を削除
  • :%!command - 外部コマンドでフィルタ
  • :earlier 5m - 5分前の状態に戻す
  • :argdo / :bufdo - 複数ファイルへの一括操作

これらは地味だが、知っているかどうかで作業効率が大きく変わる。

:help :substitute:help :global で詳細を確認しながら、少しずつ使いこなしていきたい。