[第13回] Neovimのすゝめ – LSPをセットアップ(Builtin LSP編)

[第13回] Neovimのすゝめ – LSPをセットアップ(Builtin LSP編)

連載

本記事は複数記事の連載記事の1つです。

Builtin LSP

NeoVimは標準でLSPクライアントが実装されており、Builtin LSPやnvim lspと呼ばれています。
Lua言語によってパフォーマンスが向上したり、フローティングウィンドウなどNeoVimにしかない機能が利用されています。

メリットでもデメリットでもありますが、Builtin LSPはcocのようなオールインワンではありません。

LSPに関わるランゲージサーバーのインストール、入力補完プラグインなど別途自分で用意する必要がありますが、その代わりに自分好みにカスタマイズが可能です。

ただ、細かくカスタマイズ出来る反面で設定の手間は間違いなく増えます。

Builtin LSPをインストールする

NeoVim標準で含まれているため、インスールの必要はありません。
バージョン0.5でBuiltin LSPは追加されたので、0.5以上であることを確認しておきましょう。

nvim -v

neovim/nvim-lspconfigをインストールする

ランゲージサーバーの設定を定義するNeoVim公式のプラグインです。
ランゲージサーバーごとに動作設定を変更できる設定値が用意されており、デフォルト値から変更する設定をセットすることが出来ます。

公式のキーバインド・補完設定例
https://github.com/neovim/nvim-lspconfig#keybindings-and-completion

williamboman/mason.nvimをインストールする

williamboman/mason.nvimは、各言語のLSPサーバー, Linter, FormatterをウィンドウUIからインストール・管理可能なプラグインです。

※nvim-lsp-installerは作者から、より改善したwilliamboman/nvim-lsp-installerの使用を推奨されています。

williamboman/mason-lspconfig.nvimをインストールする

williamboman/mason-lspconfig.nvimは、masonとLSP configを使いやすくするためのプラグインです。

LspInstallコマンドや定義済みのLSPリストを自動的にインストール可できます。

nvim-cmpで入力補完する

LSPでランゲージサーバーを有効にしただけではコードの情報を持つだけで入力補完として表示することは出来ません。
nvim-cmpはソースを追加していくことで必要な補完対象を追加していきます。

セットアップ例

LSP configに追加

capabilitiesにnvim-cmpを追加してLSPの入力補完を有効にします。
入力補完は全てのランゲージサーバーで動作すれば良いので、nvim-lsp-installerで全ランゲージサーバーに対してセットします。

lua << EOF

local lsp_installer = require("nvim-lsp-installer")

lsp_installer.on_server_ready(function(server)
    local opts = {}

    opts.capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())

    -- This setup() function is exactly the same as lspconfig's setup function (:help lspconfig-quickstart)
    server:setup(opts)
    vim.cmd [[ do User LspAttachBuffers ]]
end)

EOF

ソースを追加する

ソースのプラグインをそれぞれパッケージマネージャーからインストールし、nvim-cmpの設定に追加して有効化します。
以下は基本的なソースです。

ソース 機能
hrsh7th/cmp-nvim-lsp LSP(ランゲージサーバー)の補完
hrsh7th/cmp-buffer 展開中のバッファから補完
hrsh7th/cmp-path ファイル・ディレクトリパスの補完
hrsh7th/cmp-cmdline コマンドライン時に補完
onsails/lspkind-nvim 補完時にアイコン表示

その他、様々なソースがあります。
https://github.com/topics/nvim-cmp

SKK用の補完プラグインなど日本人向けのプラグインも。
rinx/cmp-skkeleton

その他、スニペットプラグインなど広く連携していけるので必要に応じて追加していきましょう。

Telescopeと連携する

ファジーファインダーのTelescopeはLSPも対応しています。
シンボルの検索など、フィルタリングがあると便利なLSPの機能はTelescopeで解決するのがオススメです。

Neovim LSP Pickers

設定例

Vim Plug プラグインインストール例

" lsp
Plug 'neovim/nvim-lspconfig'
Plug 'williamboman/nvim-lsp-installer'
Plug 'ray-x/lsp_signature.nvim'

" auto complete
Plug 'hrsh7th/nvim-cmp'
Plug 'hrsh7th/cmp-nvim-lsp'
Plug 'hrsh7th/cmp-buffer'
Plug 'hrsh7th/cmp-path'
Plug 'hrsh7th/cmp-cmdline'
Plug 'onsails/lspkind-nvim'
Plug 'ray-x/cmp-treesitter'

LSP Config

lua << EOF

local nvim_lsp = require('lspconfig')
-- local servers = {
--   'gopls'
-- }
-- for _, lsp in ipairs(servers) do
--   nvim_lsp[lsp].setup {
--     capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities()),
--     flags = {
--       debounce_text_changes = 150,
--     }
--   }
-- end

-- require'lspconfig'.volar.setup{}

EOF

