🚶🏻

やっとNeovimの設定ファイルをinit.luaに移行した

2022/08/23に公開

https://www.youtube.com/watch?v=gsc0Mw8y-cY

はじめに

hisasann/neovim: hisasann's neovim settings

Vim 時代は .vimrc 一本、 Neovim に変えてからは Init.vimdein.toml で設定やプラグインを管理していました。

neovim/vim at master · hisasann/neovim

toml ファイルにプラグインの細かい設定を書く場合は、以下のように改行コード込みの文字列として記述します。

[[plugins]]
hook_add = """
lua << EOF
-- ここに lua のコードを書くことができる
EOF
"""

ぼくはこの場合に -- のようなコメントの行があると、うまくプラグインが動かないなどの挙動がありました。

なので、基本コメントは書かないようにしていました。

このように一工夫する必要があったり、うまく動かない機能などもありました。

たとえば、ファイルの保存時に何かするみたいなのが動きませんでした。

lspconfig の以下のようなコードです。

  -- formatting
  if client.server_capabilities.documentFormattingProvider then
    vim.api.nvim_create_autocmd("BufWritePre", {
      group = vim.api.nvim_create_augroup("Format", { clear = true }),
      buffer = bufnr,
      callback = function() vim.lsp.buf.formatting_seq_sync() end
    })
  end

というのもあり、このあたりがモチベーションになりました。

何をしたかを忘れたいので、メモ程度ですが、残しておきます。

また、一応 Neovim をインストールするところからメモしておきますので、今から Neovim を lua で使ってみたい系の方でも読めるかと思います。

どんな設定なのか

lua 移植時に、いったんは必須ではないプラグインなどは入れないようにしました。

なので、以下に列挙したぐらいのことしかできません。

ただ、これはぼくにとって最高の状態なので、同じような設定を実現したい方には参考になるかもしれません。

  • ファイル操作を迅速にしたい
  • TypeScript を書きたい
  • cmp でいい感じのインテリセンスを実現したい
  • prettier で保存時にフォーマットしたい
  • tree-sitter でシンタックスハイライトはより良い感じにしたい
  • powerline 的なものは lua 製のものにしたい

Neovimをインストールする

brew で入れるのが主流でしょう。

$ brew install neovim
$ which nvim
/opt/homebrew/bin/nvim
$ nvim -v
NVIM v0.8.0-dev+547-ge837f29ce

lua が動くことを一応確認しておきます。

$ nvim
:lua print('hello lua')

init.luaを作成する

すでに init.vim がある場合は、

$ mv init.vim _init.vim

のようにリネームして、 Neovim が勝手に読み込まないようにしておきましょう。

$ cd .config
$ mkdir nvim && cd nvim
$ touch init.lua
$ nvim init.lua

以下を記述します。

print('init.lua')

ここまできて、コマンド表示の部分に init.lua が出れば読み込めています。

今回のぼくの作った init.lua の中身の最終形態は以下のようになります。

require("base")
require("autocmds")
require("options")
require("keymaps")
require("colorscheme")
require("plugins")

そこまで細かくはファイル分割はしませんでした。

必要最低限という程度です。

luaディレクトリを作成する

$ mkdir lua && cd lua

この lua というディレクトリは特殊で、 require('ファイルのパス') のように指定することで、 lua ディレクトリのファイルを読み込むことができます。

base設定をする

$ touch base.lua
vim.cmd("autocmd!")

vim.scriptencoding = "utf-8"

vim.wo.number = true

-- Open hoge file
vim.api.nvim_create_user_command("Hoge", function(opts)
	vim.cmd("e " .. "~/_/hoge/hoge.markdown")
end, {})

ぼくは :Hoge というコマンドをメモ帳起動用に割り当てています。

もっといい名前にしたかったのですが、もう手に馴染んでしまっているので、いいんです。

autocmds設定をする

