Nvim Config

A complete walkthrough of a curated NvChad-based Neovim config with LSP, DAP, project scaffolding, and one-key code running.

Brief Overview

Modern IDEs like VS Code pack powerful features — file trees, tabs, terminals, fuzzy finders — but come with heavy resource usage. Neovim offers an alternative: blisteringly fast, light, and fully customizable.

This guide walks through a production-ready config built on NvChad v2.5 with lazy.nvim. By the end, you'll have a terminal IDE with a sidebar file explorer, buffer tabs, LSP auto-completion, debugging, project scaffolding, one-key code running, and a full Git integration stack.


File Structure

~/.config/nvim/
├── init.lua              # Bootstraps lazy, loads NvChad + plugins
├── lazy-lock.json        # Plugin lockfile
└── lua/
    ├── chadrc.lua        # NvChad UI/theme overrides
    ├── options.lua       # Editor options
    ├── autocmds.lua      # Autocommands
    ├── mappings.lua      # Custom keymaps + run code
    ├── configs/
    │   ├── lazy.lua      # lazy.nvim performance config
    │   ├── lspconfig.lua # LSP server setup
    │   ├── conform.lua   # Formatter config
    │   ├── dap.lua       # Debug adapter configs
    │   ├── dapui.lua     # DAP UI layout
    │   └── project_init.lua  # Project scaffolding
    └── plugins/
        └── init.lua      # Custom plugin definitions

Core Files

init.lua — Entry Point

vim.g.base46_cache = vim.fn.stdpath "data" .. "/base46/"
vim.g.mapleader = " "
 
local lazypath = vim.fn.stdpath "data" .. "/lazy/lazy.nvim"
 
if not vim.uv.fs_stat(lazypath) then
  local repo = "https://github.com/folke/lazy.nvim.git"
  vim.fn.system { "git", "clone", "--filter=blob:none", repo, "--branch=stable", lazypath }
end
 
vim.opt.rtp:prepend(lazypath)
 
local lazy_config = require "configs.lazy"
 
require("lazy").setup({
  {
    "NvChad/NvChad",
    lazy = false,
    branch = "v2.5",
    import = "nvchad.plugins",
  },
  { import = "plugins" },
}, lazy_config)
 
dofile(vim.g.base46_cache .. "defaults")
dofile(vim.g.base46_cache .. "statusline")
 
require "options"
require "autocmds"
 
vim.schedule(function()
  require "mappings"
end)

lua/chadrc.lua — Theme & UI

---@type ChadrcConfig
local M = {}
 
M.base46 = {
  theme = "chadracula",
  transparency = true,
}
 
M.nvdash = { load_on_startup = false }
M.ui = {
  tabufline = { lazyload = false },
}
 
return M

Theme: Chadracula with transparency. NvChad ships with 30+ themes — cycle with <leader>th.

lua/options.lua — Editor Options

require "nvchad.options"
 
local o = vim.o
local wo = vim.wo
 
o.number = true
o.relativenumber = true
o.tabstop = 2
o.shiftwidth = 2
o.expandtab = true
o.mouse = "a"
o.splitright = true
o.splitbelow = true
o.termguicolors = true
o.cursorline = true
o.scrolloff = 8
o.sidescrolloff = 8
o.updatetime = 300
o.timeoutlen = 300
o.clipboard = "unnamedplus"

Key settings:

OptionValueWhy
relativenumbertrueEasy 5j / 3k navigation
tabstop22-space indent for most languages
clipboardunnamedplusSystem clipboard integration
scrolloff8Keep cursor vertically centered
splitrighttrueSplits open to the right
timeoutlen300Faster keymap response

lua/autocmds.lua

require "nvchad.autocmds"
 
local augroup = vim.api.nvim_create_augroup("UserAutocmds", { clear = true })
 
vim.api.nvim_create_autocmd("FileType", {
  group = augroup,
  pattern = "qf",
  callback = function()
    vim.opt_local.buflisted = false
  end,
})

Quickfix windows are hidden from the buffer list to keep things clean.


Mappings

lua/mappings.lua

require "nvchad.mappings"
 
local map = vim.keymap.set
 
