📘

nvimでphpを開くlspの設定(intelephenseで補完とか)

2025/01/10に公開

キラキラしたやつはさておいて、lspを入れといた方がいいかなと。となるとphpではまあほぼほぼintelephenseとなるのだが、これに関してあんまり記事がなかったような、みつけられてないだけかもしれないけど。

環境

$ nvim --version
NVIM v0.10.3
Build type: RelWithDebInfo
LuaJIT 2.1.1713484068
Run "nvim -V1 -v" for more info

init.lua

~/.config/nvim/init.lua
-- Lazy.nvimの初期化
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
    vim.fn.system({
        "git",
        "clone",
        "--filter=blob:none",
        "https://github.com/folke/lazy.nvim.git",
        "--branch=stable",
        lazypath,
    })
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup({
    -- Mason (LSPやツールのインストーラ)
    {
        "williamboman/mason.nvim",
        config = function()
            require("mason").setup()
        end,
    },

    -- MasonとLSPconfigの連携
    {
        "williamboman/mason-lspconfig.nvim",
        dependencies = { "neovim/nvim-lspconfig" },
        config = function()
            require("mason-lspconfig").setup({
                ensure_installed = { "jsonls" }, -- JSON LSPをインストール
            })

            -- LSPの設定
            local lspconfig = require("lspconfig")
            lspconfig.jsonls.setup({
                settings = {
                    json = {
                        schemas = require("schemastore").json.schemas(), -- スキーマサポートを追加
                        validate = { enable = true }, -- 検証を有効化
                    },
                },
            })
        end,
    },

    -- JSONスキーマのサポート (オプション)
    {
        "b0o/schemastore.nvim",
    },
})

などとしてnvimを起動

ここではphpではなくjsonで様子を見ている。適当なjsonを開いてミスってやると

こうなるはずだ。

