ACH-NEOVIM: A Step-by-Step Tutorial to a Supercharged Neovim
A walkthrough of ACH-NEOVIM — my personal Neovim configuration that bootstraps a 45+ language IDE with one command. Native LSP, on-demand tooling, deep ocean palette, and zero manual setup.
I've been a Neovim user for years. Every time I rebuild my config from scratch I learn something new — but I also lose a weekend wrangling lazy.nvim, mason, LSP servers, formatters, linters, and a dozen plugins that all need to be wired together just right.
So I finally stopped rebuilding and started shipping. ACH-NEOVIM is the result — a single-command install that drops a polished, 45+ language Neovim IDE onto a fresh Mac with zero manual setup.
This post is a guided tour. By the end, you should be able to clone the repo, run one script, and start coding — or fork it and make it your own.
ACH-NEOVIMMITQuick Start
| git clone https://github.com/anirbanchakraborty-dev/ACH-NEOVIM.git | |
| cd ACH-NEOVIM | |
| ./install.sh |
That's it. Launch nvim, the dashboard appears, plugins download themselves, and the first time you open a Python file the LSP, formatter, and linter install in the background.
What You Get
A short list of what makes this config different from a stock lazy.nvim starter:
- One-command install.
install.shprovisions Homebrew, the latest stable Neovim, and the Claude Code CLI. Re-run it any time — every step is idempotent. - On-demand everything. No
ensure_installedlists anywhere. Open a Python file andpyright + ruffinstall themselves. Open a Go file andgopls + gofumptarrive. Treesitter parsers, formatters, and linters all follow the same pattern. - Native LSP client. Uses Neovim 0.11+'s
vim.lsp.config/vim.lsp.enableflow withnvim-lspconfigproviding per-server defaults.blink.cmpcapabilities are merged into every server automatically. - 45+ languages supported out of the box, all installed lazily on first use.
- Deep ocean palette. A custom
tokyonightoverride (#011628background,#011423floats,#0A64ACsearch) themed across every plugin: fzf-lua, lazy, mason, which-key, snacks, noice, trouble, diffview, and git-conflict. - Claude Code inside Neovim.
coder/claudecode.nvimships Claude as a snacks-themed split with native diff review and selection tracking. - Schema-aware JSON / YAML.
SchemaStore.nvimis wired into bothjsonlsandyamlls— you get completion + validation forpackage.json,tsconfig.json, GitHub Actions, Kubernetes manifests, docker-compose, and 1200+ other schemas with no manual setup. - Per-project LSP overrides.
neoconf.nvimauto-merges.neoconf.jsonand.vscode/settings.jsonfrom the project root into the LSP config, so cloning a JS/TS project that ships shared editor settings just works.
Step 1 — Run the Installer
The installer does four things:
- Installs Homebrew if it isn't already present.
- Installs or upgrades Neovim to the latest stable release (validated against the GitHub releases API — not just whatever Homebrew last cached).
- Installs the Claude Code CLI via the official native installer (
~/.local/bin/claude, auto-updates in the background) and appends the PATH export to~/.zshrcif it isn't already there. - Symlinks
nvim/to~/.config/nvim. If you already have a Neovim config, it gets moved to a timestamped backup first.
Every step prints a colored status line, and the script bails on the first error thanks to set -euo pipefail. Re-running is safe — anything already installed is skipped.
| ./install.sh |
When it finishes, you'll see something like:
| [OK] ACH-NEOVIM setup complete! | |
| Neovim: NVIM v0.11.x | |
| Claude: 1.x.x | |
| Config: /Users/you/.config/nvim -> /path/to/ACH-NEOVIM/nvim | |
| [INFO] Launch Neovim with: nvim |
Step 2 — First Launch
Open Neovim:
| nvim |
The first launch does a few things in order:
lazy.nvimbootstraps itself (clones from GitHub on first run).- Every plugin in
nvim/lua/plugins/is downloaded. - The
snacks.dashboardstartup screen appears with quick links to recent files, sessions, and common actions. - As soon as you open a file in any supported language, mason starts installing the matching LSP server, formatter, and linter in the background. You'll see a
snacks.notifiertoast for every install.
Step 3 — Authenticate GitHub (Optional)
If you want the in-editor GitHub integration via octo.nvim (issues, PRs, code review, full-text search), authenticate the gh CLI once:
| gh auth login |
After that, <leader>gi lists issues, <leader>gp lists PRs, <leader>gP searches PRs, and <leader>gS runs a full-text GitHub search — all in fzf-lua pickers, all rendered as Neovim buffers.
The Configuration Tour
Here's what lives in the repo:
ACH-NEOVIM/
├── install.sh
├── uninstall.sh
└── nvim/
├── init.lua entry point
└── lua/
├── config/
│ ├── icons.lua central Nerd Font glyph table
│ ├── lazy.lua lazy.nvim bootstrap
│ ├── options.lua vim.opt defaults
│ ├── keymaps.lua non-plugin keymaps
│ └── autocmds.lua augroups (yank flash, big-file, prose mode, ...)
└── plugins/
├── ai.lua Claude Code (coder/claudecode.nvim)
├── coding.lua blink.cmp, mini.pairs/surround/ai, lazydev
├── colorscheme.lua tokyonight + deep ocean palette
├── editor.lua which-key, fzf-lua, flash, todo-comments, trouble
├── formatting.lua conform.nvim + on-demand mason installer
├── git.lua gitsigns, diffview, git-conflict, lazygit
├── lang.lua render-markdown, markdown-preview, vimtex, venv-selector
├── linting.lua nvim-lint + on-demand mason installer
├── lsp.lua mason + native vim.lsp client + SchemaStore + neoconf
├── lualine.lua statusline (custom ocean theme)
├── terminal.lua toggleterm + language REPLs
├── treesitter.lua nvim-treesitter (main branch) + textobjects + context
├── ui.lua snacks, noice, bufferline, mini.icons, rainbow, colorizer
└── util.lua persistence sessions, vim-sleuth, scratchEach plugin file returns a table of lazy.nvim specs. To add a new plugin, drop a new file in nvim/lua/plugins/ and lazy auto-imports it.
Languages Supported
The whole point of "on-demand everything" is that you don't have to think about this list — but if you're curious whether your language is covered, here's the rundown.
Core stack (used daily)
| Language | LSP | Formatter | Linter |
|---|---|---|---|
| Lua | lua_ls | stylua | — |
| Python | pyright + ruff | ruff (organize+fmt) | ruff (LSP) |
| TypeScript / JS | ts_ls + eslint (LSP) | prettierd / prettier + eslint | eslint (LSP) |
| HTML / CSS | html + emmet, cssls | prettierd / prettier | — |
| JSON / YAML | jsonls / yamlls + SchemaStore | prettierd / prettier | yamllint |
| Markdown | marksman | prettier + markdown-toc + mdlint | markdownlint |
| Bash / Zsh | bashls | shfmt | shellcheck |
| C / C++ | clangd + clangd_extensions | clang-format | — |
| Go | gopls | goimports + gofumpt | golangci-lint |
| LaTeX / BibTeX | texlab + vimtex | latexindent / bibtex-tidy | — |
Web
| Language | LSP | Formatter | Notes |
|---|---|---|---|
| Angular | angularls | prettier | |
| Astro | astro-language-server | prettier | |
| Svelte | svelte-language-server | prettier | |
| Vue | vue_ls (Volar) | prettier | standalone Volar |
| Tailwind CSS | tailwindcss-language-server | — | attaches across html/css/js/ts/... |
| Prisma | prismals | prettier |
Systems / Compiled
| Language | LSP | Formatter | Notes |
|---|---|---|---|
| Rust | rust_analyzer (clippy on save) | rustfmt | |
| Zig | zls | zig fmt | |
| Haskell | haskell-language-server | ormolu | heavy install (~2 GB) |
| OCaml | ocaml-lsp | ocamlformat | |
| Elixir | elixir-ls | mix format | |
| C# / VB | omnisharp | csharpier | |
| Kotlin | kotlin-language-server | ktlint | |
| Scala | metals | scalafmt | |
| Java | jdtls | google-java-format | |
| PHP | intelephense | php-cs-fixer / pint |
Infra / Data
| Language | LSP | Formatter | Linter |
|---|---|---|---|
| Ansible | ansible-language-server | — | ansible-lint |
| CMake | cmake-language-server | cmake-format | cmakelint |
| Helm | helm-ls | — | — |
| Terraform | terraform-ls | terraform fmt | tflint |
| TOML | taplo | taplo | — |
| SQL | sqls | sqlfluff | sqlfluff |
| Solidity | solidity_ls_nomicfoundation | forge fmt | solhint |
| Dockerfile | — | — | hadolint |
…plus Ruby, Perl, Swift, R, Julia, Lean, Elm, Typst, Nix, Erlang, Clojure, Gleam, Dart, Twig, Ember, Rego, and more. The full table is in the README.
Design Decisions
On-Demand Everything
This is the part I'm proudest of. Most Neovim configs hard-code an ensure_installed list — every Mason update reinstalls 50 LSPs whether you use them or not. Cloning a fresh config means staring at a progress bar for ten minutes before you can even open a file.
ACH-NEOVIM flips the model. The lsp.lua, formatting.lua, and linting.lua plugin files each register a FileType autocmd. The first time you open a buffer of a given filetype, the autocmd looks up which tools are mapped to that filetype and asks Mason to install them in the background. A snacks.notifier toast tells you when each install starts and finishes. After that, the autocmd is a no-op.
The result: a fresh install boots in seconds. Tooling appears only as you actually need it.
Native LSP Client
Neovim 0.11 introduced vim.lsp.config / vim.lsp.enable — a built-in client that replaces the long-standing nvim-lspconfig.setup() boilerplate. ACH-NEOVIM uses the new API:
nvim-lspconfigis still pulled in, but only for its default per-server config files (paths, root markers, init options).vim.lsp.config('*', { capabilities = blink_caps })mergesblink.cmp's completion capabilities into every server.vim.lsp.enable(server_name)is called per server inside the on-demand installer.
It's leaner, faster to start, and matches where the Neovim core is going.
Format + Lint Pipeline
Two plugins, two clearly-separated jobs:
conform.nvimhandles format-on-save. It uses aprettierd → prettierfallback chain for web files, gated by aprettier --file-infoparser check so prettier silently falls back to the LSP formatter on filetypes it can't parse. ESLint auto-fix-on-save runs before prettier via the eslint LSP'ssource.fixAll.eslintcode action — eslint fixes lint issues, prettier has the final word on cosmetic formatting.nvim-lintruns external linters (shellcheck,markdownlint,hadolint,yamllint,golangci-lint,ansible-lint,tflint,sqlfluff,solhint,cmakelint) via a debounced dispatcher and feeds results intovim.diagnostic.
Markdown gets two conditional formatters: markdown-toc only fires on buffers with a <!-- toc --> marker, markdownlint-cli2 only fires when there are existing markdownlint diagnostics. Cheap and surgical.
Central Icon Table
Every Nerd Font glyph used in the config — file tree icons, diagnostic markers, git symbols, which-key entries, lualine separators — lives in nvim/lua/config/icons.lua. Plugin files reference them by name (icons.diagnostics.Error, icons.git.added).
Want to swap a glyph? Change it once. It propagates everywhere. No more grep-and-replace across 15 files.
Sticky Scope Header
nvim-treesitter-context pins the current function/class/method signature to the top of the buffer when you scroll past its definition. Cursor-mode tracking with a 3-line cap so deeply-nested scopes never take over the screen. Toggle with <leader>ut.
Tailwind Class Highlighting
mini.hipatterns renders Tailwind utility class names like bg-blue-500 or text-emerald-300 with the actual color inline (background tint + contrasting fg). Works in html / css / js / ts / vue / svelte / astro / handlebars / twig / postcss. The Tailwind palette lives in config/tailwind_colors.lua as a pure data module.
It coexists with nvim-colorizer (which still handles hex / rgb / hsl / CSS named colors) — zero overlap.
Keymap Cheat Sheet
Leader is <Space>. Holding leader pops up which-key with every binding labelled and iconned.
The headline groups:
| Prefix | Group |
|---|---|
<leader>a | AI / Claude |
<leader>b | Buffer |
<leader>c | Code (LSP) |
<leader>e | Explorer (root) |
<leader>f | File / Find |
<leader>g | Git |
<leader>h | Harpoon Quick Menu |
<leader>l | Lazy |
<leader>m | Mason |
<leader>o | Overseer (Tasks) |
<leader>p | Yank History |
<leader>q | Session |
<leader>r | Refactor |
<leader>s | Search |
<leader>t | Terminal / REPLs |
<leader>u | UI toggles |
<leader>w | Window |
<leader>x | Diagnostics / Trouble |
<leader>1–<leader>9 | Harpoon jump 1–9 |
A few standalone bindings worth memorizing on day one:
| Keys | What it does |
|---|---|
<C-/> | Toggle floating terminal |
<C-h/j/k/l> | Window navigation |
<S-h> / <S-l> | Previous / next buffer |
<A-j> / <A-k> | Move line(s) down / up |
s / S | Flash jump / treesitter jump |
<C-Space> | Init / grow treesitter selection |
]f / [f | Next / prev function |
]c / [c | Next / prev class |
]d / [d | Next / prev diagnostic |
]h / [h | Next / prev git hunk |
gco / gcO | Add comment line below / above |
gsa / gsd / gsr | Surround add / delete / replace |
K | LSP hover docs |
<leader>cr | Rename symbol with live preview |
<leader>cs | Toggle symbol outline sidebar |
<leader>ch | Switch C/C++ source/header |
<leader>cv | Pick a Python virtualenv |
<leader>um | Toggle inline markdown rendering |
<leader>ut | Toggle sticky scope header |
Customization
Three things you'll probably want to tweak:
1. Add a Plugin
Create a new file in nvim/lua/plugins/ returning a table of lazy.nvim specs. lazy auto-imports it.
| -- nvim/lua/plugins/my-plugin.lua | |
| return { | |
| { | |
| "username/cool-plugin.nvim", | |
| event = "VeryLazy", | |
| opts = { | |
| -- plugin config | |
| }, | |
| }, | |
| } |
If the plugin has keymaps, follow the pattern in terminal.lua: define keys = {} on the spec, then add a parallel which-key.nvim block in the same file with icons sourced from config/icons.lua.
2. Change the Palette
Edit colorscheme.lua. The on_colors callback defines the deep-ocean colors; on_highlights overrides every plugin's themed groups. Change one hex value and the entire UI shifts.
3. Add an LSP / Formatter / Linter
Add an entry to the servers table in lsp.lua (or formatter_to_mason / linter_to_mason in formatting.lua / linting.lua). The on-demand installer will pick it up the next time you open a matching filetype.
Uninstall
| ./uninstall.sh |
Removes the symlink at ~/.config/nvim and Neovim's data / state / cache directories. Your repo clone stays intact. Re-run install.sh to set everything back up.
Try It
Clone it, fork it, rip out the parts you don't like, and never manually configure Neovim from scratch again:
| git clone https://github.com/anirbanchakraborty-dev/ACH-NEOVIM.git | |
| cd ACH-NEOVIM | |
| ./install.sh |
If you've already followed Post-Mac-Setup for the rest of your machine, this is the natural next step. Same philosophy: one script, idempotent, hands-off.