-- Run current file in a terminal
local function run_code()
  local ft = vim.bo.filetype
  local cmd = ({
    c = { "gcc", "%", "-o", "%:p:r", "&&", "%:p:r" },
    cpp = { "g++", "%", "-o", "%:p:r", "&&", "%:p:r" },
    go = { "go", "run", "%" },
    rust = { "cargo", "run" },
    java = { "java", "%:r" },
    python = { "python3", "%" },
    javascript = { "node", "%" },
    typescript = { "npx", "tsx", "%" },
    php = { "php", "%" },
    sh = { "bash", "%" },
    lua = { "lua", "%" },
  })[ft]
 
  if not cmd then
    vim.notify("No runner for filetype: " .. ft, vim.log.levels.WARN)
    return
  end
 
  local term = require "toggleterm.terminal"
  local Terminal = term.Terminal
  local terminal = Terminal:new {
    direction = "horizontal",
    size = 15,
    close_on_exit = true,
  }
  terminal:toggle()
  vim.schedule(function()
    terminal:send(cmd, true)
  end)
end
 
map("n", "<leader>r", run_code, { desc = "Run current file" })
map("n", "<leader>ni", function()
  require("configs.project_init").init_project()
end, { desc = "Init new project" })
map("n", ";", ":", { desc = "CMD enter command mode" })
map("i", "jk", "<ESC>")
 
local builtin = require "telescope.builtin"
map("n", "<C-p>", builtin.find_files, { desc = "Telescope find files" })
map("n", "<C-f>", builtin.live_grep, { desc = "Telescope live grep" })
map("n", "<leader>fb", builtin.buffers, { desc = "Telescope buffers" })
map("n", "<leader>fh", builtin.help_tags, { desc = "Telescope help tags" })
 
map("n", "gd", vim.lsp.buf.definition, { desc = "Go to definition" })
map("n", "gD", vim.lsp.buf.declaration, { desc = "Go to declaration" })
map("n", "gr", builtin.lsp_references, { desc = "LSP references" })
map("n", "gI", vim.lsp.buf.implementation, { desc = "Go to implementation" })
map("n", "K", vim.lsp.buf.hover, { desc = "LSP hover" })
map("n", "<leader>rn", vim.lsp.buf.rename, { desc = "LSP rename" })
map({ "n", "v" }, "<leader>ca", vim.lsp.buf.code_action, { desc = "LSP code action" })
 
-- DAP (debugging)
map("n", "<leader>db", function() require("dap").toggle_breakpoint() end,
  { desc = "Toggle breakpoint" })
map("n", "<leader>dB", function()
  require("dap").set_breakpoint(vim.fn.input "Breakpoint condition: ")
end, { desc = "Conditional breakpoint" })
map("n", "<leader>dc", function() require("dap").continue() end,
  { desc = "Continue / start debug" })
map("n", "<leader>dso", function() require("dap").step_over() end,
  { desc = "Step over" })
map("n", "<leader>dsi", function() require("dap").step_into() end,
  { desc = "Step into" })
map("n", "<leader>dsO", function() require("dap").step_out() end,
  { desc = "Step out" })
map("n", "<leader>dt", function() require("dap").terminate() end,
  { desc = "Terminate debug" })
map("n", "<leader>du", function() require("dapui").toggle() end,
  { desc = "Toggle DAP UI" })
map("n", "<leader>dr", function()
  require("dapui").eval(nil, { enter = true })
end, { desc = "Evaluate expression" })
map({ "n", "v" }, "<leader>dh", function() require("dapui").eval() end,
  { desc = "Hover evaluate" })

Highlights:

  • <leader>r — Run the current file in a terminal (supports 11 languages)
  • <leader>ni — Scaffold a new project for Go, Rust, C, C++, Java, Python, JS, TS, or PHP

Project Scaffolding

lua/configs/project_init.lua

A built-in project scaffolder triggered with <leader>ni. It detects the current filetype or lets you pick a language, then generates a starter project in a directory of your choice.

LanguageWhat it creates
Gogo.mod via go mod init
Rustcargo init
CMakefile + src/main.c
C++Makefile + src/main.cpp
JavaMaven archetype project
Pythonpyproject.toml + main.py + tests/
JavaScriptnpm init -y
TypeScriptnpm init -y + tsc --init
PHPcomposer init

Plugin Setup

lua/plugins/init.lua

