0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Mac と Ubuntu で同じ開発環境を作る 3

0
Posted at

Mac と Ubuntu で同じ開発環境を作る dotfiles 設計メモ 第3回:Neovim・VS Code Neovim・IME を共通化する

はじめに

前回は、Ghostty / zsh / tmux の設定を例に、Mac と Ubuntu の OS 差分をどう吸収しているかを書きました。

今回は、Neovim の設定を扱います。

自分の dotfiles では、Neovim を次の2つの環境で使っています。

  • ターミナル上の Neovim
  • VS Code Neovim

さらに、日本語入力 IME が Esc 後も残る問題にも対応しています。

この記事では、次の内容を書きます。

  • Neovim 設定のモジュール化
  • VS Code Neovim との両対応
  • lazy.nvim と lazy-lock.json による再現性
  • InsertLeave を使った IME 自動オフ

検証環境

この記事の内容は、以下のような環境を前提にしています。

項目 環境
macOS TODO: 例 macOS 15.x
Ubuntu TODO: 例 Ubuntu 24.04 LTS
Ubuntu のセッション X11
エディタ Neovim
VS Code 拡張 VS Code Neovim
プラグイン管理 lazy.nvim
Linux 側の IME TODO: 例 fcitx5
macOS 側の IME 切り替え TODO: 例 macism

Ubuntu 側は X11 前提です。

Neovim 設定の構成

Neovim の設定は Lua で書き、役割ごとにファイルを分割しています。

init.lua は読み込みの司令塔に徹し、実体は core/plugins/ に分けています。

nvim/
└── .config/
    └── nvim/
        ├── init.lua
        ├── lua/
        │   ├── core/
        │   │   ├── options.lua
        │   │   ├── keymaps.lua
        │   │   ├── autocmds.lua
        │   │   └── lazy.lua
        │   ├── plugins/
        │   │   ├── editor.lua
        │   │   ├── lsp.lua
        │   │   └── ui.lua
        │   └── vsc/
        │       └── keymaps.lua
        └── lazy-lock.json

init.lua は次のようにしています。

require("core.options")
require("core.keymaps")
require("core.autocmds")
require("core.lazy")

if vim.g.vscode then
  require("vsc.keymaps")
end

init.lua にすべてを書くのではなく、役割ごとに分けています。

自分の場合は、だいたい次のように分けています。

