🖤

ddc.vim導入時に詰まったところメモ(LSP)

2024/09/01に公開

はじめに

nvim-lspに何度も手を出そうとしてはどこかで詰まってcoc.nvimに帰ってきているのですが、闇の力に興味が出てまたnvim-lspに挑戦しているairRnotです。今回、ddc.vimの導入時にLSPとの連携部分で色々と詰まったのでそのポイントについて書いていこうと思います。

環境

neovimはv0.10.0で、プラグインマネージャはlazy.nvimです。LSPの設定は以下の通りです。

長いので省略
return {
    {
        "williamboman/mason.nvim",
        build = ":MasonUpdate",
        event = { "BufReadPre", "BufNewFile" },
        opts = {},
    },
    {
        "williamboman/mason-lspconfig.nvim",
        dependencies = {
            "neovim/nvim-lspconfig",
            "creativenull/efmls-configs-nvim",
            "Shougo/ddc-source-lsp",
            "uga-rosa/ddc-source-lsp-setup",
        },
        event = { "BufReadPre", "BufNewFile" },
        config = function()
            local lspconfig = require "lspconfig"
            require("ddc_source_lsp_setup").setup()
            local capabilities = require("ddc_source_lsp").make_client_capabilities()
            require("mason-lspconfig").setup_handlers {
                function(server_name)
                    lspconfig[server_name].setup {
                        capabilities = capabilities,
                    }
                end,
                ["lua_ls"] = function()
                    lspconfig.lua_ls.setup {
                        capabilities = capabilities,
                        settings = {
                            Lua = {
                                diagnostics = {
                                    globals = { "vim" },
                                },
                                workspace = {
                                    checkThirdParty = false,
                                },
                                format = {
                                    enable = false,
                                },
                            },
                        },
                    }
                end,
                ["efm"] = function()
                    local stylua = require "efmls-configs.formatters.stylua"
                    local languages = {
                        lua = { stylua },
                    }
                    lspconfig.efm.setup {
                        capabilities = capabilities,
                        filetypes = vim.tbl_keys(languages),
                        init_options = {
                            documentFormatting = true,
                            documentRangeFormatting = true,
                        },
                        settings = {
                            rootMarkers = { ".git/" },
                            languages = languages,
                        },
                    }
                end,
            }
            local augroup = vim.api.nvim_create_augroup("LspFormatting", {})
            vim.api.nvim_create_autocmd("LspAttach", {
                group = augroup,
                callback = function(ev)
                    vim.keymap.set("n", "<C-k>", "<cmd>lua vim.lsp.buf.hover()<CR>")
                    vim.keymap.set("n", "gf", "<cmd>lua vim.lsp.buf.format({ async = false })<CR>")
                    vim.keymap.set("n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>")
                    vim.keymap.set("n", "gd", "<cmd>lua vim.lsp.buf.definition()<CR>")
                    vim.keymap.set("n", "gD", "<cmd>lua vim.lsp.buf.declaration()<CR>")
                    vim.keymap.set("n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>")
                    vim.keymap.set("n", "gt", "<cmd>lua vim.lsp.buf.type_definition()<CR>")
                    vim.keymap.set("n", "gn", "<cmd>lua vim.lsp.buf.rename()<CR>")
                    vim.keymap.set("n", "<Leader>.", "<cmd>lua vim.lsp.buf.code_action()<CR>")
                    vim.keymap.set("n", "ge", "<cmd>lua vim.diagnostic.open_float()<CR>")
                    vim.keymap.set("n", "g]", "<cmd>lua vim.diagnostic.goto_next()<CR>")
                    vim.keymap.set("n", "g[", "<cmd>lua vim.diagnostic.goto_prev()<CR>")

                    local client = vim.lsp.get_client_by_id(ev.data.client_id)
                    if client == nil then
                        return
                    end

                    if client.supports_method "textDocument/formatting" then
                        vim.api.nvim_clear_autocmds { group = augroup, buffer = ev.bufnr }
                        vim.api.nvim_create_autocmd({ "BufWritePre" }, {
                            group = augroup,
                            buffer = ev.bufnr,
                            callback = function()
                                vim.lsp.buf.format { async = false }
                            end,
                        })
                    end
                end,
            })

            vim.lsp.handlers["textDocument/publishDiagnostics"] =
                vim.lsp.with(vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = false })
        end,
    },
}