return {
  {
    "stevearc/conform.nvim",
    opts = require "configs.conform",
  },
  {
    "neovim/nvim-lspconfig",
    config = function()
      require "configs.lspconfig"
    end,
  },
  {
    "mason-org/mason.nvim",
    opts = {
      ensure_installed = {
        "clangd", "gopls", "jdtls", "rust-analyzer",
        "typescript-language-server", "intelephense", "pyright",
      },
    },
  },
  { "mfussenegger/nvim-dap",
    config = function() require "configs.dap" end },
  { "rcarriga/nvim-dap-ui",
    dependencies = { "mfussenegger/nvim-dap", "nvim-neotest/nvim-nio" },
    config = function() require "configs.dapui" end },
  { "jay-babu/mason-nvim-dap.nvim",
    dependencies = { "mfussenegger/nvim-dap", "mason-org/mason.nvim" },
    opts = {
      handlers = {},
      ensure_installed = { "codelldb", "java-debug-adapter" },
    } },
  { "leoluz/nvim-dap-go",
    dependencies = { "mfussenegger/nvim-dap" },
    config = function() require("dap-go").setup() end },
  { "ojroques/nvim-osc52", lazy = false,
    config = function()
      local osc52 = require "osc52"
      osc52.setup { max_length = 0, silent = false, trim = false }
      vim.g.clipboard = {
        name = "OSC 52",
        copy = {
          ["+"] = function(text) osc52.copy(table.concat(text, "\n")) end,
          ["*"] = function(text) osc52.copy(table.concat(text, "\n")) end,
        },
        paste = {
          ["+"] = function() return { vim.fn.getreg "+", vim.fn.getregtype "+" } end,
          ["*"] = function() return { vim.fn.getreg "*", vim.fn.getregtype "*" } end,
        },
      }
    end },
}

LSP Servers

ServerLanguage
clangdC / C++
goplsGo
jdtlsJava
rust_analyzerRust
ts_lsTypeScript / JavaScript
intelephensePHP
pyrightPython

Formatters (conform.nvim)

LanguageFormatter
Luastylua
Gogofmt
Rustrustfmt
C / C++clang-format
Javagoogle-java-format
Pythonblack
JS / TSprettier
PHPphp-cs-fixer
HTML / CSS / JSON / YAML / MDprettier

Format on save is enabled with a 500ms timeout and LSP fallback.

DAP Configurations

LanguageAdapterNotes
C / C++codelldbPrompts for executable path
GodelveLaunch current file
Javajava-debug-adapterLaunch or attach to remote (port 5005)

DAP UI auto-opens on debug start and auto-closes on terminate.


Cheatsheet

KeyAction
;Enter command mode
jkEscape insert mode
<C-p>Find files
<C-f>Live grep (project-wide)
<leader>fbList buffers
<leader>fhHelp tags
<leader>foRecent files
<leader>/Toggle comment

Code

KeyAction
gdGo to definition
gDGo to declaration
gIGo to implementation
grLSP references
KHover docs
<leader>rnRename symbol
<leader>caCode actions
<leader>fmFormat file
<leader>rRun current file

Project

KeyAction
<leader>niScaffold new project
<leader>eFocus file tree
<C-n>Toggle file tree

Debugging (DAP)

KeyAction
<leader>dbToggle breakpoint
<leader>dBConditional breakpoint
<leader>dcContinue / start
<leader>dsoStep over
<leader>dsiStep into
<leader>dsOStep out
<leader>dtTerminate
<leader>duToggle DAP UI
<leader>drEvaluate expression
<leader>dhHover evaluate

Buffers & Windows

KeyAction
Tab / S-TabNext / prev buffer
<leader>bNew buffer
<leader>xClose buffer
C-h/C-l/C-j/C-kMove between windows
:sp / :vspSplit horizontal / vertical

Terminal

KeyAction
<A-i>Floating terminal
<A-h>Toggle horizontal terminal
<A-v>Toggle vertical terminal
<leader>hNew horizontal split
<leader>vNew vertical split
C-xEscape terminal mode

Vim Motions

KeyAction
w/bWord forward / back
eEnd of word
0/^/$Start / first non-blank / end of line
gg/GTop / bottom of file
Ctrl-d/Ctrl-uPage down / up
zzCenter screen on cursor
%Matching bracket
*/#Search word under cursor

Visual Mode

KeyAction
VLine select
vCharacter select
Ctrl-vBlock select
y/d/pYank / delete / paste
oJump to other corner

Conclusion

This config turns Neovim into a full IDE without the bloat. You get file exploration, buffer tabs, LSP, debugging, formatting, Git integration, project scaffolding, and one-key code running — all in the terminal.

The modular structure keeps user overrides separate from NvChad defaults. Upgrades are painless: pull the latest NvChad branch and your configs stay intact.