--- a/init.lua
+++ b/init.lua
@@ -33,13 +33,42 @@ require("lazy").setup({

             -- LSPの設定
             local lspconfig = require("lspconfig")
+            local capabilities = require("cmp_nvim_lsp").default_capabilities()
+
+            -- JSON LSPの設定
             lspconfig.jsonls.setup({
-                settings = {
-                    json = {
-                        schemas = require("schemastore").json.schemas(), -- スキーマサポートを追加
-                        validate = { enable = true }, -- 検証を有効化
-                    },
+                capabilities = capabilities,
+            })
+        end,
+    },
+
+    -- nvim-cmp (補完エンジン)
+    {
+        "hrsh7th/nvim-cmp",
+        dependencies = {
+            "hrsh7th/cmp-nvim-lsp", -- LSP補完
+            "hrsh7th/cmp-buffer", -- バッファ補完
+            "hrsh7th/cmp-path", -- パス補完
+            "hrsh7th/cmp-vsnip", -- スニペット補完
+            "hrsh7th/vim-vsnip", -- スニペットエンジン
+        },
+        config = function()
+            local cmp = require("cmp")
+            cmp.setup({
+                snippet = {
+                    expand = function(args)
+                        vim.fn["vsnip#anonymous"](args.body)
+                    end,
                 },
+                mapping = cmp.mapping.preset.insert({
+                    ["<C-y>"] = cmp.mapping.confirm({ select = true }), -- Enterで補完確定
+                    ["<C-Space>"] = cmp.mapping.complete(), -- 手動補完
+                }),
+                sources = cmp.config.sources({
+                    { name = "nvim_lsp" },
+                    { name = "buffer" },
+                    { name = "path" },
+                }),
             })
         end,
     },

などして

これでinstallする。ただjsonの補完つのはあんまなさそうなので、さっさとintelephenseを入れる。なぜ最初にjsonから初めたかというと、他のlspを追記するデモを作りたかった為なのでご了承願いたい。

--- a/init.lua
+++ b/init.lua
@@ -28,13 +28,19 @@ require("lazy").setup({
         dependencies = { "neovim/nvim-lspconfig" },
         config = function()
             require("mason-lspconfig").setup({
-                ensure_installed = { "jsonls" }, -- JSON LSPをインストール
+               ensure_installed = { "intelephense", "jsonls" },
             })

             -- LSPの設定
             local lspconfig = require("lspconfig")
             local capabilities = require("cmp_nvim_lsp").default_capabilities()

+            -- PHP LSPの設定
+            lspconfig.intelephense.setup({
+                capabilities = capabilities,
+                root_dir = require("lspconfig").util.root_pattern("composer.json", ".git", "."),
+            })
+
             -- JSON LSPの設定
             lspconfig.jsonls.setup({
                 capabilities = capabilities,

使ってみる

で、これは

root_dir = require("lspconfig").util.root_pattern("composer.json", ".git", "."),

ここにあるように

  • composer.json
  • .git

がrootに存在しないと発動しないので、適当に

$ mkdir test
$ cd test/
$ git init

などすると .git ができるので

$ nvim moge.php

などで起動してみる、すると

intelephenseのinstallが初まり、コンプリートした後

<?php

$db = new PDO();

このようなコードを書いてみると

こうなる

無視してつっこんでいって

<?php

$db = new PDO();
$db-> #(Ctrl + nとか押す)

とかすると

こうなるわけだ

もうちょい改良する

--- a/init.lua
+++ b/init.lua
@@ -35,15 +35,28 @@ require("lazy").setup({
             local lspconfig = require("lspconfig")
             local capabilities = require("cmp_nvim_lsp").default_capabilities()

+            -- 共通の on_attach 関数 (シグネチャヘルプ自動表示を含む)
+            local on_attach = function(client, bufnr)
+                -- キーマッピングでシグネチャヘルプを呼び出す
+                vim.api.nvim_buf_set_keymap(bufnr, "i", "<C-k>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", { noremap = true, silent = true })
+
+                -- 自動でシグネチャヘルプを表示
+                vim.cmd([[
+                    autocmd CursorHoldI * lua vim.lsp.buf.signature_help()
+                ]])
+            end
+
             -- PHP LSPの設定
             lspconfig.intelephense.setup({
                 capabilities = capabilities,
+                on_attach = on_attach,
                 root_dir = require("lspconfig").util.root_pattern("composer.json", ".git", "."),
             })

             -- JSON LSPの設定
             lspconfig.jsonls.setup({
                 capabilities = capabilities,
+                on_attach = on_attach,
             })
         end,
     },
@@ -69,6 +82,13 @@ require("lazy").setup({
                 mapping = cmp.mapping.preset.insert({
                     ["<C-y>"] = cmp.mapping.confirm({ select = true }), -- Enterで補完確定
                     ["<C-Space>"] = cmp.mapping.complete(), -- 手動補完
+                    ["<C-k>"] = cmp.mapping(function(fallback)
+                        if vim.fn.pumvisible() == 1 then
+                            vim.fn.feedkeys(vim.api.nvim_replace_termcodes("<C-n>", true, true, true), "n")
+                        else
+                            vim.lsp.buf.signature_help()
+                        end
+                    end, { "i", "s" }),
                 }),
                 sources = cmp.config.sources({
                     { name = "nvim_lsp" },

などすると

Ctrl-kでこれが出てくるようになる。キーマップは議論の余地を大いに残すが

またオブジェクトの補完が

このように自動になった。

たとえば

まあなんかゴリゴリにエクストリームプログラミングしてもわりと何かやっとるって感じになる。多分

laravelプロジェクトを開くと

頻出する問題ではあるけど、このようにlaravelプロジェクトを開くとエラーまみれになる。まあこれはある意味しゃーないところがあり、パスを解決できてないのでこうなっている。

composer require --dev barryvdh/laravel-ide-helper
php artisan ide-helper:generate
php artisan ide-helper:models

して

--- a/init.lua
+++ b/init.lua
@@ -51,6 +51,21 @@ require("lazy").setup({
                 capabilities = capabilities,
                 on_attach = on_attach,
                 root_dir = require("lspconfig").util.root_pattern("composer.json", ".git", "."),
+                settings = {
+                    intelephense = {
+                        -- Laravelの補完強化 (ide-helperファイルを含める)
+                        files = {
+                            maxSize = 5000000, -- 読み込むファイルの最大サイズを変更
+                            associations = {
+                                "*.php", -- PHPファイル全般
+                            },
+                            includePaths = {
+                                "./_ide_helper.php", -- `ide-helper` ファイルを含める
+                                "./_ide_helper_models.php", -- モデル補完ファイルを含める
+                            },
+                        },
+                    },
+                },
             })

             -- JSON LSPの設定

このように変更する。すると


メソッドの説明がウインドウで提示されているのがわかるだろうか

とか

とか、また

何も使ってないとこんなんになったりとか

そのうち

xdebugを使った記事でも再編するか...

ほしい人がいるとは思えないが

この記事のために書き起したlua

~/.config/nvim/init.lua
-- Lazy.nvimの初期化
vim.g.mapleader = " " -- Leaderキーをスペースに設定
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
    vim.fn.system({
        "git",
        "clone",
        "--filter=blob:none",
        "https://github.com/folke/lazy.nvim.git",
        "--branch=stable",
        lazypath,
    })
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup({
    -- Mason (LSPやツールのインストーラ)
    {
        "williamboman/mason.nvim",
        config = function()
            require("mason").setup()
        end,
    },

    -- MasonとLSPconfigの連携
    {
        "williamboman/mason-lspconfig.nvim",
        dependencies = { "neovim/nvim-lspconfig" },
        config = function()
            require("mason-lspconfig").setup({
                ensure_installed = { "intelephense", "jsonls" },
            })

            -- LSPの設定
            local lspconfig = require("lspconfig")
            local capabilities = require("cmp_nvim_lsp").default_capabilities()

            -- 共通の on_attach 関数 (シグネチャヘルプ自動表示を含む)
            local on_attach = function(client, bufnr)
                -- キーマッピングでシグネチャヘルプを呼び出す
                vim.api.nvim_buf_set_keymap(bufnr, "i", "<C-k>", "<cmd>lua vim.lsp.buf.signature_help()<CR>", { noremap = true, silent = true })

                -- 自動でシグネチャヘルプを表示
                vim.cmd([[
                    autocmd CursorHoldI * lua vim.lsp.buf.signature_help()
                ]])
            end

            -- PHP LSPの設定
            lspconfig.intelephense.setup({
                capabilities = capabilities,
                on_attach = on_attach,
                root_dir = require("lspconfig").util.root_pattern("composer.json", ".git", "."),
                settings = {
                    intelephense = {
                        -- Laravelの補完強化 (ide-helperファイルを含める)
                        files = {
                            maxSize = 5000000, -- 読み込むファイルの最大サイズを変更
                            associations = {
                                "*.php", -- PHPファイル全般
                            },
                            includePaths = {
                                "./_ide_helper.php", -- `ide-helper` ファイルを含める
                                "./_ide_helper_models.php", -- モデル補完ファイルを含める
                            },
                        },
                    },
                },
            })

            -- JSON LSPの設定
            lspconfig.jsonls.setup({
                capabilities = capabilities,
                on_attach = on_attach,
            })
        end,
    },

    -- nvim-cmp (補完エンジン)
    {
        "hrsh7th/nvim-cmp",
        dependencies = {
            "hrsh7th/cmp-nvim-lsp", -- LSP補完
            "hrsh7th/cmp-buffer", -- バッファ補完
            "hrsh7th/cmp-path", -- パス補完
            "hrsh7th/cmp-vsnip", -- スニペット補完
            "hrsh7th/vim-vsnip", -- スニペットエンジン
        },
        config = function()
            local cmp = require("cmp")
            cmp.setup({
                snippet = {
                    expand = function(args)
                        vim.fn["vsnip#anonymous"](args.body)
                    end,
                },
                mapping = cmp.mapping.preset.insert({
                    ["<C-y>"] = cmp.mapping.confirm({ select = true }), -- Enterで補完確定
                    ["<C-Space>"] = cmp.mapping.complete(), -- 手動補完
                    ["<C-k>"] = cmp.mapping(function(fallback)
                        if vim.fn.pumvisible() == 1 then
                            vim.fn.feedkeys(vim.api.nvim_replace_termcodes("<C-n>", true, true, true), "n")
                        else
                            vim.lsp.buf.signature_help()
                        end
                    end, { "i", "s" }),
                }),
                sources = cmp.config.sources({
                    { name = "nvim_lsp" },
                    { name = "buffer" },
                    { name = "path" },
                }),
            })
        end,
    },

    -- JSONスキーマのサポート (オプション)
    {
        "b0o/schemastore.nvim",
    },
})

Discussion