entry-header-eye-catch.html
entry-title-container.html

entry-header-author-info.html
Article by

NeovimでPHPを書いたり読んだりしている24新卒が環境を紹介します

こんにちは。ピクシブ24新卒のyuyukunです。

現在はOJTとしてpixivの開発・運用を行う部署に配属されています。PHPを勉強しながら、pixivの開発で直面している課題を解決したり、そもそもpixivではどうやって機能を提供しているのか?について日々学んでいます。

社内では、特にPHPでの開発のためにPhpStormを利用している方が多くいます。僕は普段からエディタとしてNeovimを利用しており使い慣れていることから、NeovimでPHPの開発環境を構築してみることにしました。

今回は、僕がNeovimを使ってpixivのコードを読んだり、開発したりしている環境を紹介します。一部ではありますが、主な機能として何を使っているのか、使ってみてどうだったかを書いてみようと思います。

スクリーンショットはサンプルのPHPプロジェクトを実際にNeovimで開いて編集している途中の様子で、以下の内容が含まれています。

  • エクスプローラ
  • 開いているファイルのタブ、パンくずリストの表示
  • シンタックスハイライト
  • コード補完、シグネチャの表示、ドキュメントの表示
  • 言語サーバやLinterによるコード内にあるエラーの表示

共通設定

以降で紹介するプラグインはlazy.nvimというプラグインマネージャを利用して管理しています。 github.com

Neovim GUIにはNeovideを利用しています。 neovide.dev

ターミナルでは表現できないような、スムーズなスクロールやアニメーション、カーソルのジャンプに合わせたパーティクルの表示などができるものです。コードを書くのが楽しくなるので使っています。

しかし、日本語を入力する我々にとって重要なIMEの対応が十分ではなく、入力中の文字を確定するまでテキストが表示されません。僕は、NeovideではIMEを無効化する代わりにskkeletonを使って、Neovim上でIMEを動かすことでこれを解決しています。 github.com

カラースキームはayuを利用しています。 github.com

僕がプログラミングを始めた時から気にいっているもので、他にも一目惚れしてNeovim向けに自作したダークテーマなどもあるのですが、最近はまたayuに戻ってきています。

関数やfor文、if文などのスコープと括弧の対応付けにはindent-blankline.nvim、rainbow-delimiters.nvimを組み合わせて使っています。VS Codeにも同等の機能としてBracket pair colorizationがありますね。

これらのプラグインではスコープと括弧が同じ色で対応させられるため分かりやすく、気にいっています。 github.com github.com

エクスプローラ / タブ / パンくずリスト

ファイル操作系の機能を追加するプラグインです。

エクスプローラにはnvim-tree.lua、エディタ上部にある開いているファイルの一覧を表示するタブはbufferline.nvim、ファイルのパンくずリストを表示するためにnvim-navicを利用しています。 github.com github.com github.com

Neovim v0.8.0+で追加されたwinbarがちょうどパンくずリストを表示する領域になっています。nvim-navicを使って、その領域に書きこむだけで簡単に使えます1

vim.o.winbar = "%{%v:lua.require'nvim-navic'.get_location()%}"

スクリーンショットでは Controllers > HelloWorldController.php > HelloWorldController > handle となっている部分で、今カーソルが handle メソッド内にあるということを示しています。

これに加えて、ファジーファインダーも必要です。pixivのように大きなコードの中から探し出すためには、ファイル名の検索だけでなく、ファイルの内容で絞りこみして使用箇所を探すことが楽にできる必要があります。

これには、telescope.nvimを使います。telescope.nvimではripgrepを利用することで高速な検索を実現し、表示されるfloating window上でファイルの中身のプレビューを確認しながら、開きたいファイルを選択できます。 github.com

これは Hello を検索した時にファイル名とファイルの中身でヒットした一覧を表示している例です。

コード補完 / シグネチャの表示 / 定義ジャンプ

NeovimはLanguage Server Protocol(LSP)をサポートしています。そのため、各プログラミング言語の言語サーバへクライアントとして接続することで、コード補完やシグネチャの表示、定義ジャンプといった機能が使えるようになります。

コード補完にはnvim-cmp、言語サーバによる補完候補をnvim-cmpで表示するためにcmp-nvim-lspを使っています。 github.com github.com

言語サーバは多くの場合でバイナリとして配布されており、これを事前に実行できるようにしておく必要があります。言語サーバを手動で取得する代わりに、自動的にインストールしたり更新したりするためにmason.nvimを使っています。そして、これら言語サーバの設定のために、nvim-lspconfigを使っています。 github.com github.com

PHPの言語サーバにはIntelephensePhpactorなどがあります。Intelephenseは無料でも利用できますが、ピクシブではプレミアム機能のライセンスは希望すれば会社の経費として購入できます。そのため、今回はIntelephenseを選択しました。

コードのドキュメント表示は、VS CodeやPhpStormを使ったときにコード上にマウスカーソルを重ねると表示されるものと同じです。このような言語サーバによる情報を表示するための様々なUIを提供してくれるlspsaga.nvimを使っています。 github.com

定義ジャンプや関数の参照箇所の検索にはlspsaga.nvimでもUIを提供していますが、代わりに同様のUIを提供しているtrouble.nvimも使っています。 github.com

