Skip to content

Latest commit

 

History

History
399 lines (304 loc) · 17.2 KB

File metadata and controls

399 lines (304 loc) · 17.2 KB

logo

SOV710's Neovim Configuration

Stand With Palestine License Last Commit Stars Neovim Lua Gentoo

Ask DeepWiki

English | 中文

dashboard-showcase

Philosophy

Every editor — VS Code, Emacs, Sublime, JetBrains, Neovim — is a composition of the same eight things:

  1. A plugin manager that disappears into the background
  2. The editor's own knobsvim.opt, autocmds, the built-ins
  3. A modal keymap layer you fully own
  4. A UI with taste — dashboard, statusline, indent guides, colorscheme
  5. An editing flow — pairs, file tree, picker, which-key, marks, terminal
  6. First-class language support — for every language, all eight of: Tree-sitter, LSP, completion, diagnostics, linter, formatter, DAP, snippets
  7. Version control that doesn't make you drop to a shell
  8. An AI stance — because it's 2026 and blindly accepting agent tooling is its own statement

This config covers the first seven directly. The eighth is now a deliberate omission.

Who this is for

You're already maintaining your own config. You're trying to organize 20+ language setups without the whole thing becoming a mess, you've outgrown copy-pasting from someone else's init.lua, or you want to see what a from-scratch architecture looks like at some scale.

Or: you're using LazyVim, NvChad, AstroVim, LunarVim and you're starting to run into its limits. You can't quite articulate what you want changed, but you know your editor is doing things you don't fully understand. This repo is a from-scratch alternative.

If you're brand new to Neovim, start with kickstart.nvim. Come back here in six months.

Quick start

Requirements:

  • Neovim 0.12+
  • Git, the tree-sitter CLI, a Nerd Font
  • a C compiler (for tree-sitter parsers)
  • ripgrep (required by grug-far and other pickers).
git clone https://github.com/SOV710/nvim-config ~/.config/nvim
nvim

lazy.nvim bootstraps itself on first launch, installs all plugins, and mason-tool-installer pulls every mason-managed package.

Then run:

:checkhealth langs
:TSInstall

:checkhealth langs reports which non-mason external tools still need installing, with the install command for each. :TSInstall clones the tree-sitter parser sources declared by the language modules, generates parsers when needed, compiles them, and installs parsers plus queries into this config's managed Neovim runtime.

Contents

1. Plugin manager

lazy.nvim. Spec files are organized by purpose under lua/plugins/{ui,editor,langs,git}/, each directory imported as a unit. Snacks modules live in separate spec files under lua/plugins/ui/; lazy.nvim merges them automatically.

2. Options

lua/core/options.lua — a few dozen lines of vim.opt. If something can stay a default, it stays a default.

3. Keymaps

All keymaps live under lua/keymaps/, organized by feature. Plugin specs never call vim.keymap.set directly — they reference keymap files that return plain tables:

-- lua/plugins/editor/flash.lua
return {
  'folke/flash.nvim',
  event = 'VeryLazy',
  opts = {},
  keys = require('keymaps.editor.flash'),
}
-- lua/keymaps/editor/flash.lua
return {
  { 's', mode = { 'n', 'x', 'o' }, function() require('flash').jump() end,       desc = 'Flash jump' },
  { 'S', mode = { 'n', 'x', 'o' }, function() require('flash').treesitter() end, desc = 'Flash treesitter' },
}

All keymaps are greppable from one directory.

A few notable bindings:

  • s / S → flash (overrides native substitute, which I never use)
  • m → grapple toggle (overrides native mark)
  • ' → grapple menu (overrides native mark jump)
  • + / - → dial (replaces <C-a> / <C-x>)
  • <leader>g* → git, <leader>h* → hunks

4. UI

Tokyo Night colorscheme throughout. The statusline and tabline are a custom heirline build — the tabline integrates grapple.nvim so marked files appear directly in the top bar.

Component Plugin
Colorscheme folke/tokyonight.nvim
Statusline + tabline rebelot/heirline.nvim (custom)
Icons nvim-mini/mini.icons
Messages folke/noice.nvim (messages only; cmdline stays inline at the bottom)
Dashboard, indent, scroll, statuscolumn, image folke/snacks.nvim
Mode-aware cursorline mvllow/modes.nvim
Color literals (hex, CSS, Tailwind) brenoprata10/nvim-highlight-colors

🚧 Showcase pending: full UI tour — dashboard → editing buffer → grapple-aware tabline (15s GIF)

5. Editing flow

Component Plugin
File explorer stevearc/oil.nvim + snacks.explorer
Picker snacks.picker (replaces telescope)
Motion folke/flash.nvim
Surround echasnovski/mini.surround
Pairs windwp/nvim-autopairs
Match brackets / tags andymass/vim-matchup
Text objects nvim-treesitter-textobjects
Yank ring gbprod/yanky.nvim
Comments folke/ts-comments.nvim (treesitter-aware)
Marks (harpoon-style) cbochs/grapple.nvim
LSP reference jumps snacks.words
Scratch buffer snacks.scratch
Split / join Wansmer/treesj
Inc / dec monaqa/dial.nvim
Undo tree Neovim builtin nvim.undotree
Substitute gbprod/substitute.nvim
Project search & replace MagicDuck/grug-far.nvim
Terminal akinsho/toggleterm.nvim
Which-key folke/which-key.nvim

