Neovimコマンドモードで役立つExコマンド集
- 日本語
- Neovim
昨今のNeovim開発者は、ほぼ間違いなく何かしらのプラグインを導入している。 その結果、デフォルトのキーマッピングが上書きされていることが多い。
しかし、: で始まるExコマンドはキーマップの影響を受けない。
どんな環境でも同じように動作するため、覚えておくと確実に役立つ。
今回は、RustでWebアプリケーションを開発する際の具体的なシーンを交えながら、コマンドモードで使える便利なコマンドを紹介する。
#:%s/old/new/gc - 確認付き置換
コマンドの意味: ファイル全体で old を new に置換。gc フラグで1つずつ確認しながら置換できる。
フラグの種類:
g: 行内の全てのマッチを置換(なしだと最初の1つだけ)c: 置換前に確認i: 大文字小文字を無視I: 大文字小文字を区別
利用シーン: 変数名やメソッド名をリネームしたいが、全部ではなく一部だけ変更したいとき
// 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) }
:%s/user_id/account_id/gc
確認プロンプトで y で置換、n でスキップ、a で残り全部置換、q で中止。
#:g/pattern/command - グローバルコマンド
コマンドの意味: pattern にマッチする全ての行に対して command を実行する。
利用シーン: 特定のパターンを含む行をまとめて操作したいとき
#例1: マッチする行を削除
// デバッグ用の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(()) }
:g/println.*DEBUG/d
d は削除コマンド。これで全てのDEBUG行が消える。
#例2: マッチする行を別の場所に移動
use axum::Router; use serde::Deserialize; use tokio::sync::RwLock; use axum::extract::State; use serde::Serialize; use axum::Json;
axum 関連のimportをファイル末尾に移動したい場合。
:g/axum/m$
m$ は末尾に移動。m0 なら先頭に移動。
#例3: マッチする行にコマンドを実行
struct Config { database_url: String, redis_url: String, api_key: String, }
全フィールドに pub を追加したい場合。
:g/: String/norm Ipub
norm でノーマルモードコマンドを実行。I は行頭で挿入モード。
#:v/pattern/command - 逆グローバルコマンド
コマンドの意味: pattern にマッチしない全ての行に対して command を実行する。:g! と同じ。
利用シーン: 特定のパターンを含まない行を削除したいとき
// テスト関数だけを残して、他の行を全て削除したい 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() { /* ... */ } }
:v/test/d
test を含まない行が全て削除される。
#:sort - ソート
コマンドの意味: 選択範囲または全体をソートする。
オプション:
:sort: 昇順ソート:sort!: 降順ソート:sort u: 重複を削除してソート:sort n: 数値としてソート:sort i: 大文字小文字を無視
利用シーン: import文やCargo.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"
依存関係をアルファベット順にしたい場合、依存関係の範囲を選択して。
:'<,'>sort
結果:
[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を整理したいとき
use std::collections::HashMap; use std::sync::Arc; use std::collections::HashMap; // 重複 use std::sync::Arc; // 重複 use tokio::sync::RwLock;
:sort u
重複が削除されて1つずつになる。
#:%!command - 外部コマンドでフィルタ
コマンドの意味: バッファ全体を外部コマンドに渡し、結果で置き換える。
利用シーン: 外部ツールでコードをフォーマットしたいとき
#例1: JSONを整形
APIレスポンスのサンプルを整形したい場合。
{"id":1,"name":"John","email":"john@example.com","created_at":"2026-01-26T10:00:00Z"}
:%!jq .
結果:
{ "id": 1, "name": "John", "email": "john@example.com", "created_at": "2026-01-26T10:00:00Z" }
#例2: 選択範囲だけをフィルタ
ビジュアルモードで選択してから。
:'<,'>!sort | uniq
選択範囲だけがソートされて重複削除される。
#:read !command - 外部コマンドの出力を挿入
コマンドの意味: 外部コマンドの出力をカーソル位置に挿入する。
利用シーン: 現在の日時やコマンド結果をコードに挿入したいとき
// 現在のgitコミットハッシュを定数として埋め込みたい const GIT_HASH: &str = "";
:read !git rev-parse --short HEAD
カーソル行の下にコミットハッシュが挿入される。
const GIT_HASH: &str = ""; a1b2c3d
あとは手動で整形するか、マクロで自動化する。
#:w !command - バッファを外部コマンドに渡す
コマンドの意味: バッファの内容を外部コマンドの標準入力として渡す(ファイルは変更されない)。
利用シーン: 保存せずにコードをチェックしたいとき
:w !rustfmt --check
現在のバッファをrustfmtに渡してフォーマットチェック。エラーがあれば表示される。
:w !wc -l
バッファの行数をカウント。
#:norm - 複数行にノーマルモードコマンド実行
コマンドの意味: 指定範囲の各行にノーマルモードコマンドを実行する。
利用シーン: 複数行に同じ編集を適用したいとき
#例1: 全行の末尾にカンマを追加
let items = vec![ "apple" "banana" "cherry" ];
文字列の行を選択して。
:'<,'>norm A,
A は行末で挿入モード、, を追加。
結果:
let items = vec![ "apple", "banana", "cherry", ];
#例2: 全行の先頭にテキストを追加
id name email created_at
これをSQLのSELECT句にしたい場合。
:%norm Iuser.
結果:
user.id user.name user.email user.created_at
#例3: マクロとの組み合わせ
マクロを記録してから :norm で適用することも可能。
:%norm @a
全行にマクロ a を実行。
#:earlier / :later - 時間ベースのundo
コマンドの意味: 指定した時間前/後の状態に戻す。
利用シーン: 「5分前の状態に戻したい」というとき
:earlier 5m
5分前の状態に戻る。
:later 5m
5分後の状態に進む。
:earlier 10
10回分のundoを実行。
通常の u によるundoはツリー構造になっているため、分岐した編集履歴を辿るのが難しい。:earlier なら時間指定で確実に戻れる。
#:changes - 変更履歴の確認
コマンドの意味: バッファ内の変更履歴を一覧表示する。
利用シーン: どこを編集したか確認したいとき
:changes
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 - ジャンプリストの確認
コマンドの意味: ジャンプ履歴を一覧表示する。
利用シーン: 最近どのファイルのどこにジャンプしたか確認したいとき
:jumps
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 - レジスタの確認
コマンドの意味: 全レジスタの内容を一覧表示する。
利用シーン: ヤンクや削除した内容を確認したいとき
:registers
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 - 複数ファイルに対する操作
コマンドの意味: 引数リストの全ファイルに対してコマンドを実行する。
利用シーン: 複数ファイルで一括置換したいとき
:args src/handlers/*.rs :argdo %s/unwrap()/expect("Failed")/gc | update
src/handlers/配下の全.rsファイルを引数リストに追加- 各ファイルで置換を実行し、
updateで保存
update は変更があった場合のみ保存する。
#:bufdo - 全バッファに対する操作
コマンドの意味: 開いている全バッファに対してコマンドを実行する。
利用シーン: 開いている全ファイルで一括操作したいとき
:bufdo %s/TODO/FIXME/g | update
開いている全ファイルで TODO を FIXME に置換して保存。
#:cdo - quickfixリストに対する操作
コマンドの意味: quickfixリストの各エントリに対してコマンドを実行する。
利用シーン: grepやLSPの検索結果に対して一括操作したいとき
:grep -r "old_function" src/ :cdo s/old_function/new_function/g | update
grepでquickfixリストに結果を格納- 各マッチ箇所で置換を実行
#:substitute の高度な使い方
#キャプチャグループを使う
利用シーン: パターンの一部を保持しながら置換したいとき
fn get_user(id: i64) -> User { } fn get_article(id: i64) -> Article { } fn get_comment(id: i64) -> Comment { }
戻り値を Result<T, AppError> にしたい場合。
:%s/-> \(\w\+\) {/-> Result<\1, AppError> {/g
\(\w\+\) でキャプチャして \1 で参照。
結果:
fn get_user(id: i64) -> Result<User, AppError> { } fn get_article(id: i64) -> Result<Article, AppError> { } fn get_comment(id: i64) -> Result<Comment, AppError> { }
#式を使う(=)
利用シーン: 動的な値で置換したいとき
const A: i32 = 0; const B: i32 = 0; const C: i32 = 0;
連番にしたい場合。
:let n=0 | %s/= 0/\='= ' . (n) . (let n+=1)/g
少し複雑だが、動的な置換が可能。
#大文字小文字を変換
:%s/\<user\>/\U&/g
user を USER に変換。
\U: 以降を大文字に\L: 以降を小文字に\u: 次の1文字を大文字に\l: 次の1文字を小文字に&: マッチした文字列全体
#:redir - コマンド出力をキャプチャ
コマンドの意味: コマンドの出力をファイルやレジスタにリダイレクトする。
利用シーン: :messages や :version の出力を保存したいとき
:redir @a :messages :redir END
メッセージ履歴がレジスタ a に保存される。"ap で貼り付け。
:redir > output.txt :version :redir END
ファイルに保存する場合。
#おわりに
コマンドモードのExコマンドは、プラグインの設定に関係なくどの環境でも同じように動作する。
特に覚えておきたいのは:
:%s/old/new/gc- 確認付き置換の基本:g/pattern/d- パターンにマッチする行を削除:%!command- 外部コマンドでフィルタ:earlier 5m- 5分前の状態に戻す:argdo/:bufdo- 複数ファイルへの一括操作
これらは地味だが、知っているかどうかで作業効率が大きく変わる。
:help :substitute や :help :global で詳細を確認しながら、少しずつ使いこなしていきたい。