core/options.lua   基本オプション
core/keymaps.lua   共通キーマップ
core/autocmds.lua  自動コマンド
core/lazy.lua      lazy.nvim の初期化
plugins/*.lua      プラグイン定義
vsc/keymaps.lua    VS Code Neovim 用キーマップ

こうしておくと、あとから設定を見返したときに、どこを触ればよいか分かりやすくなります。

VS Code Neovim との両対応

Neovim はターミナルでも使いますが、VS Code Neovim でも使っています。

VS Code Neovim で起動した場合は vim.g.vscode が立つので、それを見て VS Code 用の設定を読み込んでいます。

if vim.g.vscode then
  require("vsc.keymaps")
end

これにより、ターミナル Neovim では通常の設定を使いつつ、VS Code Neovim では VS Code に合わせたキーマップを追加できます。

プラグインを環境ごとに読み分ける

プラグインも、ターミナル Neovim と VS Code Neovim で読み分けています。

例えば、ターミナル Neovim では EasyMotion を使い、VS Code Neovim では別のプラグインを使う、というような分け方です。

{
  "easymotion/vim-easymotion",
  cond = function()
    return vim.g.vscode == nil
  end,
},
{
  "smoka7/hop.nvim",
  cond = function()
    return vim.g.vscode ~= nil
  end,
},

ポイントは、VS Code Neovim 上では不要なプラグインまで読み込まないことです。

同じ init.lua を使いながら、実行環境によって必要な設定だけを読み込むようにしています。

lazy-lock.json で再現性を持たせる

プラグイン管理には lazy.nvim を使っています。

dotfiles を複数マシンで使う場合、「設定ファイルが同じ」だけでは不十分です。

プラグインのバージョンが違うと、同じ設定でも挙動が変わることがあります。

そこで、lazy-lock.json をリポジトリに含めています。

lazy-lock.json によって、各プラグインのコミットハッシュを固定できます。

これは package-lock.json と同じような考え方で、別マシンでも同じバージョンの環境を再現しやすくするためです。

新しい環境では、プラグインを同期したあとに lockfile の状態へ戻すことで、できるだけ同じ状態に揃えられます。

:Lazy sync
:Lazy restore

dotfiles を育てていくうえで、再現性はかなり大事だと感じています。

ハマった課題:日本語入力 IME が Esc 後も残る

一番「自分ごと」として取り組んだのが、Neovim での日本語入力問題です。

インサートモードで日本語を打ち、Esc でノーマルモードに戻っても IME が ON のままだと、続くキー操作が文字入力として扱われてしまいます。

例えば、ノーマルモードで jk を押したいのに、日本語入力中の文字として食われることがあります。

これは、Vim のモードと IME の状態が独立しているために起きる問題です。

Vim のモード:
Insert mode / Normal mode

OS の IME:
ON / OFF

Vim 側が Normal mode に戻っても、OS 側の IME が自動で OFF になるわけではありません。

そのため、Vim のモード遷移に合わせて IME を明示的に OFF にする必要があります。

InsertLeave で IME をオフにする

解決策は、インサートモードを抜けるタイミングで IME を強制的にオフにすることでした。

Neovim では InsertLeave の autocmd を使えます。

local ime_group = vim.api.nvim_create_augroup("IMEAutoToggle", { clear = true })

vim.api.nvim_create_autocmd("InsertLeave", {
  group = ime_group,
  callback = function()
    if vim.fn.executable("fcitx5-remote") == 1 then
      -- Linux / fcitx5
      vim.fn.system({ "fcitx5-remote", "-c" })
    elseif vim.fn.has("macunix") == 1 and vim.fn.executable("macism") == 1 then
      -- macOS / macism
      vim.fn.system({ "macism", "com.apple.keylayout.ABC" })
    end
  end,
})

最初は Linux 用に fcitx5-remote -c をそのまま呼んでいました。

vim.fn.system("fcitx5-remote -c")

しかし、この書き方だと macOS や fcitx5-remote が入っていない環境で失敗します。

そこで、vim.fn.executable()vim.fn.has("macunix") を使って、使えるコマンドがある場合だけ実行するようにしました。

Ctrl-C で抜ける場合の注意

この設定は、Esc でインサートモードを抜ける運用を前提にしています。

Ctrl-C でインサートモードを抜ける場合は、InsertLeave が発火しないため、挙動が変わります。

普段から Ctrl-C で抜ける人は、別のイベントやキーマップで対応する必要があります。

自分は Esc で抜ける運用なので、InsertLeave で十分でした。

IME 対応で理解したこと

この問題に取り組むまで、IME はエディタ側で何となく制御されているものだと思っていました。

しかし実際には、Vim のモードと OS の IME は独立しています。

そのため、両者の状態を揃えるには、どこかで明示的に橋渡しする必要があります。

Insert mode を抜ける
  -> InsertLeave が発火する
  -> fcitx5-remote / macism を呼ぶ
  -> OS の IME を OFF にする

この構造を理解したことで、「なぜ Esc 後に日本語入力が残るのか」が腑に落ちました。

症状だけを消すのではなく、仕組みを理解してから直せたのが大きかったです。

今回のまとめ

今回は、Neovim / VS Code Neovim / IME の設定について書きました。

やったことをまとめると、次の通りです。

  • Neovim 設定を core/plugins/vsc/ に分割した
  • init.lua は読み込みの司令塔にした
  • vim.g.vscode で VS Code Neovim かどうかを判定した
  • プラグインを cond で環境ごとに読み分けた
  • lazy-lock.json を管理してプラグインバージョンを固定した
  • InsertLeave で IME を自動的に OFF にした
  • executable() や OS 判定で、存在しないコマンドを呼ばないようにした

Neovim の設定は、放っておくとすぐに巨大な init.lua になりがちです。

役割ごとに分け、実行環境ごとの違いを小さな条件分岐に閉じ込めることで、Mac と Ubuntu、さらに VS Code Neovim でも扱いやすくなりました。

シリーズ全体のまとめ

3回に分けて、Mac と Ubuntu で同じ開発環境を作るための dotfiles 設計について書きました。

振り返ると、設定ファイルを両対応にしようとするだけで、次のような点を調べることになりました。

  • ディストリビューションやインストール方法でファイルの置き場所が変わること
  • Homebrew の prefix が環境によって異なること
  • /usr/share/usr/local/share の使い分け
  • 設定を ~/.config 配下にまとめる XDG Base Directory の考え方
  • uname や環境変数で実行環境を判定する方法
  • X11 / Wayland でクリップボード連携の方法が変わること
  • tmux の copy-mode と OS クリップボードの関係
  • Vim のモードと IME の状態が独立していること
  • lockfile でプラグインのバージョンを固定し、環境を再現可能にすること

どれも単体では小さな知識ですが、「同じ操作感をどの環境でも使いたい」という目標を追ううちに、自然と必要になったものです。

dotfiles は一度作って終わりではなく、新しいツールを試したり、別の OS に移ったりするたびに少しずつ手を入れていくものだと感じています。

設定ファイルは地味ですが、自分の道具をどう理解し、どう手入れしているかがそのまま表れる場所だと思っています。

同じように環境を育てている方の参考になれば嬉しいです。

今後やりたいこと

今後は、次のあたりを整備したいと考えています。

  • dotfiles のインストールスクリプト化
  • OS ごとの symlink 展開を自動化する
  • macOS / Ubuntu で必要なパッケージ一覧を管理する
  • Neovim のプラグイン更新手順を整理する
  • 設定ファイルのテストや lint を導入する
  • 公開用 dotfiles と非公開設定の境界をより明確にする

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?