31 KiB
Neovim IDE Setup Guide
A comprehensive guide to setting up Neovim as a full-featured IDE for Python, Shell scripts, and more using Lua configuration.
Table of Contents
- Prerequisites
- Directory Structure
- Configuration Files
- Installing Language Servers
- Keybindings Reference
- First Launch
Prerequisites
Install Neovim (v0.9+)
Ubuntu/Debian:
sudo add-apt-repository ppa:neovim-ppa/unstable
sudo apt update
sudo apt install neovim
Fedora:
sudo dnf install neovim
Arch Linux:
sudo pacman -S neovim
macOS:
brew install neovim
Install Required Tools
# Node.js (for LSP servers via Mason)
curl -fsSL https://fnm.vercel.app/install | bash
source ~/.bashrc
fnm install --latest
# Python (for Python LSP and debugging)
pip install python-lsp-server debugpy
# ripgrep (for telescope live grep)
sudo apt install ripgrep # Ubuntu
sudo dnf install ripgrep # Fedora
sudo pacman -S ripgrep # Arch
brew install ripgrep # macOS
# git (for plugin installation)
sudo apt install git
Directory Structure
Create the following directory structure:
mkdir -p ~/.config/nvim/lua/plugins
Your final structure should look like:
~/.config/nvim/
├── init.lua
└── lua/
└── plugins/
├── init.lua
├── lsp.lua
├── dap.lua
├── treesitter.lua
├── telescope.lua
├── nvimtree.lua
├── cmp.lua
├── toggleterm.lua
└── formatter.lua
Configuration Files
~/.config/nvim/init.lua
Main entry point for Neovim configuration.
-- Leader key
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- General settings
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true
vim.opt.smartindent = true
vim.opt.wrap = false
vim.opt.scrolloff = 8
vim.opt.signcolumn = "yes"
vim.opt.termguicolors = true
vim.opt.cursorline = true
-- Enable clipboard
vim.opt.clipboard = "unnamedplus"
-- Search settings
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.hlsearch = true
-- Split settings
vim.opt.splitright = true
vim.opt.splitbelow = true
-- Performance
vim.opt.updatetime = 50
vim.opt.timeoutlen = 300
-- Set colorscheme
vim.cmd.colorscheme "tokyonight"
-- Load plugins
require("plugins")
-- Keybindings
local keymap = vim.keymap.set
-- Better window navigation
keymap("n", "<C-h>", "<C-w>h", { desc = "Navigate left" })
keymap("n", "<C-j>", "<C-w>j", { desc = "Navigate down" })
keymap("n", "<C-k>", "<C-w>k", { desc = "Navigate up" })
keymap("n", "<C-l>", "<C-w>l", { desc = "Navigate right" })
-- Resize windows
keymap("n", "<C-Up>", ":resize -2<CR>", { desc = "Resize window up" })
keymap("n", "<C-Down>", ":resize +2<CR>", { desc = "Resize window down" })
keymap("n", "<C-Left>", ":vertical resize -2<CR>", { desc = "Resize window left" })
keymap("n", "<C-Right>", ":vertical resize +2<CR>", { desc = "Resize window right" })
-- Move text up/down
keymap("n", "<A-j>", ":m .+1<CR>==", { desc = "Move line down" })
keymap("n", "<A-k>", ":m .-2<CR>==", { desc = "Move line up" })
keymap("v", "<A-j>", ":m '>+1<CR>gv=gv", { desc = "Move selection down" })
keymap("v", "<A-k>", ":m '<-2<CR>gv=gv", { desc = "Move selection up" })
-- Stay in indent mode
keymap("v", "<", "<gv", { desc = "Indent left" })
keymap("v", ">", ">gv", { desc = "Indent right" })
-- Better paste
keymap("v", "p", '"_dP', { desc = "Paste without yanking" })
-- Clear highlights
keymap("n", "<leader>h", ":nohlsearch<CR>", { desc = "Clear search highlights" })
-- Save and quit
keymap("n", "<leader>w", ":w<CR>", { desc = "Save file" })
keymap("n", "<leader>q", ":q<CR>", { desc = "Quit" })
keymap("n", "<leader>Q", ":qa!<CR>", { desc = "Force quit all" })
-- Toggle options
keymap("n", "<leader>tn", ":set nu!<CR>", { desc = "Toggle line numbers" })
keymap("n", "<leader>tr", ":set rnu!<CR>", { desc = "Toggle relative numbers" })
~/.config/nvim/lua/plugins/init.lua
Plugin manager setup using lazy.nvim.
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Plugin specifications
require("lazy").setup({
-- Theme
{ "folke/tokyonight.nvim", lazy = false },
-- Plugin manager
{ "folke/lazy.nvim", version = "*" },
-- LSP
{ "neovim/nvim-lspconfig" },
{ "williamboman/mason.nvim" },
{ "williamboman/mason-lspconfig.nvim" },
-- Autocomplete
{ "hrsh7th/nvim-cmp" },
{ "hrsh7th/cmp-nvim-lsp" },
{ "hrsh7th/cmp-buffer" },
{ "hrsh7th/cmp-path" },
{ "hrsh7th/cmp-cmdline" },
{ "L3MON4D3/LuaSnip" },
{ "saadparwaiz1/cmp_luasnip" },
-- Treesitter (syntax)
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" },
{ "nvim-treesitter/nvim-treesitter-textobjects" },
-- Fuzzy finder
{ "nvim-telescope/telescope.nvim", dependencies = { "nvim-lua/plenary.nvim" } },
-- File tree
{ "nvim-tree/nvim-tree.lua" },
{ "nvim-tree/nvim-web-devicons" },
-- Terminal
{ "akinsho/toggleterm.nvim" },
-- Debugging
{ "mfussenegger/nvim-dap" },
{ "rcarriga/nvim-dap-ui" },
{ "theHamsta/nvim-dap-virtual-text" },
-- Formatters/Linters
{ "nvimtools/none-ls.nvim" },
{ "nvim-lua/plenary.nvim" },
-- Utility
{ "windwp/nvim-autopairs" },
{ "numToStr/Comment.nvim" },
})
~/.config/nvim/lua/plugins/lsp.lua
LSP configuration for Python and Shell scripts.
-- Setup Mason (LSP installer)
require("mason").setup({
ui = {
border = "rounded",
icons = {
package_installed = "✓",
package_pending = "➜",
package_uninstalled = "✗",
},
},
log_level = vim.log.levels.INFO,
max_concurrent_installers = 4,
})
-- LSP config
local lspconfig = require("lspconfig")
local cmp_nvim_lsp = require("cmp_nvim_lsp")
-- Capabilities for autocomplete
local capabilities = cmp_nvim_lsp.default_capabilities()
-- Diagnostic settings
vim.diagnostic.config({
virtual_text = true,
signs = true,
underline = true,
update_in_insert = false,
severity_sort = true,
float = {
border = "rounded",
source = true,
},
})
-- Signs for diagnostics
local signs = { Error = " ", Warn = " ", Hint = " ", Info = " " }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" })
end
-- LSP handlers
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
border = "rounded",
})
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, {
border = "rounded",
})
-- LSP Keybindings
local function lsp_keymaps(bufnr)
local opts = { buffer = bufnr, silent = true }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gD", vim.lsp.buf.declaration, opts)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, 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", "<leader>e", vim.diagnostic.open_float, opts)
vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
end
-- Python LSP
lspconfig.pyright.setup({
capabilities = capabilities,
on_attach = function(client, bufnr)
lsp_keymaps(bufnr)
-- Disable virtual text for python if needed
-- client.server_capabilities.semanticTokensProvider = nil
end,
settings = {
python = {
analysis = {
typeCheckingMode = "basic",
autoSearchPaths = true,
useLibraryCodeForTypes = true,
},
},
},
})
-- Bash LSP
lspconfig.bashls.setup({
capabilities = capabilities,
on_attach = function(client, bufnr)
lsp_keymaps(bufnr)
end,
})
-- HTML, CSS, JSON (optional)
lspconfig.html.setup({
capabilities = capabilities,
on_attach = lsp_keymaps,
})
lspconfig.jsonls.setup({
capabilities = capabilities,
on_attach = lsp_keymaps,
})
-- Lua LSP (for neovim config editing)
lspconfig.lua_ls.setup({
capabilities = capabilities,
on_attach = function(client, bufnr)
lsp_keymaps(bufnr)
end,
settings = {
Lua = {
runtime = {
version = "LuaJIT",
},
diagnostics = {
globals = { "vim" },
},
workspace = {
library = vim.api.nvim_get_runtime_file("", true),
checkThirdParty = false,
},
telemetry = {
enable = false,
},
},
},
})
-- Setup Mason LSP config
require("mason-lspconfig").setup({
ensure_installed = {
"pyright",
"bashls",
"html",
"jsonls",
"lua_ls",
},
handlers = {
function(server_name)
require("lspconfig")[server_name].setup({
capabilities = capabilities,
})
end,
},
})
~/.config/nvim/lua/plugins/dap.lua
Debug Adapter Protocol configuration for debugging Python.
local dap = require("dap")
local dapui = require("dapui")
-- Setup DAP UI
dapui.setup({
icons = { expanded = "▾", collapsed = "▸", current_frame = "▸" },
mappings = {
expand = { "<CR>", "<2-LeftMouse>" },
open = "o",
remove = "d",
edit = "e",
repl = "r",
toggle = "t",
},
expand_lines = vim.fn.has("nvim-0.7") == 1,
layouts = {
{
elements = {
{ id = "scopes", size = 0.25 },
{ id = "breakpoints", size = 0.25 },
{ id = "stacks", size = 0.25 },
{ id = "watches", size = 0.25 },
},
size = 0.25,
position = "right",
},
{
elements = {
{ id = "repl", size = 0.5 },
{ id = "console", size = 0.5 },
},
position = "bottom",
size = 0.25,
},
},
floating = {
max_height = nil,
max_width = nil,
border = "rounded",
mappings = {
close = { "q", "<Esc>" },
},
},
windows = { indent = 1 },
})
-- Open DAP UI automatically
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
dap.listeners.before.event_terminated["dapui_config"] = function()
dapui.close()
end
dap.listeners.before.event_exited["dapui_config"] = function()
dapui.close()
end
-- Python debug adapter configuration
dap.configurations.python = {
{
type = "python",
request = "launch",
name = "Launch file",
program = "${file}",
pythonPath = function()
return vim.fn.exepath("python3") or "python"
end,
},
{
type = "python",
request = "launch",
name = "Debug current file (integrated terminal)",
program = "${file}",
console = "integratedTerminal",
pythonPath = function()
return vim.fn.exepath("python3") or "python"
end,
},
{
type = "python",
request = "launch",
name = "Debug with arguments",
program = "${file}",
args = function()
local args = vim.fn.input("Args: ", ""):split(" ")
return args
end,
console = "integratedTerminal",
pythonPath = function()
return vim.fn.exepath("python3") or "python"
end,
},
}
-- DAP keybindings
vim.keymap.set("n", "<leader>db", dap.toggle_breakpoint, { desc = "Toggle breakpoint" })
vim.keymap.set("n", "<leader>dc", dap.continue, { desc = "Continue" })
vim.keymap.set("n", "<leader>ds", dap.step_over, { desc = "Step over" })
vim.keymap.set("n", "<leader>di", dap.step_into, { desc = "Step into" })
vim.keymap.set("n", "<leader>do", dap.step_out, { desc = "Step out" })
vim.keymap.set("n", "<leader>dr", dap.repl.open, { desc = "Open REPL" })
vim.keymap.set("n", "<leader>dl", dap.run_last, { desc = "Run last debug config" })
vim.keymap.set("n", "<leader>du", dapui.toggle, { desc = "Toggle DAP UI" })
~/.config/nvim/lua/plugins/treesitter.lua
Treesitter configuration for syntax highlighting.
require("nvim-treesitter.configs").setup({
ensure_installed = {
"python",
"bash",
"lua",
"html",
"css",
"javascript",
"typescript",
"json",
"yaml",
"markdown",
"markdown_inline",
"go",
"rust",
"c",
"cpp",
},
sync_install = false,
auto_install = true,
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
indent = {
enable = true,
},
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
},
},
move = {
enable = true,
set_jumps = true,
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
},
goto_next_end = {
["]F"] = "@function.outer",
["]C"] = "@class.outer",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
},
goto_previous_end = {
["[F"] = "@function.outer",
["[C"] = "@class.outer",
},
},
swap = {
enable = true,
swap_next = {
["<leader>sp"] = "@parameter.inner",
},
swap_previous = {
["<leader>sP"] = "@parameter.inner",
},
},
},
})
~/.config/nvim/lua/plugins/telescope.lua
Telescope fuzzy finder configuration.
local telescope = require("telescope")
local actions = require("telescope.actions")
telescope.setup({
defaults = {
prompt_prefix = " ",
selection_caret = " ",
path_display = { "truncate" },
sorting_strategy = "ascending",
layout_strategy = "horizontal",
layout_config = {
horizontal = {
prompt_position = "top",
preview_width = 0.55,
results_width = 0.8,
},
vertical = {
mirror = false,
},
width = 0.87,
height = 0.80,
preview_cutoff = 120,
},
mappings = {
i = {
["<C-n>"] = actions.cycle_history_next,
["<C-p>"] = actions.cycle_history_prev,
["<C-j>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<C-c>"] = actions.close,
["<Down>"] = actions.move_selection_next,
["<Up>"] = actions.move_selection_previous,
["<CR>"] = actions.select_default,
["<C-x>"] = actions.select_horizontal,
["<C-v>"] = actions.select_vertical,
["<C-t>"] = actions.select_tab,
["<C-u>"] = actions.preview_scrolling_up,
["<C-d>"] = actions.preview_scrolling_down,
},
n = {
["<esc>"] = actions.close,
["<CR>"] = actions.select_default,
["<C-x>"] = actions.select_horizontal,
["<C-v>"] = actions.select_vertical,
["<C-t>"] = actions.select_tab,
["j"] = actions.move_selection_next,
["k"] = actions.move_selection_previous,
["H"] = actions.move_to_top,
["M"] = actions.move_to_middle,
["L"] = actions.move_to_bottom,
["gg"] = actions.move_to_top,
["G"] = actions.move_to_bottom,
["<C-u>"] = actions.preview_scrolling_up,
["<C-d>"] = actions.preview_scrolling_down,
},
},
},
pickers = {
find_files = {
theme = "dropdown",
previewer = false,
},
live_grep = {
theme = "ivy",
},
buffers = {
theme = "dropdown",
previewer = false,
},
},
})
-- Load telescope extensions
pcall(telescope.load_extension, "fzf")
-- Telescope keybindings
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" })
vim.keymap.set("n", "<leader>fr", builtin.oldfiles, { desc = "Recent files" })
vim.keymap.set("n", "<leader>fc", builtin.grep_string, { desc = "Grep string under cursor" })
vim.keymap.set("n", "<leader>fd", builtin.diagnostics, { desc = "Find diagnostics" })
~/.config/nvim/lua/plugins/nvimtree.lua
File tree configuration.
require("nvim-tree").setup({
on_attach = function(bufnr)
local api = require("nvim-tree.api")
local function opts(desc)
return { desc = "nvim-tree: " .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
end
vim.keymap.set("n", "<CR>", api.node.open.edit, opts("Open"))
vim.keymap.set("n", "<Tab>", api.node.open.preview, opts("Open Preview"))
vim.keymap.set("n", ">", api.node.navigate.sibling.next, opts("Next Sibling"))
vim.keymap.set("n", "<", api.node.navigate.sibling.prev, opts("Previous Sibling"))
vim.keymap.set("n", ".", api.node.run.cmd, opts("Run Command"))
vim.keymap.set("n", "-", api.tree.change_root_to_parent, opts("Up"))
vim.keymap.set("n", "a", api.fs.create, opts("Create File"))
vim.keymap.set("n", "bd", api.node.mbx.trash, opts("Trash"))
vim.keymap.set("n", "bn", api.fs.rename, opts("Rename"))
vim.keymap.set("n", "bmv", api.fs.rename_basename, opts("Rename: Basename"))
vim.keymap.set("n", "bt", api.fs.copy.node_to_clipboard, opts("Copy To Clipboard"))
vim.keymap.set("n", "bp", api.fs.paste_from_clipboard, opts("Paste From Clipboard"))
vim.keymap.set("n", "q", api.tree.close, opts("Close"))
vim.keymap.set("n", "R", api.tree.reload, opts("Refresh"))
vim.keymap.set("n", "f", api.live_filter.start, opts("Filter"))
vim.keymap.set("n", "F", api.live_filter.clear, opts("Clean Filter"))
vim.keymap.set("n", "g?", api.tree.toggle_help, opts("Help"))
vim.keymap.set("n", "W", api.tree.collapse_all, opts("Collapse"))
vim.keymap.set("n", "E", api.tree.expand_all, opts("Expand All"))
vim.keymap.set("n", "S", api.node.search.manual, opts("Search"))
vim.keymap.set("n", "U", api.tree.toggle_custom_filter, opts("Toggle Filter"))
vim.keymap.set("n", "]", api.node.navigate.diagnostics.next, opts("Next Diagnostic"))
vim.keymap.set("n", "[", api.node.navigate.diagnostics.prev, opts("Prev Diagnostic"))
end,
sort_by = "case_sensitive",
view = {
width = 30,
hide_root_folder = false,
side = "left",
number = false,
relativenumber = false,
signcolumn = "yes",
},
renderer = {
group_empty = true,
indent_width = 1,
indent_markers = {
enable = true,
inline_arrows = true,
icons = {
corner = "└",
edge = "│",
item = "│",
bottom = "─",
none = " ",
},
},
icons = {
webdev_colors = true,
git_placement = "after",
padding = " ",
symlink_arrow = " ➛ ",
show = {
file = true,
folder = true,
folder_arrow = true,
git = true,
},
glyphs = {
default = " ",
symlink = "S",
bookmark = "B",
folder = {
arrow_closed = "▸",
arrow_open = "▾",
default = "D",
open = "D",
empty = "E",
empty_open = "E",
symlink = "S",
symlink_open = "S",
},
git = {
unstaged = "✗",
staged = "✓",
unmerged = "✦",
renamed = "➜",
untracked = "★",
ignored = "◌",
},
},
},
},
filters = {
dotfiles = false,
git_clean = false,
no_buffer = false,
custom = { "node_modules", ".cache", "__pycache__" },
exclude = {},
},
actions = {
use_system_clipboard = true,
change_dir = {
enable = true,
global = false,
restrict_above_cwd = false,
},
file_popup = {
open_win_config = {
col = 1,
row = 1,
relative = "cursor",
border = "shadow",
style = "minimal",
},
},
open_file = {
quit_on_open = false,
resize_window = true,
window_picker = {
enable = true,
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
exclude = {
filetype = { "notify", "packer", "qf", "diff", "fugitive", "Outline" },
buftype = { "terminal", "help" },
},
},
},
remove_file = {
close_buffer = true,
},
},
trash = {
cmd = "trash",
},
})
-- Nvim-tree keybindings
vim.keymap.set("n", "<leader>e", ":NvimTreeToggle<CR>", { desc = "Toggle file tree" })
vim.keymap.set("n", "<leader>o", ":NvimTreeFocus<CR>", { desc = "Focus file tree" })
~/.config/nvim/lua/plugins/cmp.lua
Autocomplete configuration.
local cmp = require("cmp")
local luasnip = require("luasnip")
require("luasnip.loaders.from_vscode").lazy_load()
local has_words_before = function()
local line, col = unpack(vim.api.nvim_win_get_cursor(0))
return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end
cmp.setup({
snippet = {
expand = function(args)
luasnip.lsp_expand(args.body)
end,
},
mapping = cmp.mapping.preset.insert({
["<C-b>"] = 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()
elseif has_words_before() then
cmp.complete()
else
fallback()
end
end, { "i", "s" }),
["<S-Tab>"] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end, { "i", "s" }),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
{ name = "luasnip" },
{ name = "path" },
}, {
{ name = "buffer", keyword_length = 3 },
}),
formatting = {
fields = { "kind", "abbr", "menu" },
format = function(entry, vim_item)
local kind_icons = {
Text = "",
Method = "",
Function = "",
Constructor = "",
Field = "",
Variable = "",
Class = "",
Interface = "",
Module = "",
Property = "",
Unit = "",
Value = "",
Enum = "",
Keyword = "",
Snippet = "",
Color = "",
File = "",
Reference = "",
Folder = "",
EnumMember = "",
Constant = "",
Struct = "",
Event = "",
Operator = "",
TypeParameter = "",
}
vim_item.kind = string.format("%s %s", kind_icons[vim_item.kind], vim_item.kind)
vim_item.menu = ({
nvim_lsp = "[LSP]",
luasnip = "[Snip]",
buffer = "[Buf]",
path = "[Path]",
})[entry.source.name]
return vim_item
end,
},
window = {
completion = cmp.config.window.bordered(),
documentation = cmp.config.window.bordered(),
},
})
-- Command line completion
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = cmp.config.sources({
{ name = "path" },
}, {
{ name = "cmdline" },
}),
})
cmp.setup.cmdline("/", {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "buffer" },
},
})
~/.config/nvim/lua/plugins/toggleterm.lua
Integrated terminal configuration.
require("toggleterm").setup({
size = function(term)
if term.direction == "horizontal" then
return 15
elseif term.direction == "vertical" then
return vim.o.columns * 0.4
end
end,
open_mapping = [[<c-\>]],
hide_numbers = true,
shade_filetypes = {},
shade_terminals = true,
shading_factor = 2,
start_in_insert = true,
insert_mappings = true,
terminal_mappings = true,
persist_size = true,
direction = "float",
close_on_exit = true,
shell = vim.o.shell,
float_opts = {
border = "curved",
winblend = 0,
highlights = {
border = "Normal",
background = "Normal",
},
},
})
-- Terminal keybindings
local Terminal = require("toggleterm.terminal").Terminal
local lazygit = Terminal:new({
cmd = "lazygit",
hidden = true,
direction = "float",
float_opts = {
border = "curved",
},
})
function _lazygit_toggle()
lazygit:toggle()
end
vim.keymap.set("n", "<leader>gg", "<cmd>lua _lazygit_toggle()<CR>", { desc = "Toggle lazygit" })
vim.keymap.set("n", "<leader>tf", "<cmd>ToggleTerm direction=float<CR>", { desc = "Toggle float terminal" })
vim.keymap.set("n", "<leader>th", "<cmd>ToggleTerm direction=horizontal<CR>", { desc = "Toggle horizontal terminal" })
vim.keymap.set("n", "<leader>tv", "<cmd>ToggleTerm direction=vertical<CR>", { desc = "Toggle vertical terminal" })
~/.config/nvim/lua/plugins/formatter.lua
Formatter and linter configuration using none-ls.
local null_ls = require("null-ls")
local formatting = null_ls.builtins.formatting
local diagnostics = null_ls.builtins.diagnostics
null_ls.setup({
debug = false,
sources = {
-- Python
formatting.black,
formatting.isort,
diagnostics.pylint,
diagnostics.mypy,
-- Shell
formatting.shfmt,
diagnostics.shellcheck,
-- Lua
formatting.stylua,
-- JavaScript/TypeScript
formatting.prettier,
formatting.eslint_d,
-- JSON/YAML
formatting.jsonlint,
formatting.yamllint,
},
on_attach = function(client, bufnr)
if client.supports_method("textDocument/formatting") then
vim.keymap.set("n", "<leader>f", function()
vim.lsp.buf.format({ async = false })
end, { buffer = bufnr, desc = "Format buffer" })
end
end,
})
-- Formatter keybindings
vim.keymap.set("n", "<leader>f", vim.lsp.buf.format, { desc = "Format buffer" })
Installing Language Servers
After launching Neovim for the first time with this configuration:
- Open Neovim:
nvim - Wait for lazy.nvim to install all plugins (this may take a few minutes)
- After plugins are installed, install language servers:
- Press
<leader>msto open Mason (or run:Mason) - Install the following:
pyright(Python)bash-language-server(Shell/Bash)lua-language-server(Lua)html-lsp(HTML)json-lsp(JSON)prettier(Formatting)shellcheck(Shell linting)shfmt(Shell formatting)black(Python formatting)mypy(Python type checking)
- Press
Keybindings Reference
General
| Keybinding | Action |
|---|---|
<Space> |
Leader key |
<leader>h |
Clear search highlights |
<leader>w |
Save file |
<leader>q |
Quit |
<leader>tn |
Toggle line numbers |
<leader>tr |
Toggle relative numbers |
Window Navigation
| Keybinding | Action |
|---|---|
<C-h> |
Navigate left |
<C-j> |
Navigate down |
<C-k> |
Navigate up |
<C-l> |
Navigate right |
<C-Up> |
Resize window up |
<C-Down> |
Resize window down |
<C-Left> |
Resize window left |
<C-Right> |
Resize window right |
Telescope (Fuzzy Finder)
| Keybinding | Action |
|---|---|
<leader>ff |
Find files |
<leader>fg |
Live grep |
<leader>fb |
Find buffers |
<leader>fh |
Help tags |
<leader>fr |
Recent files |
<leader>fc |
Grep string under cursor |
<leader>fd |
Find diagnostics |
NvimTree (File Tree)
| Keybinding | Action |
|---|---|
<leader>e |
Toggle file tree |
<leader>o |
Focus file tree |
LSP
| Keybinding | Action |
|---|---|
gd |
Go to definition |
gD |
Go to declaration |
gi |
Go to implementation |
gr |
Go to references |
K |
Hover |
<leader>rn |
Rename |
<leader>ca |
Code actions |
<leader>e |
Show diagnostics |
[d |
Previous diagnostic |
]d |
Next diagnostic |
Debugging (DAP)
| Keybinding | Action |
|---|---|
<leader>db |
Toggle breakpoint |
<leader>dc |
Continue |
<leader>ds |
Step over |
<leader>di |
Step into |
<leader>do |
Step out |
<leader>dr |
Open REPL |
<leader>dl |
Run last debug config |
<leader>du |
Toggle DAP UI |
Terminal
| Keybinding | Action |
|---|---|
<C-\> |
Toggle terminal |
<leader>tf |
Toggle float terminal |
<leader>th |
Toggle horizontal terminal |
<leader>tv |
Toggle vertical terminal |
<leader>gg |
Toggle lazygit |
Treesitter (Textobjects)
| Keybinding | Action |
|---|---|
af |
Select function (outer) |
if |
Select function (inner) |
ac |
Select class (outer) |
ic |
Select class (inner) |
aa |
Select parameter (outer) |
ia |
Select parameter (inner) |
First Launch
-
Launch Neovim:
nvim -
Wait for plugin installation:
- Lazy.nvim will automatically install all plugins
- You'll see progress in the statusline
-
Install language servers:
- Run
:Masonor press<leader>ms - Install the servers listed above
- Run
-
Verify installation:
- For Python: Open a
.pyfile and run:LspInfo - Run
:checkhealthto see overall status
- For Python: Open a
-
Optional: Install additional tools:
# For Python debugging pip install debugpy # For shell linting pip install shellcheck-py # For lazygit (optional but recommended) brew install lazygit # macOS sudo apt install lazygit # Ubuntu
Troubleshooting
LSP not starting
- Run
:LspInfoto check LSP status - Make sure language servers are installed via Mason
Treesitter highlighting not working
- Run
:TSUpdateto update/install parsers - Check
:TSInstallInfofor available parsers
Telescope not working
- Ensure
ripgrepis installed - Check telescope log with
:Telescope log
DAP not connecting
- Ensure
debugpyis installed:pip install debugpy - Check
: DapLogfor debug logs
Performance issues
- Check startup time with
:StartupTime - Disable unused plugins in
lua/plugins/init.lua
This guide provides a complete Neovim IDE setup. Modify the configuration files to suit your preferences!