$ touch autocmds.lua
local augroup = vim.api.nvim_create_augroup -- Create/get autocommand group
local autocmd = vim.api.nvim_create_autocmd -- Create autocommand

-- Remove whitespace on save
autocmd("BufWritePre", {
	pattern = "*",
	command = ":%s/\\s\\+$//e",
})

-- Don't auto commenting new lines
autocmd("BufEnter", {
	pattern = "*",
	command = "set fo-=c fo-=r fo-=o",
})

-- Restore cursor location when file is opened
autocmd({ "BufReadPost" }, {
	pattern = { "*" },
	callback = function()
		vim.api.nvim_exec('silent! normal! g`"zv', false)
	end,
})

option設定をする

$ touch options.lua
local options = {
	encoding = "utf-8",
	fileencoding = "utf-8",
	title = true,
	backup = false,
	clipboard = "unnamedplus",
	cmdheight = 2,
	completeopt = { "menuone", "noselect" },
	conceallevel = 0,
	hlsearch = true,
	ignorecase = true,
	mouse = "a",
	pumheight = 10,
	showmode = false,
	showtabline = 2,
	smartcase = true,
	smartindent = true,
	swapfile = false,
	termguicolors = true,
	timeoutlen = 300,
	undofile = true,
	updatetime = 300,
	writebackup = false,
	shell = "fish",
	backupskip = { "/tmp/*", "/private/tmp/*" },
	expandtab = true,
	shiftwidth = 2,
	tabstop = 2,
	cursorline = true,
	number = true,
	relativenumber = false,
	numberwidth = 4,
	signcolumn = "yes",
	wrap = false,
	winblend = 0,
	wildoptions = "pum",
	pumblend = 5,
	background = "dark",
	scrolloff = 8,
	sidescrolloff = 8,
	guifont = "monospace:h17",
	splitbelow = false, -- オンのとき、ウィンドウを横分割すると新しいウィンドウはカレントウィンドウの下に開かれる
	splitright = false, -- オンのとき、ウィンドウを縦分割すると新しいウィンドウはカレントウィンドウの右に開かれる
}

vim.opt.shortmess:append("c")

for k, v in pairs(options) do
	vim.opt[k] = v
end

vim.cmd("set whichwrap+=<,>,[,],h,l")
vim.cmd([[set iskeyword+=-]])
vim.cmd([[set formatoptions-=cro]]) -- TODO: this doesn't seem to work

設定が反映されているかを確認します。