とりあえずluaのlspと、efmを通してstyluaだけ使えるようにしています。

詰まりポイント

ddc-source-lspは一度名前が変わっている

ddcとLSPを連携させるときはddc-source-lspを使うと思いますが、このpluginは2023年の秋頃まではddc-source-nvim-lspという名前でした。

どうやら、名前が変わった理由は

Because it is not only for nvim-lsp.

だそうです。

ddc-source-lspに関する記事を読むと、

vim.fn["ddc#custom#patch_global"]("sources", { "nvim-lsp" })

vim.fn["ddc#custom#patch_global"]("sourceOptions", {
    ["nvim-lsp"] = {
        mark = "lsp",
        forceCompletionPattern = "\\.\\w*|:\\w*|->\\w*",
    },
})

というように、"nvim-lsp"と指定されていることがあるのはこの影響ですね。現在は単に"lsp"と指定すれば大丈夫です。最初はどちらが合っているのかわからず少し戸惑いました。

ちなみに、markに"[LSP]"と指定している方もいるのですが、これは単純に

右側に出る文字列を指定しているだけなので気にする必要はないです。

<CR>は補完の確定ではない

<CR>はデフォルトでは補完の確定ではありません。<CR>を押してもただ改行されるだけです。coc.nvimは気にしなくても<CR>で補完してくれたので気がつくのに結構てごずりました。

おそらくデフォルトだと<C-y>が確定です。(uiがpumの場合は自分でkeymapを書かないとだめです)

E1206: Dictionary required for argument 1というエラーが出る

ここが今回一番ハマった部分です。

補完を確定した際に、以下のようなエラーが発生しました。

Error detected while processing function <SNR>73_on_complete_done_after[12]..<SNR>73_get_expand_text:
line   15:
E1206: Dictionary required for argument 1

結論としては、hrsh7th/vim-vsnip-integを依存に入れていたのが原因でした。

vnsip使うからなんとなくintegの方も入れていたのですが、これが間違いでした。ただLSPと連携させるだけならhrsh7th/vim-vsnipだけで十分です。

補完したものがすぐに消滅する

snippet系において、補完を確定したあとすぐに消滅してしまうという挙動が起きていました。

原因は、vim scriptをluaに書き直した際のミスにありました。

ddc-source-lspのREADMEには以下のように書かれています。

call ddc#custom#patch_global('sourceParams', #{
      \   lsp: #{
      \     snippetEngine: denops#callback#register({
      \           body -> vsnip#anonymous(body)
      \     }),
      \     enableResolveItem: v:true,
      \     enableAdditionalTextEdit: v:true,
      \   }
      \ })

body -> vsnip#anonymous(body)が波括弧で囲まれていたので、luaに書き直した際にも波括弧で囲んでいました。

vim.fn["ddc#custom#patch_global"]("sourceParams", {
    lsp = {
        snippetEngine = vim.fn["denops#callback#register"] {
            function(body)
                vim.fn["vsnip#anonymous"](body)
            end,
        },
        enableResolveItem = true,
        enableAdditionalTextEdit = true,
    },
})

しかし、luaの場合は波括弧は必要ありませんでした。

vim.fn["ddc#custom#patch_global"]("sourceParams", {
    lsp = {
        snippetEngine = vim.fn["denops#callback#register"](function(body)
            vim.fn["vsnip#anonymous"](body)
        end),
        enableResolveItem = true,
        enableAdditionalTextEdit = true,
    },
})

これで、無事に補完ができるようになりました。

おわりに

詰まったときは、GitHubで他の方の設定を漁るのが結構効果的でした。
また、闇の力は識者の方が多くいらっしゃり、本当に助かりました。
しかし、まだLSPと最低限の連携をしただけなので、これからもっと拡張していきたいです。

GitHubで編集を提案

Discussion