🚧 Showcase pending: 10s flash.nvim jump GIF

🚧 Showcase pending: grapple marks appearing in heirline tabline (10s GIF)

6. Languages

Each supported language — Rust, Go, Python, TypeScript, Haskell, Lua, C/C++, LaTeX, Fish, SQL, and 20+ others — has a single file under lua/langs/. That file declares everything: LSP server config, tree-sitter parser source/build/query manifests, formatters, linters, DAP adapters, snippets, mason packages, file-type detection, and any language-specific plugins.

A typical lua/langs/<name>.lua:

return {
  filetypes = { 'rust' },
  treesitter = {
    languages = {
      rust = {
        parser = {
          source = {
            type = 'git',
            url = 'https://github.com/tree-sitter/tree-sitter-rust',
          },
          build = {
            files = { 'src/parser.c', 'src/scanner.c' },
          },
        },
        queries = {
          sources = {
            { type = 'parser_source', lang = 'rust' },
          },
        },
      },
    },
  },

  lsp = {
    rust_analyzer = {
      settings = {
        ['rust-analyzer'] = {
          checkOnSave = { command = 'clippy' },
        },
      },
    },
  },

  formatter = 'rustfmt',
  -- linter omitted: rust_analyzer covers it

  dap = {
    adapter        = { codelldb = { type = 'server', port = '${port}', ... } },
    configurations = { rust = { { name = 'Launch', type = 'codelldb', ... } } },
  },

  snippets = function()
    local ls = require('luasnip')
    return {
      ls.snippet('pln', { ls.text_node('println!("'), ls.insert_node(1), ls.text_node('");') }),
    }
  end,

  mason = { 'rust-analyzer', 'codelldb' },

  external_deps = {
    { cmd = 'cargo',   install = 'rustup default stable',          required = true },
    { cmd = 'rustfmt', install = 'rustup component add rustfmt',   required = true },
  },

  plugins = {
    { 'mrcjkb/rustaceanvim', version = '^5', lazy = false },
  },
}