:set backupskip
backupskip=/tmp/*,/private/tmp/*

今回、キーの入力待機時間の設定が割と重要でした。

updatetime がそれなんですが、はじめ 100ms にしてみたのですが、相当速いキー入力じゃないと連続したキーとして扱ってくれなかったので、 300ms にしたら安定しました。

ここは個人差がありそうですね。

また、結構ハマってしまったのですが、 splitbelow splitright を true にしてしまっていたのですが、これのせいで :vsplit したときに次のウィンドウに移動する keymap が動かなくてなんでなのかをデバッグしました。

keymaps設定をする

$ touch keymaps.lua
local opts = { noremap = true, silent = true }
local term_opts = { silent = true }

--local keymap = vim.keymap
local keymap = vim.api.nvim_set_keymap

--Remap space as leader key
keymap("", "<Space>", "<Nop>", opts)
vim.g.mapleader = " "
vim.g.maplocalleader = " "

-- Modes
--   normal_mode = 'n',
--   insert_mode = 'i',
--   visual_mode = 'v',
--   visual_block_mode = 'x',
--   term_mode = 't',
--   command_mode = 'c',

-- Normal --
-- Better window navigation
keymap("n", "<C-h>", "<C-w>h", opts)
keymap("n", "<C-j>", "<C-w>j", opts)
keymap("n", "<C-k>", "<C-w>k", opts)
keymap("n", "<C-l>", "<C-w>l", opts)

-- New tab
keymap("n", "te", ":tabedit", opts)
-- 新しいタブを一番右に作る
keymap("n", "gn", ":tabnew<Return>", opts)
-- move tab
keymap("n", "gh", "gT", opts)
keymap("n", "gl", "gt", opts)

-- Split window
keymap("n", "ss", ":split<Return><C-w>w", opts)
keymap("n", "sv", ":vsplit<Return><C-w>w", opts)

-- Select all
keymap("n", "<C-a>", "gg<S-v>G", opts)

-- Do not yank with x
keymap("n", "x", '"_x', opts)

-- Delete a word backwards
keymap("n", "dw", 'vb"_d', opts)

-- 行の端に行く
keymap("n", "<Space>h", "^", opts)
keymap("n", "<Space>l", "$", opts)

-- ;でコマンド入力( ;と:を入れ替)
keymap("n", ";", ":", opts)

-- 行末までのヤンクにする
keymap("n", "Y", "y$", opts)

-- <Space>q で強制終了
keymap("n", "<Space>q", ":<C-u>q!<Return>", opts)

-- ESC*2 でハイライトやめる
keymap("n", "<Esc><Esc>", ":<C-u>set nohlsearch<Return>", opts)

-- Insert --
-- Press jk fast to exit insert mode
keymap("i", "jk", "<ESC>", opts)

-- コンマの後に自動的にスペースを挿入
keymap("i", ",", ",<Space>", opts)

-- Visual --
-- Stay in indent mode
keymap("v", "<", "<gv", opts)
keymap("v", ">", ">gv", opts)

-- ビジュアルモード時vで行末まで選択
keymap("v", "v", "$h", opts)

-- 0番レジスタを使いやすくした
keymap("v", "<C-p>", '"0p', opts)

colorscheme設定をする

$ touch colorscheme.lua

もともとは Slack も iTerm2 も全部 dracula にしていたのですが、

最近は dracula ではなく nightfox をよく使っています。

vim.cmd [[
try
  colorscheme nightfox
catch /^Vim\%((\a\+)\)\=:E185/
  colorscheme default
  set background=dark
endtry
]]

plugins設定をする

$ touch plugins.lua
local fn = vim.fn

-- Automatically install packer
local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim"
if fn.empty(fn.glob(install_path)) > 0 then
	PACKER_BOOTSTRAP = fn.system({
		"git",
		"clone",
		"--depth",
		"1",
		"https://github.com/wbthomason/packer.nvim",
		install_path,
	})
	print("Installing packer close and reopen Neovim...")
	vim.cmd([[packadd packer.nvim]])
end

-- Autocommand that reloads neovim whenever you save the plugins.lua file
vim.cmd([[
  augroup packer_user_config
    autocmd!
    autocmd BufWritePost plugins.lua source <afile> | PackerSync
  augroup end
]])

-- Use a protected call so we don't error out on first use
local status_ok, packer = pcall(require, "packer")
if not status_ok then
	return
end

-- Have packer use a popup window
packer.init({
	display = {
		open_fn = function()
			return require("packer.util").float({ border = "rounded" })
		end,
	},
})

-- Install your plugins here
return packer.startup(function(use)
	-- My plugins here

	use({ "wbthomason/packer.nvim" })
	use({ "nvim-lua/plenary.nvim" }) -- Common utilities

	-- Colorschemes
	use({ "EdenEast/nightfox.nvim" }) -- Color scheme

	use({ "nvim-lualine/lualine.nvim" }) -- Statusline
	use({ "windwp/nvim-autopairs" }) -- Autopairs, integrates with both cmp and treesitter
	use({ "kyazdani42/nvim-web-devicons" }) -- File icons
	use({ "akinsho/bufferline.nvim" })

	-- cmp plugins
	use({ "hrsh7th/nvim-cmp" }) -- The completion plugin
	use({ "hrsh7th/cmp-buffer" }) -- buffer completions
	use({ "hrsh7th/cmp-path" }) -- path completions
	use({ "hrsh7th/cmp-cmdline" }) -- cmdline completions
	use({ "saadparwaiz1/cmp_luasnip" }) -- snippet completions
	use({ "hrsh7th/cmp-nvim-lsp" })
	use({ "hrsh7th/cmp-nvim-lua" })
	use({ "onsails/lspkind-nvim" })

	-- snippets
	use({ "L3MON4D3/LuaSnip" }) --snippet engine

	-- LSP
	use({ "neovim/nvim-lspconfig" }) -- enable LSP
	use({ "williamboman/nvim-lsp-installer" }) -- simple to use language server installer
	use({ "jose-elias-alvarez/null-ls.nvim" }) -- for formatters and linters
	use({ "glepnir/lspsaga.nvim" }) -- LSP UIs

	-- Formatter
	use({ "MunifTanjim/prettier.nvim" })

	-- Telescope
	use({ "nvim-telescope/telescope.nvim" })

	-- Treesitter
	use({ "nvim-treesitter/nvim-treesitter", { run = ":TSUpdate" } })
	use({ "nvim-telescope/telescope-file-browser.nvim" })

	use({ "windwp/nvim-ts-autotag" })

	-- Automatically set up your configuration after cloning packer.nvim
	-- Put this at the end after all plugins
	if PACKER_BOOTSTRAP then
		require("packer").sync()
	end
end)

それぞれのプラグインの設定の部分は以下のリンクからご覧ください。

neovim/plugin at master · hisasann/neovim

packer.nvimでプラグインを管理する

packer.nvimをインストールする

一応ここに packer.nvim のインストール方法を貼ってはいますが、上記 plugins.lua の最初のほうで packer.nvim がない場合は自動的にインストールするようにしているので、不要ではあります。

git clone --depth 1 https://github.com/wbthomason/packer.nvim\
 ~/.local/share/nvim/site/pack/packer/start/packer.nvim

wbthomason/packer.nvim: A use-package inspired plugin manager for Neovim. Uses native packages, supports Luarocks dependencies, written in Lua, allows for expressive config

packer.nvimの使い方

packer.nvim のメインとなる部分は、以下のように use でインストールしたいプラグインを指定するだけです。

use({ "neovim/nvim-lspconfig" })

さらに git の commit を指定することで、特定のバージョンもインストールできるようです。

use({ "neovim/nvim-lspconfig", commit = "148c99bd09b44cf3605151a06869f6b4d4c24455" })

さいごに

hisasann/neovim: hisasann's neovim settings

なんだかんだで10時間ほどいろいろいじっていましたが、ベーシックな部分は5時間ほどでマイグレーションができました。

さらに深夜に作業をしていたので、とにかく楽しかったです。

lofi hiphop を聴きながら無限に Neovim の設定ファイルをいじるのは最高です。

とくに今回の収穫は、 telescope-file-browser.nvim との出会いでした。

nvim-telescope/telescope-file-browser.nvim: File Browser extension for telescope.nvim

すごく使いやすいファイラーでもあり、爆速です。

今まではダークパワーの Shougo/defx.nvim: The dark powered file explorer implementation for neovim/Vim8 を使っていました。

こちらは数年お世話になったファイラーで、ほんと好きなファイラーです。

参考記事

Set up Neovim on a new M2 MacBook Air for coding React, TypeScript, Tailwind CSS, etc. - YouTube

NeovimとLua

Neovim プラグインを(ほぼ)全て Lua に移行した

brainfucksec/neovim-lua: Neovim KISS configuration with Lua

Neovim - Null-LS Formatting, Linting & more (Supports prettier, black, eslint, flake8 & more) - YouTube

LunarVim/Neovim-from-scratch: A Neovim config designed from scratch to be understandable

craftzdog/dotfiles-public: My personal dotfiles

Discussion