参照箇所が複数ある場合に、ファイルの中身をプレビューしながら見れるようになっているのですが、lspsaga.nvimのようにfloating windowではなく、候補一覧は分割された画面下部、そしてその中身は最後に使用していたwindowで表示するという使い方が僕に合っていました。

Linter / Formatter

PHPStanやEasy Coding StandardのようなLinter、Formatterは言語サーバではないのですが、これらの静的解析結果のエラーを表示したり、コードアクションを呼び出したりできると便利です。

こういった機能はnone.nvim2で実現できます3。mason.nvimが言語サーバのバイナリを取得するのと同様にLinterやFormatterのバイナリを取得してくれるのがmason-null-ls.nvimです。 github.com github.com

保存時に自動でフォーマットを適用するのはlsp-format.nvimを使っています。今までは自分でBufWritePreイベントにautocmdを用意していたのですが、これを管理してくれるプラグインに任せることにしました。

今は保存時の自動フォーマット機能しか利用していませんが、プラグインを利用することで設定が集約できたり、フォーマッタごとの設定を書きたくなったときに便利に使えるかもしれない、と期待しています。 github.com

mason-null-ls.nvimではEasy Coding Standardをインストールできますが、none-ls.nvimには用意されていないので、自分で用意しています。none-ls.nvimのbuiltinsで用意しているものを参考に、同じようにファイルを作成しているだけです。 github.com

Git操作

Neovimに限らず、普段のGitの操作にはlazygitというGit TUIを利用しています。移動がVimと同様にHJKLなのも嬉しいですし、行ごとのステージングもやりやすいので綺麗なコミット作りも意識できてオススメです。 github.com

Neovimではlazygitをfloating windowで表示するプラグインとしてlazygit.nvimがあり、これを使うことでターミナルへ行き来せずともNeovim上でGit操作が完結します。 github.com

また、実際に開発に利用してみて、blameをもっと簡単に表示する機能が欲しい場面があることに気がつきました。

コードがいつ、誰によって追加されたのかという情報を見る機能なのですが、初めて参画するプロダクトのコードはもちろん、pixivのような大規模なコードで多くの開発者によって書かれたコードの背景を知るためにも、この機能がとても便利でした。 github.com

画面が3分割されていて、真ん中のソースコードと左側のblameの表示が行で対応しています。また、右側にあるように、コミットの詳細も見ることができます。

開発環境へコードを転送する

pixivの開発では、開発環境として用意されたサーバーにファイルを配置することで動作確認をすることができます。GitでコミットしてGitLabにpush、その後開発環境からpullしても良いのですが、コードの変更と動作確認のサイクルを高速に回すのは不向きな方法です。

VS CodeのRemote SSHや、Neovimを開発環境で動かすことで直接ファイルを変更すればこの問題は解決できるのですが、今回は手元のNeovimで編集し、変更をrsyncで開発環境へ転送することにしました。

これはtransfer.nvimというプラグインを使って実現しています。

以下のようなautocmdを用意することで、ファイルの保存時にlsp-format.nvimによるフォーマットを BufWritePre イベントで適用し4、フォーマットの適用後の BufWritePost イベントでファイルを転送しています。

vim.api.nvim_create_autocmd("BufWritePost", {
    group = "transfer",
    pattern = { "*" },
    callback = function()
        local f = io.open(vim.fn.getcwd() .. "/.nvim/deployment.lua", "r")

        -- transfer.nvim用の転送設定が無いときは以降を実行しない
        if f == nil then
            return
        end

        io.close(f)

        -- 開いているバッッファのファイルパスを指定して転送
        vim.cmd([[let g:current_buf_file_path = expand('%:p')]])
        vim.cmd("TransferUpload " .. vim.g.current_buf_file_path)
    end,
})

おわりに

基本的にはVS CodeやPhpStormにはあるあの機能をNeovimでも実現していくのですが、自分が使いやすいようにカスタマイズして作りあげる過程も楽しいものです。VS CodeやPhpStormには元から用意されているような便利な機能にはいつも驚かされますが、Neovimでも十分に開発ができそうだと感じています。

社内には他にもNeovimやVimを利用している方、Emacsを使っている方がいます。アドベントカレンダーやLTイベントの録画が公開されているので興味があればぜひ調べてみてください。また、僕の設定はdotfilesリポジトリがあるので、こちらも気になる方はぜひ覗いてみてください5github.com


  1. 執筆時(2024年4月23日)では開発中のNeovim Nightly v0.10.0を使うと、更に便利になりそうなdropbar.nvimというプラグインもあるようなのでこちらも気になっています。
  2. null-ls.nvimの開発を引き継いだforkプロジェクトです。
  3. IntelephenseのようにPHPStanの静的解析結果を表示してくれる場合はnone-ls.nvimを使わなくとも実現できました。そのため、今回のPHPの環境構築にはFormatterを利用するためだけにnone-ls.nvimを利用しています。
  4. BufWritePreイベントで実行するには非同期ではなく同期で実行するように sync = true をセットする必要があります。フォーマットされたコードを転送する必要が無いのであれば非同期でも問題ないと思います。
  5. いい書き方とかがあれば教えてもらえると喜びます。
yuyukun
2024年4月に新卒入社。Pastelaリリースをきっかけにお絵描きを始めました。東方Project、VTuberが好きで今はFF14にドハマりしています。