1248 lines
31 KiB
Markdown
1248 lines
31 KiB
Markdown
# 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
|
|
|
|
1. [Prerequisites](#prerequisites)
|
|
2. [Directory Structure](#directory-structure)
|
|
3. [Configuration Files](#configuration-files)
|
|
- [init.lua](#initlua)
|
|
- [lua/plugins/init.lua](#luapluginsinitlua)
|
|
- [lua/plugins/lsp.lua](#luapluginslsplua)
|
|
- [lua/plugins/dap.lua](#luapluginsdaplua)
|
|
- [lua/plugins/treesitter.lua](#luapluginstreesitterlua)
|
|
- [lua/plugins/telescope.lua](#luapluginstelescopelua)
|
|
- [lua/plugins/nvimtree.lua](#luapluginsnvimtreelua)
|
|
- [lua/plugins/cmp.lua](#luapluginscmplua)
|
|
- [lua/plugins/toggleterm.lua](#luapluginstoggletermlua)
|
|
- [lua/plugins/formatter.lua](#luapluginsformatterlua)
|
|
4. [Installing Language Servers](#installing-language-servers)
|
|
5. [Keybindings Reference](#keybindings-reference)
|
|
6. [First Launch](#first-launch)
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
### Install Neovim (v0.9+)
|
|
|
|
**Ubuntu/Debian:**
|
|
```bash
|
|
sudo add-apt-repository ppa:neovim-ppa/unstable
|
|
sudo apt update
|
|
sudo apt install neovim
|
|
```
|
|
|
|
**Fedora:**
|
|
```bash
|
|
sudo dnf install neovim
|
|
```
|
|
|
|
**Arch Linux:**
|
|
```bash
|
|
sudo pacman -S neovim
|
|
```
|
|
|
|
**macOS:**
|
|
```bash
|
|
brew install neovim
|
|
```
|
|
|
|
### Install Required Tools
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
```lua
|
|
-- 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.
|
|
|
|
```lua
|
|
-- 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.
|
|
|
|
```lua
|
|
-- 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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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.
|
|
|
|
```lua
|
|
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:
|
|
|
|
1. Open Neovim: `nvim`
|
|
2. Wait for lazy.nvim to install all plugins (this may take a few minutes)
|
|
3. After plugins are installed, install language servers:
|
|
- Press `<leader>ms` to 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)
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
1. **Launch Neovim:**
|
|
```bash
|
|
nvim
|
|
```
|
|
|
|
2. **Wait for plugin installation:**
|
|
- Lazy.nvim will automatically install all plugins
|
|
- You'll see progress in the statusline
|
|
|
|
3. **Install language servers:**
|
|
- Run `:Mason` or press `<leader>ms`
|
|
- Install the servers listed above
|
|
|
|
4. **Verify installation:**
|
|
- For Python: Open a `.py` file and run `:LspInfo`
|
|
- Run `:checkhealth` to see overall status
|
|
|
|
5. **Optional: Install additional tools:**
|
|
```bash
|
|
# 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 `:LspInfo` to check LSP status
|
|
- Make sure language servers are installed via Mason
|
|
|
|
### Treesitter highlighting not working
|
|
- Run `:TSUpdate` to update/install parsers
|
|
- Check `:TSInstallInfo` for available parsers
|
|
|
|
### Telescope not working
|
|
- Ensure `ripgrep` is installed
|
|
- Check telescope log with `:Telescope log`
|
|
|
|
### DAP not connecting
|
|
- Ensure `debugpy` is installed: `pip install debugpy`
|
|
- Check `: DapLog` for 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!
|