Neovim
GitHub概要
スター91,915
ウォッチ984
フォーク6,251
作成日:2014年1月31日
言語:Vim Script
ライセンス:Other
トピックス
apicluaneovimnvimtext-editorvim
スター履歴
データ取得日時: 2025/8/13 01:43
開発ツール
Neovim
概要
NeovimはVimの現代的なフォークとして開発されたテキストエディタです。Vimの哲学を継承しながら、Luaスクリプト、内蔵LSP、Tree-sitterなどの現代的機能を追加し、より拡張性と保守性を向上させています。
詳細
Neovimは2014年にThiago de Arruda氏によってVimのフォークとして開始されました。Vimの100%近くをBram Moolenaar氏1人で維持している状況と比較し、Neovimははるかに広い貢献者ベースを持ち、活発な開発コミュニティによって支えられています。
Neovimの最大の革新は、Luaをファーストクラスのスクリプト言語として導入したことです。従来のVimscriptに加えてLuaによる設定とプラグイン開発が可能になり、より高速で保守性の高い拡張機能を作成できます。内蔵LSP(Language Server Protocol)サポート、Tree-sitterによる高度なシンタックスハイライト、非同期処理の改善などが主要な特徴です。
「ヘッドレス」コアエディタ設計により、他のプログラムとの統合が容易になり、IDEやブラウザとの連携も可能です。Vimとの後方互換性を保ちながら、現代的な開発環境に適応した機能を提供しています。ターミナルやSSH経由のリモートサーバーでの作業にも対応し、モーダル編集による効率的なワークフローを実現します。
メリット・デメリット
メリット
- 現代的なアーキテクチャ: Luaスクリプト、内蔵LSP、Tree-sitterサポート
- 活発な開発コミュニティ: Vimより広い貢献者ベースによる継続的な改善
- 高性能: 最適化された軽量設計により高速で効率的な動作
- 優れた拡張性: LuaとVimscriptの両方による柔軟なカスタマイズ
- ヘッドレス設計: 他のプログラムやIDEとの統合が容易
- Vim互換性: Vimで学んだ知識をそのまま活用可能
- リモート作業対応: ターミナルベースでSSH環境に最適
デメリット
- 急峻な学習曲線: モーダル編集の習得が困難
- プラグイン依存: 高度な機能には多くのプラグインが必要
- 混乱する文書: LuaとVimscriptの混在により情報が分散
- 設定の複雑さ: 最適な環境構築には深い知識が必要
- レガシー機能: Vimからの多くの古い機能を継承
- ターミナル制限: GUIでの機能が限定的
- 初期設定: 基本的な使用にも相当な設定が必要
主要リンク
書き方の例
init.lua基本設定
-- ~/.config/nvim/init.lua - Lua設定
-- 基本設定
vim.opt.number = true -- 行番号表示
vim.opt.relativenumber = true -- 相対行番号表示
vim.opt.mouse = 'a' -- マウス有効
vim.opt.ignorecase = true -- 大文字小文字を無視
vim.opt.smartcase = true -- 大文字が含まれる場合は区別
vim.opt.hlsearch = false -- 検索ハイライト無効
vim.opt.wrap = false -- 行の折り返し無効
vim.opt.breakindent = true -- インデント継続
vim.opt.tabstop = 4 -- タブ幅
vim.opt.shiftwidth = 4 -- シフト幅
vim.opt.expandtab = true -- タブをスペースに変換
-- 見た目の設定
vim.opt.termguicolors = true -- True colorサポート
vim.opt.signcolumn = 'yes' -- サインカラム常時表示
vim.opt.cmdheight = 1 -- コマンド行の高さ
vim.opt.scrolloff = 8 -- スクロールオフセット
vim.opt.sidescrolloff = 8 -- 横スクロールオフセット
-- ファイル設定
vim.opt.backup = false -- バックアップ無効
vim.opt.writebackup = false -- 書き込み時バックアップ無効
vim.opt.undofile = true -- アンドーファイル有効
vim.opt.swapfile = false -- スワップファイル無効
-- キーマップ設定
vim.g.mapleader = ' ' -- リーダーキーをスペースに設定
-- 基本的なキーマップ
local keymap = vim.keymap.set
keymap('n', '<leader>w', ':w<CR>', { desc = 'Save file' })
keymap('n', '<leader>q', ':q<CR>', { desc = 'Quit' })
keymap('n', '<leader>h', ':nohlsearch<CR>', { desc = 'Clear search highlight' })
keymap('n', '<C-h>', '<C-w>h', { desc = 'Move to left window' })
keymap('n', '<C-j>', '<C-w>j', { desc = 'Move to bottom window' })
keymap('n', '<C-k>', '<C-w>k', { desc = 'Move to top window' })
keymap('n', '<C-l>', '<C-w>l', { desc = 'Move to right window' })
-- ビジュアルモードでの移動
keymap('v', 'J', ":m '>+1<CR>gv=gv", { desc = 'Move selected lines down' })
keymap('v', 'K', ":m '<-2<CR>gv=gv", { desc = 'Move selected lines up' })
-- 検索時の中央表示
keymap('n', 'n', 'nzzzv')
keymap('n', 'N', 'Nzzzv')
-- システムクリップボード
keymap('n', '<leader>y', '"+y', { desc = 'Yank to system clipboard' })
keymap('v', '<leader>y', '"+y', { desc = 'Yank to system clipboard' })
keymap('n', '<leader>p', '"+p', { desc = 'Paste from system clipboard' })
プラグイン管理(lazy.nvim)
-- ~/.config/nvim/lua/plugins/init.lua
return {
-- カラースキーム
{
'folke/tokyonight.nvim',
lazy = false,
priority = 1000,
config = function()
vim.cmd([[colorscheme tokyonight]])
end,
},
-- ファイルエクスプローラー
{
'nvim-tree/nvim-tree.lua',
dependencies = { 'nvim-tree/nvim-web-devicons' },
config = function()
require('nvim-tree').setup{}
vim.keymap.set('n', '<leader>e', ':NvimTreeToggle<CR>', { desc = 'Toggle file explorer' })
end,
},
-- ファジーファインダー
{
'nvim-telescope/telescope.nvim',
dependencies = { 'nvim-lua/plenary.nvim' },
config = function()
local telescope = require('telescope')
telescope.setup{}
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, { desc = 'Find files' })
vim.keymap.set('n', '<leader>fg', builtin.live_grep, { desc = 'Live grep' })
vim.keymap.set('n', '<leader>fb', builtin.buffers, { desc = 'Find buffers' })
vim.keymap.set('n', '<leader>fh', builtin.help_tags, { desc = 'Help tags' })
end,
},
-- LSP設定
{
'neovim/nvim-lspconfig',
dependencies = {
'williamboman/mason.nvim',
'williamboman/mason-lspconfig.nvim',
},
config = function()
require('mason').setup()
require('mason-lspconfig').setup{
ensure_installed = { 'lua_ls', 'pyright', 'tsserver' }
}
local lspconfig = require('lspconfig')
-- Lua LSP
lspconfig.lua_ls.setup{
settings = {
Lua = {
diagnostics = {
globals = {'vim'}
}
}
}
}
-- Python LSP
lspconfig.pyright.setup{}
-- TypeScript LSP
lspconfig.tsserver.setup{}
-- LSPキーマップ
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = 'Go to definition' })
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { desc = 'Hover documentation' })
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, { desc = 'Go to implementation' })
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, { desc = 'Rename' })
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, { desc = 'Code action' })
end,
},
-- 補完エンジン
{
'hrsh7th/nvim-cmp',
dependencies = {
'hrsh7th/cmp-nvim-lsp',
'hrsh7th/cmp-buffer',
'hrsh7th/cmp-path',
'hrsh7th/cmp-cmdline',
'L3MON4D3/LuaSnip',
'saadparwaiz1/cmp_luasnip',
},
config = function()
local cmp = require('cmp')
local luasnip = require('luasnip')
cmp.setup{
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end, { 'i', 's' }),
}),
sources = cmp.config.sources({
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
}, {
{ name = 'buffer' },
})
}
end,
},
-- Tree-sitter
{
'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
config = function()
require('nvim-treesitter.configs').setup{
ensure_installed = {
'lua', 'python', 'javascript', 'typescript', 'html', 'css'
},
highlight = {
enable = true,
},
indent = {
enable = true,
},
}
end,
},
-- ステータスライン
{
'nvim-lualine/lualine.nvim',
dependencies = { 'nvim-tree/nvim-web-devicons' },
config = function()
require('lualine').setup{
options = {
theme = 'tokyonight'
}
}
end,
},
}
カスタム関数とコマンド
-- ~/.config/nvim/lua/utils.lua
local M = {}
-- 末尾の空白を削除する関数
function M.trim_whitespace()
local save_cursor = vim.fn.getpos('.')
vim.cmd([[%s/\s\+$//e]])
vim.fn.setpos('.', save_cursor)
end
-- 現在の行を複製する関数
function M.duplicate_line()
local line = vim.fn.getline('.')
vim.fn.append('.', line)
end
-- プロジェクトルートを取得する関数
function M.get_project_root()
local markers = {'.git', 'package.json', 'Cargo.toml', 'pyproject.toml'}
local current_file = vim.fn.expand('%:p:h')
for _, marker in ipairs(markers) do
local root = vim.fn.finddir(marker, current_file .. ';')
if root ~= '' then
return vim.fn.fnamemodify(root, ':h')
end
end
return current_file
end
-- ファイルを新しい名前で保存する関数
function M.save_as()
local current_file = vim.fn.expand('%:t')
local new_name = vim.fn.input('New filename: ', current_file)
if new_name ~= '' and new_name ~= current_file then
vim.cmd('saveas ' .. new_name)
end
end
-- 設定ファイルを素早く編集する関数
function M.edit_config()
vim.cmd('edit ~/.config/nvim/init.lua')
end
-- コマンドの登録
vim.api.nvim_create_user_command('TrimWhitespace', M.trim_whitespace, {})
vim.api.nvim_create_user_command('DuplicateLine', M.duplicate_line, {})
vim.api.nvim_create_user_command('SaveAs', M.save_as, {})
vim.api.nvim_create_user_command('EditConfig', M.edit_config, {})
-- キーマップの設定
vim.keymap.set('n', '<leader>tw', M.trim_whitespace, { desc = 'Trim whitespace' })
vim.keymap.set('n', '<leader>dl', M.duplicate_line, { desc = 'Duplicate line' })
vim.keymap.set('n', '<leader>sa', M.save_as, { desc = 'Save as' })
vim.keymap.set('n', '<leader>ec', M.edit_config, { desc = 'Edit config' })
return M
オートコマンドとイベント
-- ~/.config/nvim/lua/autocmds.lua
local augroup = vim.api.nvim_create_augroup
local autocmd = vim.api.nvim_create_autocmd
-- 一般的なオートコマンド
local general = augroup('General', { clear = true })
autocmd('TextYankPost', {
group = general,
pattern = '*',
callback = function()
vim.highlight.on_yank({ higroup = 'Visual', timeout = 200 })
end,
desc = 'Highlight yanked text',
})
autocmd('BufReadPost', {
group = general,
pattern = '*',
callback = function()
local mark = vim.api.nvim_buf_get_mark(0, '"')
local lcount = vim.api.nvim_buf_line_count(0)
if mark[1] > 0 and mark[1] <= lcount then
pcall(vim.api.nvim_win_set_cursor, 0, mark)
end
end,
desc = 'Restore cursor position',
})
-- ファイルタイプ別設定
local filetypes = augroup('FileTypes', { clear = true })
autocmd('FileType', {
group = filetypes,
pattern = { 'python' },
callback = function()
vim.opt_local.tabstop = 4
vim.opt_local.shiftwidth = 4
vim.opt_local.expandtab = true
end,
})
autocmd('FileType', {
group = filetypes,
pattern = { 'javascript', 'typescript', 'html', 'css', 'json' },
callback = function()
vim.opt_local.tabstop = 2
vim.opt_local.shiftwidth = 2
vim.opt_local.expandtab = true
end,
})
autocmd('FileType', {
group = filetypes,
pattern = { 'lua' },
callback = function()
vim.opt_local.tabstop = 2
vim.opt_local.shiftwidth = 2
vim.opt_local.expandtab = true
end,
})
-- 保存時の処理
local save_group = augroup('SaveGroup', { clear = true })
autocmd('BufWritePre', {
group = save_group,
pattern = { '*.py', '*.js', '*.ts', '*.lua', '*.html', '*.css' },
callback = function()
require('utils').trim_whitespace()
end,
desc = 'Trim whitespace on save',
})
-- LSP設定
local lsp_group = augroup('LspGroup', { clear = true })
autocmd('LspAttach', {
group = lsp_group,
callback = function(ev)
-- LSPアタッチ時のキーマップ設定
local opts = { buffer = ev.buf }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
vim.keymap.set('n', '<leader>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', '<leader>f', function()
vim.lsp.buf.format { async = true }
end, opts)
end,
})