" nnoremap <silent>gD <cmd>lua vim.lsp.buf.declaration()<CR>
" nnoremap <silent>gd <cmd>lua vim.lsp.buf.definition()<CR>
nnoremap <silent>gh <cmd>lua vim.lsp.buf.hover()<CR>
nnoremap <silent>gH <cmd>lua vim.lsp.buf.signature_help()<CR>
" nnoremap <silent>gi <cmd>lua vim.lsp.buf.implementation()<CR>
" nnoremap <silent><space>wa <cmd>lua vim.lsp.buf.add_workspace_folder()<CR>
" nnoremap <silent><space>wr <cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>
" nnoremap <silent><space>wl <cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>
" nnoremap <silent><space>D <cmd>lua vim.lsp.buf.type_definition()<CR>
" nnoremap <silent><space>rn <cmd>lua vim.lsp.buf.rename()<CR>
" nnoremap <silent><space>ca <cmd>lua vim.lsp.buf.code_action()<CR>
" nnoremap <silent>gf <cmd>lua vim.lsp.buf.references()<CR>
" nnoremap <silent><space>e <cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>
nnoremap <silent>[d <cmd>lua vim.lsp.diagnostic.goto_prev()<CR>
nnoremap <silent>]d <cmd>lua vim.lsp.diagnostic.goto_next()<CR>
" nnoremap <silent><space>q <cmd>lua vim.lsp.diagnostic.set_loclist()<CR>
" nnoremap <silent><space>f <cmd>lua vim.lsp.buf.formatting()<CR>

LSP Installer

lua << EOF

local server_configs = {
  ["gopls"] = {
  }
}

local lsp_installer = require("nvim-lsp-installer")

lsp_installer.on_server_ready(function(server)
    local opts = {}

    -- (optional) Customize the options passed to the server
    -- if server.name == "tsserver" then
    --     opts.root_dir = function() ... end
    -- end

    if server_configs[server.name] then
      opts = vim.tbl_deep_extend('force', opts, server_configs[server.name])
    end

    opts.capabilities = require('cmp_nvim_lsp').update_capabilities(vim.lsp.protocol.make_client_capabilities())

    -- This setup() function is exactly the same as lspconfig's setup function (:help lspconfig-quickstart)
    server:setup(opts)
    vim.cmd [[ do User LspAttachBuffers ]]
end)

EOF

nvim cmp

lua << EOF
  -- Setup nvim-cmp.
  local cmp = require'cmp'
  local lspkind = require('lspkind')

  cmp.setup({
    preselect = cmp.PreselectMode.None,
    snippet = {
      expand = function(args)
        -- For `vsnip` user.
        -- vim.fn["vsnip#anonymous"](args.body) -- For `vsnip` user.

        -- For `luasnip` user.
        -- require('luasnip').lsp_expand(args.body)

        -- For `ultisnips` user.
        -- vim.fn["UltiSnips#Anon"](args.body)
      end,
    },
    mapping = {
      -- ['<C-i>'] = cmp.mapping.complete(),
      ['<C-e>'] = cmp.mapping.close(),
      ['<C-i>'] = function(fallback)
        if cmp.visible() then
          cmp.select_next_item()
        else
          fallback()
        end
      end
    },
    sources = {
      { name = 'nvim_lsp' },

      -- For vsnip user.
      -- { name = 'vsnip' },

      -- For luasnip user.
      -- { name = 'luasnip' },

      -- For ultisnips user.
      -- { name = 'ultisnips' },

      { name = 'buffer' },
      { name = 'path' },
      { name = 'treesitter' },
    },
    formatting = {
      format = lspkind.cmp_format({
        with_text = false, -- do not show text alongside icons
        maxwidth = 50, -- prevent the popup from showing more than provided characters (e.g 50 will not show more than 50 characters)

        -- The function below will be called before any actual modifications from lspkind
        -- so that you can provide more controls on popup customization. (See [#30](https://github.com/onsails/lspkind-nvim/pull/30))
        before = function (entry, vim_item)
          return vim_item
        end
      })
    }
  })

  -- Use buffer source for `/` (if you enabled `native_menu`, this won't work anymore).
  cmp.setup.cmdline('/', {
    sources = {
      { name = 'buffer' }
    }
  })

  -- Use cmdline & path source for ':' (if you enabled `native_menu`, this won't work anymore).
  cmp.setup.cmdline(':', {
    sources = cmp.config.sources({
      { name = 'path' }
    }, {
      { name = 'cmdline' }
    })
  })

EOF

Telescope

" LSP Picker
nnoremap gd <cmd>lua require'telescope.builtin'.lsp_definitions()<cr>
nnoremap gf <cmd>lua require'telescope.builtin'.lsp_references()<cr>
nnoremap <leader>o <cmd>lua require'telescope.builtin'.lsp_document_symbols()<cr>
nnoremap <leader>O <cmd>lua require'telescope.builtin'.lsp_workspace_symbols()<cr>
nnoremap <leader>a <cmd>lua require'telescope.builtin'.lsp_code_actions()<cr>

 

Neovimのすゝめカテゴリの最新記事