NeovimでのUnreal Engine開発環境を快適にするプラグイン群(UEP.nvimなど)の今週のアップデート情報です。
今週は、先日いただいたフィードバックを元にした重要なバグ修正と、今後の拡張性を見据えたプラグイン構成の整理(新プラグインの追加)が中心です!
🐛 バグ修正
1. UNL.nvim: Windows環境でのbatファイル文字コード問題の修正
Windows環境において、.batファイルのエンコーディング(文字コード)が原因でコマンドが正常に動作しないケースがある、というご報告をいただきました。(本当にありがとうございます!)
これは、cmd.exeがShift_JIS (CP932)を期待するのに対し、プラグイン側の.batファイルがUTF-8で作成されていたために発生していました。
対策として、.batファイルの先頭でchcp 65001 > nulを実行して文字コードをUTF-8に固定し、さらにリポジトリ側で.gitattributesを設定して改行コードをCRLFに強制することで、あらゆるWindows環境で安定動作するように根本的な修正を行いました。
(当方でバグの再現ができなかったのでもしダメだったらすいません)
2. UEP.nvim: :UEP files の検索対象漏れを修正
シェーダーファイル(.ush, .usf)が、:UEP filesコマンドの検索結果に出てこない不具合を修正しました。
お手数ですが、:UEP files! (最後に!が付きます)を実行して、ファイルキャッシュの再構築をお願いします。
✨ 新機能・変更点
1. シンタックス専用プラグイン USX.nvim を作成しました
これまで、Unreal C++のシンタックスハイライト(queriesファイルなど)は、tree-sitter-unreal-cppパーサー本体に同梱していました。
これを、シンタックス定義だけを持つ軽量プラグイン USX.nvim として完全に分離しました。また USX.nvimを使うことで.uprojectもjsonとして扱うようになりjson用Tree-sitterをインストールする事でシンタックスエラーなども見た目でわかるようになります。
-
メリット:
シンタックスハイライト(構文色付け)だけが欲しい場合に、重いTree-sitterパーサー(tree-sitter-unreal-cpp)をtree-sitterのインストールとは別にプラグインとしてlazyなどに書かなくてもよくなります
2. Unrealシェーダー (.ush, .usf) のハイライトに対応
Unreal Engineのシェーダーファイル(.ush, .usf)のためのtree-sitter-unreal-shaderパーサーを新たに作成しました!
tree-sitterへのインストールとUSX.nvimの両方を使うことでシェーダーファイルも構文に基づいたハイライトが有効になります。
また、tree-sitter-unreal-cpp本体も更新し、UObjectの解析精度を向上させています。
tree-sittere mainレポジトリ使用の場合は下記のようにインストールしてください
config = function(_, opts)
vim.api.nvim_create_autocmd('User', { pattern = 'TSUpdate',
callback = function()
local parsers = require('nvim-treesitter.parsers')
parsers.cpp = {
install_info = {
url = 'https://github.com/taku25/tree-sitter-unreal-cpp',
revision = '89f3408b2f701a8b002c9ea690ae2d24bb2aae49',
},
}
parsers.ushader = {
install_info = {
url = 'https://github.com/taku25/tree-sitter-unreal-shader',
revision = '26f0617475bb5d5accb4d55bd4cc5facbca81bbd',
},
}
end})
local langs = {"cpp", "ushader","json"}
require("nvim-treesitter").install(langs)
local group = vim.api.nvim_create_augroup('MyTreesitter', { clear = true })
vim.api.nvim_create_autocmd('FileType', {
group = group,
pattern = langs,
callback = function(args)
vim.treesitter.start(args.buf)
vim.bo[args.buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
end,
})
end
3. ✨クラスと構造体の検索機能を追加! (classes, structs)
UnrealEngineではしばしばファイル名とクラス名が違うときがあります、そのような時にファイル名ではなく、シンボル名から定義へジャンプしたい、シンボル名検索専用のコマンドを追加しました。パーサーを強化したことで、structも対象になります。
-
:UEP classes[!]:プロジェクトのクラス一覧をピッカーで表示し、選択したクラスの定義ファイルへジャンプします。 -
:UEP structs[!]:プロジェクトの構造体一覧をピッカーで表示し、選択した構造体の定義ファイルへジャンプします。 -
キャッシュ戦略:
:UEP filesと同様に、コマンド実行時にキャッシュが存在すればそれを利用し、!を付けることでキャッシュを再生成します。 -
スコープ:
[Game|Engine|Editor]、[--no-deps|--all-deps]フラグに対応しています。
4. ✨クラス定義へジャンプ機能を追加! (goto_definition)
LSPの定義ジャンプ (:LspDefinition や gd) を使うと、Unreal C++ では先行宣言 (class AMyActor;) に飛んじゃうこと、よくありますよね? これ、地味に面倒だったりします。
そこで今回! UEP.nvim がキャッシュしているプロジェクト全体のクラス情報とモジュール依存関係を使って、先行宣言を華麗にスルー! ヘッダーファイルの定義直接ジャンプする :UDEV goto_definition コマンドを追加しました!🎉
-
どう動くの?: カーソル下のクラス名(例:
AMyActor)を拾って、賢く定義ファイルを探します。- まず、今いるモジュールの中を探します。
- なければ、直接依存しているモジュール(Shallow Dependencies)を探します。
- それでもなければ、間接的に依存しているモジュール(Deep Dependencies)まで探しに行きます。
- 見つからなかったら?: UEPキャッシュに無い場合は、ちゃんとLSPの定義ジャンプを呼び出すので安心です 👍。
-
クラスを選んでジャンプも!: コマンドに
!を付けて (:UDEV goto_definition!) 実行すれば、カーソル位置は関係なし! プロジェクト全体のクラス一覧からジャンプ先を選べます。
キーマップ例:
標準のgd (LSP) と使い分けると便利ですよ!
-- LSP定義ジャンプ (今まで通り)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = "LSP Definition" })
-- UEP定義ジャンプ (カーソル下の単語にジャンプ!)
vim.keymap.set('n', '<leader><C-]>', function() require('UnrealDev.api').goto_definition({ has_bang = false }) end, { desc = "UEP: Go to Definition (Cursor)" })
-- UEP定義ジャンプ (クラスを選んでジャンプ!)
vim.keymap.set('n', '<leader>gD', function() require('UnrealDev.api').goto_definition({ has_bang = true }) end, { desc = "UEP: Go to Definition (Picker)" })
5. 統合ラッパープラグイン UnrealDev.nvim の登場
UEP.nvim, UCM.nvim, UBT.nvim... と複数のプラグインを個別に管理・ロードするのが大変、という問題を解決するため、これらをまとめる薄いラッパープラグイン UnrealDev.nvim を作成しました。
-
メリット①(インストール):
lazy.nvimなどでプラグインを導入する際、UnrealDev.nvimを1行追加し、そのdependenciesに関連プラグイン(UBT.nvimなど)を設定するだけで、一括で読み込めるようになります。
return {
'taku25/UnrealDev.nvim/',
ft = { "cpp", "c" },
cmd = {
"UDEV",
},
dependencies = {
"j-hui/fidget.nvim",
"nvim-telescope/telescope.nvim",
'taku25/UNL.nvim',
'taku25/UEP.nvim',
'taku25/UBT.nvim',
'taku25/UCM.nvim',
'taku25/USH.nvim',
'taku25/ULG.nvim',
{
'taku25/USX.nvim',
lazy=false,
},
},
}
-
メリット②(コマンド統一):
:UBT Buildや:UCM Newのようにバラバラだったコマンドを、**:UDEV Buildや:UDEV New**のように、:UDEVという共通のプレフィックスから全ての機能が使えるようになりました。
このプラグインは、あくまでコマンドとAPIの薄いラッパーです。今まで通りの使い方(各プラグインを直接呼び出す)をしても全く問題ありません。
ご注意:
一部のコマンド名が重複を避けるために変更されています。詳しくはGitHubのレポジトリをご覧ください。
またコマンドだけではなくAPIもラップしているので以下のようにUnrealDev.api経由でも今までと同じようにapiが使えます
local is_open_ui = false
config = function(_, opts)
require("UnrealDev").setup(opts)
vim.keymap.set("n", "<leader>a",
function()
require("UnrealDev.api").switch_file()
end,
{ noremap = true, silent = true })
vim.keymap.set('n', '<leader>gf', require('UEP.api').open_file, { noremap = true, silent = true })
vim.keymap.set("n", "<C-s>",
function()
require("UnrealDev.api").is_process_running(
{
process_name = "UnrealEditor",
on_complete = function(is_runing)
if is_runing == false then
require("UnrealDev.api").build({})
else
require("UnrealDev.api").remote_command("livecoding.compile")
end
end
})
end,
{ noremap = true, silent = true })
local open_ui = function()
if is_open_ui == true then
return
end
local project = require("UNL.api").find_project(vim.loop.cwd() )
--
if project and project.root then
require("UnrealDev.api").tree()
require("UnrealDev.api").start_log()
is_open_ui = true
end
end
open_ui()
vim.api.nvim_create_autocmd("FileType", {
pattern = { "cpp", "c" },
callback = function()
open_ui()
end,
})
end,
🚀 今後の予定
今週は基盤整備が中心でしたが、来週以降、NeovimをさらにRiderやVisual Studioに近づけるため、以下の機能開発を予定しています。
-
リファクタリング機能
- 先行宣言の自動追加(先日作成した「自動インクルード」の先行宣言版)
-
ファイルリネーム機能の強化(ファイルリネーム時に、
#include文も自動で修正する)
-
UProject
- 正確なキャッシュ作成 現状のキャッシュファイルはGame/Engineという大きなくくりでキャッシュをしており、Editorモジュールの判別が難しいです。今後はUProjectを正確に読み取りEditor Module / Runtime Moduleを判別できるよにする。またビルド構成も自動化できたらと思っています
機能リクエストや「こういうのが欲しい」というアイデアがあれば、ぜひGitHubのIssuesやこの記事のコメントに、日本語でOKなのでお気軽に書き込んでください!
☕ 雑談とお知らせ
最後にお知らせです。
来週は諸事情によりまとまった作業時間の確保が難しいため、開発・更新が一時的にストップする予定です。
やる気が枯渇したわけでは決してないのでご安心ください!
(むしろ、やる気はあるのですが、自分が使う範囲では十分すぎる機能が入ってきたため、次に何を作ろうか(=皆さんは何に困っているか)と悩む時間が増えてきている感じです)
では。今週のアップデート情報でした






