Neovimはじめてみた
はじめに
9月の末ごろからWezTermを触り始めて、LazyGitなどを入れてみると、ターミナルで全て完結できるのがいいなと思い始めました。そこで、いっそのことneovimを使ってコーディングもターミナル上で完結しようと思いました。
環境
実行環境は以下の通りです。
- macOS Sequoia 15.0
- WezTerm (zsh)
- neovim: v0.10.1
- プラグインマネージャー: lazy.nvim
現在のコーディング環境
現在のコーディング環境は以下のようになっています。
項目 | 現在 |
---|---|
Terminal | WezTerm |
IDE | JetBrains系 + VSCode |
Gitクライアント | Fork |
Databaseクライアント | DataGrip |
TerminalにはWezTermを使っています。使い始めたのは最近ですが設定をテキストファイルで管理できる点がとても良いです。以前はiTerm2を使っており、ストレスはなかったのですが今ではWezTermの方が良いと思っています。
コーディングで主に使っているのはJetBrainsのIDE( PyCharm, WebStorm, PhpStorm )です。JetBrainsのIDEは、デフォルトで各種言語の補完が効き、プラグインを追加しなくてもストレスなくコーディングできるのが良い点です。またどちらかというと、JetBrainsにプラグインの追加はしたくないです。プラグインの追加はGUIベースになるため、管理が煩雑になるのと異なる環境間での共有が難しいためです。もちろん、設定のexport機能を使えばいいのですが、設定を変更するたびにexportしてimportしてというのは煩雑なためです。
VSCodeは、csvやjsonなどのテキストファイルを開いて確認・編集するときに使っています。VSCodeに関しても、JetBrainsのIDEと同様の理由でプラグインの追加はしたくないです。プラグインの追加をしていないためコーディングには使えず、利用頻度は低めです。Curosr
などの生成AIを組み込んだVSCodeベースのエディタも出てきていますがほとんど使っていないです。
コーディング以外のアプリケーションとして、GitクライアントにはForkを利用しています。名前がGitの機能のforkと被っているので検索するのが大変ですが、シンプルな見た目とgit-flowの機能がデフォルトで利用できて便利なため利用しています。DatabaseクライアントにはJetBrainsのDataGripを利用しています。PyCharmやWebStormからでもほぼ同様のDatabaseクライアントが利用できるのですが、Database接続専用のクライアントがあるとシンプルになるのでDataGripを利用しています。
これからのコーディング環境
これからのコーディング環境を以下に示します。
項目 | 現在 | これから |
---|---|---|
Terminal | WezTerm | 同じ |
IDE | JetBrains系 + VSCode | neovim (+ plugins) |
Gitクライアント | Fork | lazygit |
Databaseクライアント | DataGrip | 同じ |
vimに対しては操作が難しいそうと言う心理的なハードルがあったため、今まで使ったことがありませんでした。しかし、~/.config
以下で設定およびプラグインを管理できるのが私にとって魅力的に感じたため、操作が難しいそうと言う心理的なハードルは気にならなくなりました。加えてターミナルだけで完結し、ホームポジションからほとんど手を動かす必要がないため、マウス操作が必要になるJetBrainsのIDEより早く操作ができるようになるかなと思っています。
Forkは、ターミナル上でTUIで操作できるlazygitを代替にする予定です。一方DataGripを代替する予定はないです。TUIベースのデータベースクライアントのツールはあるのですが、利用を見送っています。
プラグインなどの詳細は後述しますが、現在とこれからのアプリケーションで利用する機能の対応付けは以下の通りです。
機能 | 現在 | これから |
---|---|---|
コマンドの実行 | WezTerm | WezTerm |
補完・エラー機能 | IDE | neovim (+ LSP) |
関数の定義ジャンプ | IDE | neovim (+ LSP) |
誤字検出 | IDE | neovim (+ LSP) |
リポジトリ内全体での文字列検索 | IDE | neovim (+ telescope) |
行ごとのGit Blame | IDE | neovim (+ git-blame.nvim) |
コンフリクトの解消 | IDE | neovim (+ diffview.nvim) |
GitFlowに従ったブランチ管理 | Fork | lazygit |
行ごとのコミット、プッシュ機能 | Fork | lazygit |
スタッシュ | Fork | lazygit |
データベース操作 | DataGrip | DataGrip |
NeoVimについて
NeoVimとは
公式サイトによると
hyperextensible Vim-based text editor
拡張可能なVimベースのテキストエディタのことです。
開発しているGitHubによると
Neovim is a project that seeks to aggressively refactor Vim in order to:
- Simplify maintenance and encourage contributions
- Split the work between multiple developers
- Enable advanced UIs without modifications to the core
- Maximize extensibility
メンテナンスを簡素化し、開発しやすくし、高度なUIを提供し、拡張しやすくするためにvimをリファクタすることを目指すプロジェクトのことです。
インストール
neovim
はbrewでインストールできます。
$ brew install neovim
インストール後、$ nvim
でneovimを開くことができます。
キーマップの表記について
neovimでは<C-@>
や<S-@>
などの表記を見かけます。これらはキーマッピングのことで<C-@>
の時はCtrl
と別のキー(@
)を、<S-@>
の時はShift
と別のキー(@
)を押下することを意味しています。詳しくは以下の記事をご覧ください。
また、そのほかにも<Leader>
という表記も見かけます。これはユーザーが自由に割り当てれる複数入力の開始キーのことです。既存の<C-@>
などと被らないため自由度が高いです。私は<Leader>
にスペースキーに割り当てています。
基本操作
Vimには以下のモードが存在します。
- Normal mode
- Insert mode
- Command mode
- Visual mode
それぞれどのようなモードで、どのように遷移し、そのモードで打てるキーマップを説明します。
Normal mode
Normal modeはneovimを開いた直後のモードです。キーを押下することでカーソル移動やコピーなどができます。
Normal mode: 移動
キーマップ | 操作 |
---|---|
h |
左移動 |
j |
下移動 |
k |
上移動 |
l |
右移動 |
e |
英単語のスペース区切り前などに移動 |
$ |
末尾に移動 |
0 |
先頭に移動 |
{ |
段落ごとに上に移動 |
} |
段落ごとに下に移動 |
[[ |
セクションごとに上に移動 |
]] |
セクションごとに下に移動 |
<C-b> |
ページを上に移動(1ページずつ) |
<C-f> |
ページを下に移動(1ページずつ) |
<C-u> |
ページを上に移動(半ページずつ) |
<C-d> |
ページを下に移動(半ページずつ) |
<C-y> |
ページを上に送る(1行ずつ) |
<C-e> |
ページを下に送る(1行ずつ) |
gg |
ファイルの先頭に移動 |
G |
ファイルの末尾に移動 |
Normal mode: 移動して入力モードへ移行
キーマップ | 操作 |
---|---|
a |
カーソルの右側から入力を開始 |
A |
行の末尾に移動して入力を開始 |
i |
カーソルの左側から入力を開始 |
I |
行の先頭に移動して入力を開始 |
o |
改行して入力を開始 |
O |
下に改行をして上に入力を開始 |
C |
カーソルのある位置から末尾までを削除して入力を開始 |
c0 |
カーソルのある位置から先頭まで削除をして入力を開始 |
cc |
カーソルのある行を削除して入力を開始 |
Normal mode: コピー
ここでは、Normal modeのみで完結するコピーのコマンドのみを載せていますが、実際は後述するVisual modeでコピー範囲を選択してなどの方法をよく使います。
キーマップ | 操作 |
---|---|
yy |
無名レジスタに行コピー |
dd |
行を切り取り |
p |
無名レジスタの内容を貼り付け |
x |
カーソル上の文字を削除(Deleteキーと同じ) |
Normal mode: 検索
キーマップ | 操作 |
---|---|
/ 検索したい文字列 |
文字の検索 |
n |
次の検索結果に移動 |
N |
前の検索結果に移動 |
Normal mode: その他
キーマップ | 操作 |
---|---|
u |
直前の入力を削除(Ctrl + Zと同じ挙動) |
Insert mode
Insert modeは文字を入力するモードです。普段のテキストエディタと同じ感覚で使うことができます。<esc>
を入力することでNormal modeに戻ることができます。ただ、頻繁に<esc>
を入力するのは大変なので別のキーにマッピングするのが便利です。次の記事でもあるようにjj
やkk
に割り当てると、カーソル移動時にモードが切り替わるので操作しやすくなります。
上記の設定をする場合、以下のように追記します。
vim.keymap.set("i", "jj", "<esc>")
vim.keymap.set("i", "kk", "<esc>")
Command mode
Command modeは様々なコマンドを実行するモードです。Normal modeから:
を入力して切り替えることができます。<esc>
で再びNormal modeに戻ることができます。
Command modeでよく利用するコマンドは以下の通りです。
キーマップ | 操作 |
---|---|
:u | 直前の操作を取り消す |
:q | 終了する |
:q! | 保存しないで終了する |
:w | 保存する |
Visual mode
Visual modeはテキストの領域選択が出来るモードです。Normal modeから以下のいずれかの入力でVisual modeに切り替えることができます。
キーマップ | 操作 |
---|---|
v |
vを押したところから選択ができる |
V |
Vを押した行全体を選択できる |
<C-v> |
矩形選択が出来る |
上記のコマンドで範囲選択をした後に以下のコマンドを実行することで、操作ができます。
キーマップ | 操作 |
---|---|
d | 選択範囲の切り取り |
y | 選択範囲のコピー |
<S->> |
インデントを下げる |
<S-<> |
インデントを上げる |
またクリップボードを利用したコピーをするために以下のキーマップを設定しています。
キーマップ | 操作 |
---|---|
<leader>y |
行をクリップボードにコピー |
<leader>d |
行をクリップボードにコピーし、選択範囲を切り取る |
<leader>p |
クリップボードの内容を貼り付け |
状態遷移
それぞれのモードを状態遷移図で表すと以下のようになります。a,i,o
などでInsert modeに、v,V
などでVisual modeに、:
でCommand modeになります。そして<esc>
でNormal modeになります。
そのほか
Normal modeでのカーソルが太く、どこを基準としているかわかりにくい場合があります。
カーソルが四角になっていて、どこを基準としているのわかりづらい
カーソルを変更してInsert modeと同じにするとわかりやすい
上の画像のような変更をするために、以下の内容を追記します。
-- cursor
vim.opt.guicursor = "n-i:ver25"
プラグイン
vimでコーディングする際にはプラグインの追加が必須になるかと思います。そして複数のプラグインを導入・管理するために、まずはプラグインマネージャーを導入します。色々ありますが、lazynvim
を導入します。
公式の設定にあるように以下の設定を追記していきます。
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
local lazyrepo = "https://github.com/folke/lazy.nvim.git"
local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath })
if vim.v.shell_error ~= 0 then
vim.api.nvim_echo({
{ "Failed to clone lazy.nvim:\n", "ErrorMsg" },
{ out, "WarningMsg" },
{ "\nPress any key to exit..." },
}, true, {})
vim.fn.getchar()
os.exit(1)
end
end
vim.opt.rtp:prepend(lazypath)
-- Make sure to setup `mapleader` and `maplocalleader` before
-- loading lazy.nvim so that mappings are correct.
-- This is also a good place to setup other settings (vim.opt)
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"
-- Setup lazy.nvim
require("lazy").setup({
spec = {
-- add your plugins here
},
-- Configure any other settings here. See the documentation for more details.
-- colorscheme that will be used when installing plugins.
install = { colorscheme = { "habamax" } },
-- automatically check for plugin updates
checker = { enabled = true },
})
このファイルの、spec
にプラグインを追記していくことで複数のプラグインを管理・インストールできます。ただ、このファイルに追加し続けるとinit.lua
が非常に大きくなり管理しづらくなります。 そのため lazy.nvim
ではプラグインを管理するファイルを分割することができます。
spec = {
+ { { import = "plugins" } }
},
こうすることで、~/.config/nvim/lua/plugins/*.lua
で作成したプラグインを読み込めます。ファイル作成後にneovimを開くと、lazy.nvimによってプラグインのインストールが始まります。
nvim起動時のlazy.nvimの画面
上記の画面は:Lazy
コマンドで呼び出せます。lazy.nvimのそのほかの操作については以下の記事を参照してください。
plugins: ファイラー
ファイラーにはnvim-tree
をインストールします。
nvim起動時のnvim-treeの画面
以下のようにキーマップを設定しています。
キーマップ | 操作 |
---|---|
<C-n> |
NvimTreeを表示・非表示にする |
<C-m> |
NvimTreeにフォーカスする |
nvim-treeの設定
-- open File Tree when open
local function open_nvim_tree()
require("nvim-tree.api").tree.open()
end
vim.api.nvim_create_autocmd({ "VimEnter" }, { callback = open_nvim_tree })
return {
"nvim-tree/nvim-tree.lua",
version = "*",
lazy = false,
dependencies = {
"nvim-tree/nvim-web-devicons",
},
keys = {
{mode = "n", "<C-n>", "<cmd>NvimTreeToggle<CR>", desc = "NvimTreeをトグルする"},
{mode = "n", "<C-m>", "<cmd>NvimTreeFocus<CR>", desc = "NvimTreeにフォーカス"},
},
config = function()
require("nvim-tree").setup {
git = {
enable = true,
ignore = true,
}
}
end,
}
plugins: bufferをタブ表示
neovimで開いたファイルを上部にタブとして表示するプラグインです。
bufferlineの設定
return {
'akinsho/bufferline.nvim',
version = "*",
dependencies = 'nvim-tree/nvim-web-devicons',
config = function ()
vim.opt.termguicolors = true
require("bufferline").setup{}
end
}
bufferlineによるタブの表示(垢枠内)
nvimのbufferの切り替えは<C-h>
と<C-l>
で移動できるように設定しています。ちなみに、プラグインのbufferlineと<C-h>
・<C-l>
は関係なく動きます。
(〜前略〜)
-- ファイル切り替え
vim.keymap.set("n", "<C-h>", "<cmd>bprev<CR>")
vim.keymap.set("n", "<C-l>", "<cmd>bnext<CR>")
キーマップ | 操作 |
---|---|
<C-h> |
左のバッファに移動 |
<C-l> |
右のバッファに移動 |
plugins: ファジーファインダー
ファジーファインダーとは、あいまい検索ができるツールのことで、ファイルの文字列やファイル名を検索できます。ファジーファインダーにはtelescope.nvim
を利用します。
telescopeの文字列検索の画面
検索には様々なモードがあり、以下のように設定しています。
キーマップ | 操作 |
---|---|
<leader>ff |
ファイル名を検索 |
<leader>fg |
プロジェクト内の文字列を検索 |
<leader>fh |
neovimのヘルプタグを検索 |
telescope.nvimの設定
return {
'nvim-telescope/telescope.nvim', tag = '0.1.8',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = 'Telescope find files' })
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Telescope live grep' })
vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Telescope help tags' })
end
}
live_grep
機能を利用するためには、ripgrep
をインストールする必要があります。brewでインストールします。
$ brew install ripgrep
plugins: Git
エディタの行番号の左側に、ファイルに変更・追記がある行を表示するようにします。
gitsignsでエディタの左側に各行のgitの状態を表示(赤枠内)
gitsigns.nvimの設定
return {
"lewis6991/gitsigns.nvim",
config = function()
require('gitsigns').setup {
signs = {
add = { text = '┃' },
change = { text = '┃' },
delete = { text = '_' },
topdelete = { text = '‾' },
changedelete = { text = '~' },
untracked = { text = '┆' },
},
signs_staged = {
add = { text = '┃' },
change = { text = '┃' },
delete = { text = '_' },
topdelete = { text = '‾' },
changedelete = { text = '~' },
untracked = { text = '┆' },
},
signs_staged_enable = true,
signcolumn = true, -- Toggle with `:Gitsigns toggle_signs`
numhl = false, -- Toggle with `:Gitsigns toggle_numhl`
linehl = false, -- Toggle with `:Gitsigns toggle_linehl`
word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff`
watch_gitdir = {
follow_files = true
},
auto_attach = true,
attach_to_untracked = false,
current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame`
current_line_blame_opts = {
virt_text = true,
virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align'
delay = 1000,
ignore_whitespace = false,
virt_text_priority = 100,
use_focus = true,
},
current_line_blame_formatter = '<author>, <author_time:%R> - <summary>',
sign_priority = 6,
update_debounce = 100,
status_formatter = nil, -- Use default
max_file_length = 40000, -- Disable if file is longer than this (in lines)
preview_config = {
-- Options passed to nvim_open_win
border = 'single',
style = 'minimal',
relative = 'cursor',
row = 0,
col = 1
},
}
end
}
plugins: Git Blame
git-blameはカーソルがある行のgitの編集者・編集日時などステータスを表示するプラグインです。
git-blameでカーソル行のステータスを表示している
git-blame.nvimの設定
return {
"f-person/git-blame.nvim",
-- load the plugin at startup
event = "VeryLazy",
-- Because of the keys part, you will be lazy loading this plugin.
-- The plugin will only load once one of the keys is used.
-- If you want to load the plugin at startup, add something like event = "VeryLazy",
-- or lazy = false. One of both options will work.
opts = {
-- your configuration comes here
-- for example
enabled = true, -- if you want to enable the plugin
message_template = " <author>, <date> : <summary>", -- template for the blame message, check the Message template section for more options
date_format = "%m-%d-%Y %H:%M:%S", -- template for the date, check Date format section for more options
virtual_text_column = 1, -- virtual text start column, check Start virtual text at column section for more options
},
}
plugins: コンフリクトの解消・diffの確認
gitのコンフリクト解消にはdiffview.nvim
を使います。
コンフリクトが発生している画面
上記のようなコンフリクトがある状態で<leader>hd
を押下すると以下のコンフリクト解消画面になります。コンフリクト解消のためのコマンドだけ載せておきます。コマンド実行の結果などは隠しておくので適宜確認してください。
コンフリクト解消画面
キーマップ | 操作 |
---|---|
<leader>hd |
コンフリクト解消画面の表示 |
<leader>hc |
コンフリクト解消画面を閉じる |
<leader>co |
マージ先の変更を取り込む |
<leader>ct |
マージ元の変更を取り込む |
<leader>cb |
両方のコンフリクトを取り込まない(まず選ばない) |
<leader>ca |
両方のコンフリクトを取り込む(まず選ばない) |
コンフリクト解消画面での、各種コマンドの実行結果
<leader>co
を押下した時:左側(マージ先)の変更を取り込む
<leader>ct
を押下した時:右側(マージ元)の変更を取り込む
<leader>cb
を押下した時:両方のコンフリクトを取り込まない
<leader>ca
を押下した時:両方のコンフリクトを取り込む
:h diffview-merge-toolの説明
READMEにはコンフリクト解消に関することはあまり書かれてないのですが、:h diffview-merge-tool
を実行すると説明を見ることができます。説明の一部を載せます。
You can jump between conflict markers with `]x` and `[x`. This works
from the file panel as well.
Additionally there are mappings for operating directly on the conflict
markers:
• `<leader>co`: Choose the OURS version of the conflict.
• `<leader>ct`: Choose the THEIRS version of the conflict.
• `<leader>cb`: Choose the BASE version of the conflict.
• `<leader>ca`: Choose all versions of the conflict (effectively
just deletes the markers, leaving all the content).
• `dx`: Choose none of the versions of the conflict (delete the
conflict region).
そのほかにも<leader>hf
で単一のファイルの変更履歴を追えるようにすることもできます。画面下部がgitの履歴で、上部のパネルの左が編集前、右が編集後になっています。単一のファイルに注目して編集履歴を追えるのは便利かなと思います。
単一のファイルに注目して変更履歴を追う
diffview.nvimの設定
return {
"sindrets/diffview.nvim",
config = function ()
require("diffview").setup()
end,
lazy = false,
keys = {
{mode = "n", "<leader>hh", "<cmd>DiffviewOpen HEAD~1<CR>", desc = "1つ前とのdiff"},
{mode = "n", "<leader>hf", "<cmd>DiffviewFileHistory %<CR>", desc = "ファイルの変更履歴"},
{mode = "n", "<leader>hc", "<cmd>DiffviewClose<CR>", desc = "diffの画面閉じる"},
{mode = "n", "<leader>hd", "<cmd>Diffview<CR>", desc = "コンフリクト解消画面表示"},
},
}
plugins: neovimの前面にターミナルを表示
toggletermをインストールすると、nvimを開きながらターミナルなどを開くことができます。設定をdirection = "float"
にすると、<leader>tt
で前面にターミナルを出すことができます。また<leader>lg
でlazygit
を出し、git操作が可能です。q
で前面に出したlazygit
を閉じることができます。
lazygitをfloatでneovimの前面に表示した状態
toggleterm.nvimの設定
return {
'akinsho/toggleterm.nvim',
version = "*",
config = function()
require("toggleterm").setup{
-- size can be a number or function which is passed the current terminal
size = 20,
open_mapping = [[<leader>tt]], -- or { [[<c-\>]], [[<c-¥>]] } if you also use a Japanese keyboard.
hide_numbers = true, -- hide the number column in toggleterm buffers
shade_filetypes = {},
autochdir = false, -- when neovim changes it current directory the terminal will change it's own when next it's opened
shade_terminals = true, -- NOTE: this option takes priority over highlights specified so if you specify Normal highlights you should set this to false
start_in_insert = true,
insert_mappings = true, -- whether or not the open mapping applies in insert mode
terminal_mappings = true, -- whether or not the open mapping applies in the opened terminals
persist_size = true,
persist_mode = true, -- if set to true (default) the previous terminal mode will be remembered
direction = 'float',
close_on_exit = true, -- close the terminal window when the process exits
clear_env = false, -- use only environmental variables from `env`, passed to jobstart()
-- Change the default shell. Can be a string or a function returning a string
shell = vim.o.shell,
auto_scroll = true, -- automatically scroll to the bottom on terminal output
-- This field is only relevant if direction is set to 'float'
float_opts = {
border = 'curved',
winblend = 3,
title_pos = 'center',
},
winbar = {
enabled = false,
name_formatter = function(term) -- term: Terminal
return term.name
end
},
}
-- ここでlazygitを開く設定を追加している
local Terminal = require('toggleterm.terminal').Terminal
local lazygit = Terminal:new({ cmd = "lazygit", hidden = true })
function _lazygit_toggle()
lazygit:toggle()
end
vim.api.nvim_set_keymap("n", "<leader>lg", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true})
end
}
キーマップ | 操作 |
---|---|
<leader>tt |
ターミナルを表示する |
<leader>lg |
lazygitを表示する |
plugins: コメントアウト
コメントアウトのトグルにはComment.nvim
を使います。
comment.nvimの設定
return {
'numToStr/Comment.nvim',
}
キーマップ | 操作 |
---|---|
gcc |
Normal modeにて単一の行のコメントアウトをトグル |
gc |
Visual modeにて複数行のコメントアウトをトグル |
plugins: LSP
コーディングする際に必須となる機能を追加します。LSPとはLanguage Server Protocolの略称で、コーディング支援をエディタ・言語に依存しない形に切り出して広く使いやすいようにした仕組みです。詳しくは以下の記事を参照してください。
以下の記事を参考に導入して、合計4つのプラグインを入れます。
LSP.1: LSP管理
LSPの設定とサーバー管理のためのプラグインを3つ入れます。
私は、主にPython
とTypeScript
を使うのでそれらのLSPサーバーを設定します。これらの設定をすることで、定義ジャンプや変数の定義のコメントを見れるようになります。加えて、誤字も確認できるようにtypos_lsp
という設定も追加しています。
LSP周りの3つの設定ファイル
以下の3つのファイルを起きます。nvim-lspconfig.lua
とmason-nvim.lua
は設定を読み込むためだけに利用しています。
return {
"neovim/nvim-lspconfig",
version = "*",
lazy = false,
config = function()
local lspconfig = require('lspconfig')
end,
}
return {
"williamboman/mason.nvim",
version = "*",
lazy = false,
config = function()
require("mason").setup()
end,
}
return {
"williamboman/mason-lspconfig.nvim",
version = "*",
lazy = false,
config = function()
local lsp_servers = { "lua_ls", "pyright", "ruff", "ts_ls", "html", "yamlls", "jsonls" }
local diagnostics = { "typos_lsp" }
require("mason-lspconfig").setup {
ensure_installed = vim.tbl_flatten({ lsp_servers, diagnostics }),
}
require("mason-lspconfig").setup_handlers {
function (server_name)
local nvim_lsp = require("lspconfig")
require("lspconfig").typos_lsp.setup {}
require("lspconfig").pyright.setup {
root_dir = nvim_lsp.util.root_pattern(".venv"),
-- cmd = { "bash", "-c", "source ./.venv/bin/activate"},
settings = {
python = {
-- 仮想環境のルートパス
venvPath = ".",
-- 仮想環境のフォルダ名
-- venv = ".venv",
pythonPath = "./.venv/bin/python",
-- analysis = {
-- extraPaths = {"."},
-- autoSearchPaths = true,
-- useLibraryCodeForTypes = true
-- }
}
}
}
end,
}
-- カーソル下の変数の情報
vim.keymap.set('n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>')
-- 定義ジャンプ
vim.keymap.set('n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>')
-- 定義ジャンプ後に下のファイルに戻る
vim.keymap.set('n', 'gt', '<C-t>')
-- 改行やインデントなどのフォーマット
vim.keymap.set('n', 'gf', '<cmd>lua vim.lsp.buf.formatting()<CR>')
-- カーソル下の変数をコード内で参照している箇所
vim.keymap.set('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>')
-- 変数名のリネーム
vim.keymap.set('n', 'gn', '<cmd>lua vim.lsp.buf.rename()<CR>')
end,
}
キーマップ | 操作 |
---|---|
K |
カーソル下の変数などの定義を表示 |
gd |
定義先へジャンプ |
gt |
ジャンプした定義先から戻る |
K
でカーソル下の定義を表示した画面
LSP.2: 補完
次に、コーディングしながら補完候補を出すためのプラグインを入れます。
nvim-cmpの設定
公式サイトにはなかったため、lazy.nvimでの設定は次のサイトを参考にしました。
return {
"hrsh7th/nvim-cmp",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
"hrsh7th/cmp-nvim-lua",
"hrsh7th/cmp-buffer",
"hrsh7th/cmp-path",
"hrsh7th/cmp-cmdline",
"saadparwaiz1/cmp_luasnip",
"L3MON4D3/LuaSnip",
},
config = function ()
local cmp = require("cmp")
local types = require('cmp.types')
vim.opt.completeopt = { "menu", "menuone", "noselect" }
cmp.setup({
snippet = {
expand = function(args)
require("luasnip").lsp_expand(args.body) -- For `luasnip` users.
end,
},
window = {
-- completion = cmp.config.window.bordered(),
-- documentation = cmp.config.window.bordered(),
},
mapping = cmp.mapping.preset.insert({
-- <C-n>: down, <C-p>: up
['<Tab>'] = cmp.mapping.select_next_item({ behavior = types.cmp.SelectBehavior.Insert }),
['<S-Tab>'] = cmp.mapping.select_prev_item({ behavior = types.cmp.SelectBehavior.Insert }),
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-l>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "nvim_lua" },
-- { name = "luasnip" }, -- For luasnip users.
-- { name = "orgmode" },
}, {
{ name = "buffer" },
{ name = "path" },
}),
})
end
}
<Tab>
でも候補を選べるようにキーマップを設定しました。
キーマップ | 操作 |
---|---|
<C-p> |
上の補完候補に移動(デフォルト) |
<S-Tab> |
上の補完候補に移動 |
<C-n> |
下の補完候補に移動(デフォルト) |
<Tab> |
下の補完候補に移動 |
<C-b> |
補完候補のドキュメントを上にスクロール(ドキュメントが短い場合は効かないように見える) |
<C-f> |
補完候補のドキュメントを下にスクロール(ドキュメントが短い場合は効かないように見える) |
<C-l> |
補完候補が出てない時に出すようにする |
<C-e> |
補完候補を閉じる |
<CR> |
補完候補から選択 |
nvim-comp
で補完が動いている様子
plugins: インデントの色付け
インデントに色をつけるプラグインです。注意点は言語ごとに設定が必要なところです。ただ、lua
だけは何もせずにインデントに色がつきました。
インデントに色がついている様子(Python)
インデントに色がついている様子(TypeScript)
インデントに色がついている様子(Lua)
indent-blankline.nvimの設定
return {
'lukas-reineke/indent-blankline.nvim',
dependencies = {
'HiPhish/rainbow-delimiters.nvim',
'nvim-treesitter/nvim-treesitter',
},
config = function()
require('nvim-treesitter.configs').setup({
ensure_installed = { "python", "typescript" },
highlight = {
enable = true,
}
})
local highlight = {
"RainbowRed",
"RainbowYellow",
"RainbowBlue",
"RainbowOrange",
"RainbowGreen",
"RainbowViolet",
"RainbowCyan",
}
local hooks = require("ibl.hooks")
-- create the highlight groups in the highlight setup hook, so they are reset
-- every time the colorscheme changes
hooks.register(hooks.type.HIGHLIGHT_SETUP, function()
vim.api.nvim_set_hl(0, "RainbowRed", { fg = "#E06C75" })
vim.api.nvim_set_hl(0, "RainbowYellow", { fg = "#E5C07B" })
vim.api.nvim_set_hl(0, "RainbowBlue", { fg = "#61AFEF" })
vim.api.nvim_set_hl(0, "RainbowOrange", { fg = "#D19A66" })
vim.api.nvim_set_hl(0, "RainbowGreen", { fg = "#98C379" })
vim.api.nvim_set_hl(0, "RainbowViolet", { fg = "#C678DD" })
vim.api.nvim_set_hl(0, "RainbowCyan", { fg = "#56B6C2" })
end)
vim.g.rainbow_delimiters = { highlight = highlight }
require("ibl").setup { scope = { highlight = highlight } }
hooks.register(hooks.type.SCOPE_HIGHLIGHT, hooks.builtin.scope_highlight_from_extmark)
end,
}
plugins: テーマ(colorscheme)
neovimの見た目もプラグインで変えることができます。「neovim colorscheme」などと検索するとトレンドなどを載せているサイトが出てきます。その中から以下のcatppuccin
を選びました。見た目が柔らかい感じがするので良さげです。本当はJetBrainsに近いdarcula
テーマを選びたかったのですが、あまり良さそうなのがなかったので一旦諦めました。
catppuccinの設定
return {
"catppuccin/nvim",
name = "catppuccin",
priority = 1000,
-- enabled = false,
config = function()
require("catppuccin").setup({
flavour = "frappe",
integrations = {
gitsigns = true,
nvimtree = true,
}
})
vim.cmd.colorscheme("catppuccin")
end
}
plugins: IMEの設定
以下の記事を頼りにIMEを自動で切り替えるプラグインを入れます。これを入れてjj
やkk
ではなく<C-g>
でInsert modeを抜けるようにすると便利です。
事前にim-select
のインストールが必要です。
$ brew tap daipeihust/tap
$ brew install im-select
im-selectの設定
return {
"keaising/im-select.nvim",
config = function()
require("im_select").setup({
-- デフォルトのIME
default_im_select = "com.apple.keylayout.ABC",
-- 以下のイベント時に、デフォルトのIMEになる
set_default_events = {"VimEnter", "InsertEnter", "InsertLeave"},
-- 以下のイベント時に、前回使われていたIMEになる(無効にしている)
set_previous_events = {},
})
end,
}
おわりに
これでひとまずneovimの環境構築は終わりです。構築しつつ触ってみて感じたデメリットとメリットと所感をまとめます。
デメリット
デメリットは以下の3点を感じました。
1つ目は、neovimとプラグインの開発が今なお活発であるため、環境構築が大変な点です。プラグインなどの概念に慣れてしまえば大変では無いのですが、初学者からすると「あの機能も・この機能も必要なのか・・・」のようにIDEとneovimに関するプラグインへのある程度の理解が必要なため大変だと思います。
2つ目は、neovimに関するコミュニティ・技術書・記事もたくさんあるのですが、欲しい情報にアクセスするのがプログラミング言語に比べると慣れるまで大変という点です。特にプラグインのインストールに関して、READMEに書いてあるインストール方法をそのままコピペでは利用できない場合もあり、「自分の使っているプラグインマネージャーだとどう書くんだ・・・?」となることが多々ありました。
3つ目に、よく言われている操作がわかりづらいと言う点です。ただ、これは他の2つに比べると慣れの問題だと思うので、触っていくとデメリットには感じないようになりました。どちらかというとneovimに慣れてしまうとターミナルなどの操作においてj
とk
を押しそうになってしまうところが大変です。zshにvimモードなるものがあるらしいので気になっています。
メリット
メリットは以下の4点を感じました。
1つ目は、設定をdotfileで管理できる点です。これは私がneovimを使っていて1番気に入っている点です。WezTerm
やstarship
など、ターミナル関連のアプリケーションは~/.config
以下で設定を管理できることが多いです。これらをdotfile
と言うGitリポジトリで、管理して育てていくのがとてもエンジニアっぽくていいなと思っています。
2つ目は、コマンドの操作やコーディングをキーボードのみでおおよそ完結できる点です。今まで私はMacでJetBrainsとターミナルとGitクライアントなどをそれぞれフルスクリーンで表示しつつ、画面を左右に切り替えながら(=トラックパッドを3本指で左右にスワイプしながら)使っていました。それらの作業がターミナル1つで完結するのはとても良いなと思います。ただし、ブラウザなど他のアプリを使っているので、全ての作業がターミナルだけで完結するわけでは無いです。
3つ目は、IDEの仕組みを知れたり、IDEの便利さを改めて感じれた点です。ファイルのタブや曖昧検索なども単一の機能として独立した機能として存在していて、それらがうまく組み合わさっていることで、普段使っているIDEは動いているんだなと改めて感じました。加えて、普段何気なく使っているIDEの機能である補完に関して、LSPと言うプロトコルがあってその上で動いていることを知れたのはよかったです。
4つ目は、vimが怖く無くなった点です。今までターミナルでのエディタはnanoしか使っておらずたまに意図せずvimの利用を迫られた時にドギマギして:wq
などを押してなんとかしていました。その漠然としたvimへの恐怖感は無くなったかなと思います。
所感
ここまで書いておいてなんなのですが、neovimは手放しに他人におすすめはできないと思いました。私にとってneovimを使うメリットはデメリットを超えていますが、それが当てはまらない場合もあるからです。neovimは操作にある程度の慣れが必要で、そもそも環境を作るのにも一苦労します。加えて設定をテキストファイルで管理できることに楽しみを覚えない場合には、JetBrainsや他のIDEを使ったほうが良いかと思います。ただ、慣れるとコーディングの大体の操作がターミナルだけで完結するのでとても良いと思います。
あと、最初に使うエディタではないと思いました。特定の言語に精通し、自分に合ったJetBrainsなどのIDE/エディタを見つけた上で、そのエディタに使いたい機能が無い場合や特定のやりたいことができない場合に、neovimに移るのが良いと思いました。最初にJetBrainsなどのIDEを使うと、IDEの補完機能への解像度や、知らない機能がある便利さに気付ける点において良いなと思っています。
2024年9月の末頃からneovimを触り始めて、3日ほどでhjkl
などの基本操作は慣れました。neovimの環境は1日1~2時間程度で作っていき、トータル40~50時間ほど(実期間は1ヶ月くらい・・・)で最低限の機能は持たせられたかなという感じです。ただ、よくよく考えると最低限使える機能を持たせるのに1ヶ月もかかるって今の世の中だと中々無いと思うのですが、その大変さも相まってneovimのことがより好きになりました。
プラグインを随時追加していきつつ、これからも使っていきます!
参考
Discussion