lua/core/language.lua scans lua/langs/*.lua on require and aggregates the declarations into:

Aggregated as Consumed by
language.formatters conform.nvim
language.linters nvim-lint
language.treesitter core.treesitter's native installer/runtime
language.mason mason-tool-installer
language.dap_adapters, language.dap_configurations nvim-dap
language.snippets LuaSnip
language.plugins injected directly into the lazy.nvim spec
LSP servers registered via the native vim.lsp.config / vim.lsp.enable API — no nvim-lspconfig

lua/core/treesitter.lua owns the tree-sitter pipeline. It provides :TSInstall, :TSUpdate, :TSStatus, and :TSClean; checks out each parser source from its declared git URL; runs tree-sitter generate when the manifest asks for it; compiles parser shared objects; installs query sources into a managed runtime; registers filetypes through vim.treesitter.language.register; and starts highlighting through Neovim's native vim.treesitter.start.

There is no nvim-treesitter parser registry, ensure_installed, or installer dependency in this config. Parser provenance lives in lua/langs/*.lua, not in an external plugin registry.

So conform's full spec is:

opts = {
  formatters_by_ft = require('core.language').formatters,
}

🚧 Showcase pending: architecture diagram — lua/langs/*.luacore/language.lua → fan-out to consumers

Toggling languages. Each lang file accepts an enabled = false field. When set, the file is dropped during the scan and all downstream consumers (Tree-sitter, LSP, conform, lint, DAP, snippets, mason) lose it consistently. Useful for isolating which language broke after a plugin update.

External dependencies. Many language toolchains can't be managed by mason — tsgo builds from source, ty ships via PyPI, HLS via GHCup, fish-lsp via npm, sleek via cargo, and things like latexindent and chktex come bundled with TeX Live. Each lang file with non-mason deps documents them in two places:

  1. A top-of-file block comment with the install command and a one-liner to verify it.
  2. A structured external_deps field consumed by :checkhealth langs, which reports what's present and what's missing.
:checkhealth langs

==============================================================================
langs:                                          require("langs.health").check()

haskell ~
- OK ghc
- OK cabal
- ERROR haskell-language-server: not found
  - install: ghcup install hls recommended
- WARN haskell-debug-adapter: not found (optional)
  - install: cabal install haskell-debug-adapter haskell-dap ghci-dap

python ~
- OK ty

rust ~
- OK cargo
- OK rustfmt

dashboard-checkhealth-langs

The plumbing per language:

Layer Plugin
Package manager williamboman/mason.nvim + WhoIsSethDaniel/mason-tool-installer.nvim
Tree-sitter core.treesitter + native vim.treesitter
LSP vim.lsp.config + vim.lsp.enable (native, no nvim-lspconfig)
Completion saghen/blink.cmp (Rust fuzzy matcher)
Snippets L3MON4D3/LuaSnip + friendly-snippets
Formatter stevearc/conform.nvim
Linter mfussenegger/nvim-lint
Diagnostics vim.diagnostic (native) + rachartier/tiny-inline-diagnostic.nvim
Diagnostics list folke/trouble.nvim
DAP mfussenegger/nvim-dap + theHamsta/nvim-dap-virtual-text

Language-specific plugins. Injected into the lazy.nvim spec via each lang file's plugins field:

Language Plugin
Rust mrcjkb/rustaceanvim, Saecki/crates.nvim
Haskell mrcjkb/haskell-tools.nvim
C / C++ p00f/clangd_extensions.nvim
LaTeX lervag/vimtex
Clojure / Lisp Olical/conjure
Markdown MeanderingProgrammer/render-markdown.nvim
CSV / TSV cameron-wags/rainbow_csv.nvim
JSON / YAML b0o/SchemaStore.nvim
Ghostty bezhermoso/tree-sitter-ghostty

7. Version control

Component Plugin Purpose
Hunks in the gutter lewis6991/gitsigns.nvim ]h / [h jump, <leader>hs stage hunk, blame line
Git commands tpope/vim-fugitive :Git status, commit, push, pull, blame
Diff viewer sindrets/diffview.nvim Visual diffs and file history
Open in browser Snacks.gitbrowse <leader>gB to GitHub / Codeberg

The four tools don't overlap: fugitive handles git commands, diffview handles viewing diffs, gitsigns handles in-buffer hunk operations, gitbrowse opens the web view.

In practice the plugin I reach for most in this section is toggleterm. Most git operations — stage, commit, rebase, push — happen in a shell buffer. The four plugins above exist to cover the cases a terminal is awkward for: inline blame, hunk navigation inside the current buffer, side-by-side diffs, and jumping to a commit on the web.

8. AI

There is no AI plugin layer now. lua/plugins/ai/ was removed deliberately.

Claude Code has, for my purposes, become a piece of bullshit. Anthropic is no longer a company I trust or respect: the product direction feels hostile to the people using it, and Claude Code now carries far too much opaque telemetry and detection surface for something that wants to act as an agent in my development environment. Whether you call that spyware or simply hostile tooling, it is not acceptable as a layer inside this config.

The broader agent workflow has also moved on. Agents are leaving the old "Cursor mode" — editor sidebars, inline chat overlays, editor-owned workflows — and moving toward "chatbox mode": standalone tools such as Codex and opencode that own their own TUI/chat surface and treat the editor as context, not as the whole interface. In that world, claudecode.nvim, avante.nvim, and similar Cursor-style Neovim plugins add more coupling than value.

So AI remains part of the workflow, but outside Neovim. This config will not carry a dedicated AI plugin category for now.

Adding a new language

  1. Drop a new file: lua/langs/<name>.lua
  2. Fill in the fields you need (everything is optional except filetypes)
  3. Restart Neovim

The aggregator picks it up on the next require; all downstream plugins see the new entries automatically.

If the new language declares a tree-sitter manifest, run :TSInstall <lang> after restart to fetch, build, and register its parser/query runtime.

To temporarily disable a language without deleting the file, add enabled = false at the top and restart. It disappears from all consumers — Tree-sitter, LSP, formatters, linters, DAP, snippets.

Removing a language

  1. Delete lua/langs/<name>.lua — or just trim the unwanted entries out of its mason field.
  2. Restart Neovim.
  3. Run :checkhealth langs. The langs.mason section flags any mason package that's installed but no longer declared.
  4. Run :TSClean if the removed language had a tree-sitter parser, so stale parser/query checkouts and runtime entries are pruned.
  5. Remove orphans with :MasonUninstall <pkg> (targeted) or :MasonToolsClean (all at once). :MasonToolsClean also removes packages belonging to a lang currently marked enabled = false, so prefer per-package uninstall if you have disabled langs.

Project layout

init.lua                     -- lazy.nvim bootstrap, top-level setup
lua/
├── core/
│   ├── options.lua
│   ├── language.lua         -- the aggregator
│   ├── treesitter.lua       -- native parser/query installer and runtime
│   └── sysinfo.lua
├── keymaps/                 -- every keymap, organized by feature
│   ├── init.lua
│   ├── editing.lua
│   ├── winbuf.lua
│   ├── snacks.lua
│   ├── which-key.lua
│   ├── editor/
│   ├── git/
│   ├── ai/
│   └── langs/
├── plugins/
│   ├── snacks.lua           -- central Snacks registration
│   ├── ui/
│   ├── editor/
│   ├── langs/               -- completion, format, lint, treesitter, dap, mason, snippets
│   ├── git/
│   └── ai/
└── langs/                   -- one file per language; the source of truth
    ├── rust.lua
    ├── python.lua
    ├── haskell.lua
    └── ...

Acknowledgments

Thanks to @Lingshinx for the great ASCII art.

License

GPL-3.0-or-later. See LICENSE.

Every source file carries an SPDX-License-Identifier header.