My Neovim Setup for Python, Go, and TypeScript
I like Neovim because it can be very fast without feeling bare. The goal is not to turn the editor into a full IDE clone. The goal is to build a focused editing environment that handles the boring parts well:
- finding files
- jumping around code
- running formatters
- using language servers
- searching the project
- working inside a terminal-heavy workflow For Python, Go, and TypeScript, the setup does not need to be complicated. It needs a few reliable pieces that work every day.
The Mental Model
My mental model for Neovim is simple:
Neovim should be the place where editing, navigation, and feedback happen quickly. It does not need to own everything. I still use the terminal directly for many commands. I still run test suites from the command line. I still inspect docs in a browser. The editor does not need to hide those tools. The setup should make common actions cheap:
- open a file by fuzzy search
- jump to a definition
- rename a symbol
- format a buffer
- search for text with ripgrep
- see diagnostics without leaving the file
- run a terminal command when needed That is the core.
Plugin Manager
Most modern Neovim setups use a plugin manager. `lazy.nvim` is a common choice because it keeps plugin configuration organized and can load plugins only when needed. The exact plugin manager matters less than the structure: ```plain text nvim/ init.lua lua/ config/ plugins/
I prefer having plugin configuration split by purpose instead of putting everything into one huge file.
Example categories:
```plain text
plugins/editor.lua
plugins/lsp.lua
plugins/completion.lua
plugins/git.lua
plugins/search.lua
plugins/theme.lua
This makes the setup easier to change later.
File Search and Project Search
The two navigation tools I care about most are file search and text search. For file search, a fuzzy finder is essential. Many setups use Telescope or fzf-lua. The workflow is: ```plain text leader + f -> find file leader + g -> live grep leader + b -> open buffer
The exact keybindings are personal, but they should be memorable.
For text search, I want the editor to use \`rg\` under the hood. Ripgrep is fast, respects \`.gitignore\`, and works well across large projects.
This matters a lot in real projects. If finding a file or searching for a function name feels slow, the whole editor feels slow.
## Language Servers
Language servers are what make modern Neovim practical for application work.
They provide:
- diagnostics
- go to definition
- hover information
- rename symbol
- references
- completion
- code actions
For the languages I use often:
```markdown
| Language | Common language server |
| --- | --- |
| Python | `pyright` or `basedpyright` |
| Go | `gopls` |
| TypeScript | `typescript-language-server` |
The important part is making sure each language server is installed and wired into Neovim's LSP client. Once that is working, the editor starts to feel much more useful without needing a heavyweight setup.
Formatting
Formatting should be boring. For Python, I usually want something like `ruff` or `black`. For Go, `gofmt` is the standard. For TypeScript, Prettier is common. The editor should be able to format the current file with one keybinding: ```plain text leader + format
Some people like format-on-save. I usually like it when the project is already consistent. On messy projects, I prefer manual formatting so I do not accidentally create huge diffs.
That choice depends on the repository.
## Completion
Completion should help without getting in the way.
The usual pieces are:
- LSP completion
- buffer words
- file paths
- snippets if the project benefits from them
I do not want completion to be noisy. If the completion menu constantly suggests irrelevant things, it slows me down instead of helping.
This is especially noticeable in TypeScript projects where there can be many exported symbols. Good completion is useful. Bad completion is clutter.
## Git Integration
I like having lightweight Git feedback in the editor:
- changed lines in the gutter
- quick blame for the current line
- stage or reset small hunks when useful
But I still prefer the terminal for most Git work.
That keeps the workflow simple:
```bash
git status
git add path/to/file
git commit -m "message"
git push
Neovim should make Git status visible, not replace understanding Git.
Common Mistakes
Mistake 1: Installing too many plugins at once
It is easy to copy a large config and end up with a setup you do not understand. Start with editing, search, LSP, formatting, and Git indicators. Add more only when you feel a real problem.
Mistake 2: Ignoring project tools
Neovim should respect the project. If the project uses Ruff, use Ruff. If the project uses Prettier, use Prettier. If the project has a specific TypeScript config, let the language server read it. The editor should not fight the repository.
Mistake 3: Making keybindings impossible to remember
Keybindings should have a pattern.
For example:
plain text
leader + f = file actions
leader + g = grep or git actions
leader + l = language actions
The exact choices are less important than consistency.
Where This Shows Up in Real Projects
A good Neovim setup helps most when switching between different kinds of projects. In a Django project, I want to jump between models, views, templates, and tests quickly. In a Go project, I want `gopls` and `gofmt` to be invisible and reliable. In a TypeScript project, I want imports, types, and file navigation to feel smooth. The setup is not about showing off an editor. It is about removing friction from ordinary development work.
Key Takeaways
- Start with navigation, LSP, formatting, search, and Git feedback.
- Let project tools drive formatting and diagnostics.
- Keep the config understandable enough that you can fix it later.
Neovim works best when it complements the terminal instead of trying to replace it.
Related Articles
Setting Up a Mac for Development
- Terminal Tools I Actually Use
- Git Branching Explained: Feature Branch to Master