From Zero to Killer Neovim on Fedora 42 (Rust-Edition)
If you want a vanilla-leaning Neovim that feels stock but has full language features, debugging, tests, Git, fuzzy search, completions, keybinding help, AI, statusline, spell-check, and developer fonts—this is it. We’ll use native LSP, Treesitter, lazy.nvim for plugin management, and a short, readable config. Rust is first-class; Python, TypeScript/CDK, and Bash get the same baseline.
1) System prerequisites (Fedora 42)
# Core editor + search tools + build deps
sudo dnf install -y neovim git ripgrep fd-find curl wget unzip cmake gcc-c++ make python3 python3-pip nodejs npm
# Rust toolchain via rustup (recommended)
sudo dnf install -y rustup
rustup toolchain install stable
rustup default stable
rustup component add rustfmt clippy
# rust-analyzer (prefer rustup component if available; else use Fedora package)
rustup component add rust-analyzer || sudo dnf install -y rust-analyzer
# Optional: shells/linters/formatters used later
sudo dnf install -y shellcheck shfmt
Neovim ships with a built-in LSP client, so we’ll not add external LSP frameworks. (Neovim)
2) Developer fonts (Nerd Font + ligatures)
Install a Nerd Font (icons for statusline/git signs/fuzzy UI). JetBrains Mono Nerd Font is a great default.
mkdir -p ~/.local/share/fonts ~/.config/fontconfig/conf.d
cd /tmp
# Download a Nerd Font release zip (choose JetBrainsMono Nerd Font from nerdfonts.com releases)
# Example (update the URL to the latest release if needed):
wget -O JBMNerd.zip "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/JetBrainsMono.zip"
unzip JBMNerd.zip -d ~/.local/share/fonts
fc-cache -fv
For ligatures (JetBrains Mono already supports), set your terminal to use JetBrainsMono Nerd Font.
3) Directory layout
We’ll keep a clean, minimal layout:
~/.config/nvim/
├─ init.lua
└─ lua/
└─ plugins/
├─ core.lua
├─ ui.lua
├─ lsp.lua
├─ cmp.lua
├─ treesitter.lua
├─ telescope.lua
├─ git.lua
├─ statusline.lua
├─ rust.lua
├─ dap.lua
├─ test.lua
├─ format.lua
├─ keys.lua
└─ ai.lua
4) Bootstrap the plugin manager (lazy.nvim)
lazy.nvim
is modern, fast, and dead simple. (GitHub)
~/.config/nvim/init.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)
-- Sensible defaults: keep close to vanilla
vim.g.mapleader = " " -- <Space> as leader
vim.o.number = true
vim.o.relativenumber = true
vim.o.termguicolors = true
vim.o.updatetime = 300
vim.o.signcolumn = "yes"
vim.o.clipboard = "unnamedplus"
vim.o.spell = true
vim.o.spelllang = "en_us"
-- Load plugins from lua/plugins/*.lua
require("lazy").setup("plugins", {
checker = { enabled = true }, -- background plugin update checker off by default; toggle if you like
})
5) Core plugins (minimal, powerful)
Create ~/.config/nvim/lua/plugins/core.lua
:
return {
-- Keybinding helper popup
{ "folke/which-key.nvim", event = "VeryLazy", opts = {} },
-- LSP, mason, and autoconfig
{ "neovim/nvim-lspconfig" },
{ "williamboman/mason.nvim", build = ":MasonUpdate", opts = {} },
{ "williamboman/mason-lspconfig.nvim", opts = {} },
-- Completion
{ "hrsh7th/nvim-cmp" },
{ "hrsh7th/cmp-nvim-lsp" },
{ "hrsh7th/cmp-buffer" },
{ "hrsh7th/cmp-path" },
{ "L3MON4D3/LuaSnip", build = "make install_jsregexp", dependencies = { "saadparwaiz1/cmp_luasnip" } },
-- Treesitter
{ "nvim-treesitter/nvim-treesitter", build = ":TSUpdate" },
-- Telescope (fuzzy finder)
{ "nvim-lua/plenary.nvim" },
{ "nvim-telescope/telescope.nvim", tag = "0.1.6" },
-- Git
{ "lewis6991/gitsigns.nvim", opts = {} },
-- UI / statusline
{ "nvim-lualine/lualine.nvim" },
-- Diagnostics drawer (optional, extremely useful)
{ "folke/trouble.nvim", opts = {} },
-- Terminal for quick cargo/pytest/npm
{ "akinsho/toggleterm.nvim", version = "*", opts = {} },
}
-
lazy.nvim
install notes and UI are here. (GitHub)
6) Treesitter (syntax, motions, folding)
lua/plugins/treesitter.lua
return {
"nvim-treesitter/nvim-treesitter",
opts = {
ensure_installed = { "lua", "rust", "python", "tsx", "typescript", "json", "bash", "toml", "yaml", "markdown" },
highlight = { enable = true },
indent = { enable = true },
},
}
7) Completion (nvim-cmp)
lua/plugins/cmp.lua
return {
"hrsh7th/nvim-cmp",
event = "InsertEnter",
dependencies = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "L3MON4D3/LuaSnip", "saadparwaiz1/cmp_luasnip" },
config = function()
local cmp = require("cmp")
local luasnip = require("luasnip")
cmp.setup({
snippet = { expand = function(args) luasnip.lsp_expand(args.body) end },
mapping = cmp.mapping.preset.insert({
["<C-Space>"] = cmp.mapping.complete(),
["<CR>"] = cmp.mapping.confirm({ select = true }),
["<Tab>"] = cmp.mapping.select_next_item(),
["<S-Tab>"] = cmp.mapping.select_prev_item(),
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" }, { name = "luasnip" }, { name = "path" }, { name = "buffer" },
}),
})
end,
}
8) Fuzzy search (Telescope)
lua/plugins/telescope.lua
return {
"nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
cmd = "Telescope",
opts = {
defaults = {
mappings = { i = { ["<C-j>"] = "move_selection_next", ["<C-k>"] = "move_selection_previous" } },
},
},
}
Usage:
-
:Telescope find_files
(respects.gitignore
) -
:Telescope live_grep
(uses ripgrep) -
:Telescope buffers
,:Telescope help_tags
9) Git integration
lua/plugins/git.lua
return {
"lewis6991/gitsigns.nvim",
event = "BufReadPre",
opts = {
signs = { add = { text = "▎" }, change = { text = "▎" }, delete = { text = "契" }, topdelete = { text = "契" }, changedelete = { text = "▎" } },
on_attach = function(bufnr)
local gs = package.loaded.gitsigns
local map = function(m, l, r) vim.keymap.set(m, l, r, { buffer = bufnr }) end
map("n", "]h", gs.next_hunk)
map("n", "[h", gs.prev_hunk)
map("n", "<leader>hs", gs.stage_hunk)
map("n", "<leader>hr", gs.reset_hunk)
map("n", "<leader>hb", gs.blame_line)
end,
},
}
10) Statusline
lua/plugins/statusline.lua
return {
"nvim-lualine/lualine.nvim",
dependencies = { "nvim-tree/nvim-web-devicons" },
opts = {
options = { theme = "auto", section_separators = "", component_separators = "" },
sections = {
lualine_a = { "mode" },
lualine_b = { "branch", "diff", "diagnostics" },
lualine_c = { { "filename", path = 1 } },
lualine_x = { "encoding", "fileformat", "filetype" },
lualine_y = { "progress" },
lualine_z = { "location" },
},
},
}
11) Keybinding help
which-key.nvim
pops up a cheatsheet when you press <Space>
. It’s minimal and perfect for discoverability. Add custom top-level hints in lua/plugins/keys.lua
:
return {
"folke/which-key.nvim",
opts = function()
local wk = require("which-key")
wk.add({
{ "<leader>f", group = "Find (Telescope)" },
{ "<leader>g", group = "Git" },
{ "<leader>l", group = "LSP" },
{ "<leader>t", group = "Test" },
{ "<leader>d", group = "Debug" },
{ "<leader>a", group = "AI" },
})
end,
}
12) LSP servers via Mason (one command per language)
lua/plugins/lsp.lua
return {
"neovim/nvim-lspconfig",
dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim" },
config = function()
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = {
-- Rust handled by rustaceanvim (below), but mason can still manage tools
"pyright", -- Python
"tsserver", -- TypeScript/JavaScript
"bashls", -- Bash
"jsonls", "yamlls", "lua_ls"
},
automatic_installation = true,
})
-- Python
lspconfig.pyright.setup({ capabilities = capabilities })
-- TypeScript / JS
lspconfig.tsserver.setup({ capabilities = capabilities })
-- Bash
lspconfig.bashls.setup({ capabilities = capabilities })
-- Others
lspconfig.jsonls.setup({ capabilities = capabilities })
lspconfig.yamlls.setup({ capabilities = capabilities })
lspconfig.lua_ls.setup({
capabilities = capabilities,
settings = { Lua = { diagnostics = { globals = { "vim" } } } },
})
-- LSP keymaps (global)
local map = vim.keymap.set
map("n","gd",vim.lsp.buf.definition,{desc="LSP: goto definition"})
map("n","gr",vim.lsp.buf.references,{desc="LSP: references"})
map("n","K",vim.lsp.buf.hover,{desc="LSP: hover"})
map("n","<leader>lr",vim.lsp.buf.rename,{desc="LSP: rename"})
map("n","<leader>la",vim.lsp.buf.code_action,{desc="LSP: code action"})
map("n","<leader>lf",function() vim.lsp.buf.format({async=true}) end,{desc="LSP: format"})
end,
}
13) Rust superpowers (rust-analyzer, inlay hints, code actions)
Use rustaceanvim—it auto-wires rust-analyzer with Neovim’s LSP and integrates tools; don’t double-configure rust_analyzer
in lspconfig. (GitHub)
lua/plugins/rust.lua
return {
"mrcjkb/rustaceanvim",
lazy = false, -- filetype plugin; loads on Rust buffers automatically
init = function()
-- Optional rust-analyzer settings
vim.g.rustaceanvim = {
server = {
on_attach = function(_, bufnr)
local map = function(m, l, r, d) vim.keymap.set(m, l, r, { buffer = bufnr, desc = d }) end
map("n", "<leader>lc", function() vim.cmd.RustLsp("codeAction") end, "Rust: code action")
map("n", "<leader>lh", function() vim.cmd.RustLsp("hover actions") end, "Rust: hover actions")
end,
settings = {
["rust-analyzer"] = {
cargo = { allFeatures = true },
checkOnSave = { command = "clippy" },
},
},
},
}
end,
}
14) Debugging (nvim-dap + CodeLLDB)
We’ll use nvim-dap with codelldb. The wiki shows canonical config examples and options. (GitHub)
lua/plugins/dap.lua
return {
{ "mfussenegger/nvim-dap" },
{ "rcarriga/nvim-dap-ui", dependencies = { "mfussenegger/nvim-dap" } },
{ "jay-babu/mason-nvim-dap.nvim",
dependencies = { "williamboman/mason.nvim", "mfussenegger/nvim-dap" },
opts = { ensure_installed = { "codelldb", "python" }, automatic_installation = true },
config = function(_, opts)
require("mason-nvim-dap").setup(opts)
local dap, dapui = require("dap"), require("dapui")
dapui.setup()
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
end
},
}
Usage:
-
Place breakpoints with
F9
(map it if you want); start with:DapContinue
. -
For Rust, debug compiled binaries with codelldb. The dap wiki has a codelldb recipe. (GitHub)
15) Testing (neotest)
Use neotest framework + language adapters. It relies on Treesitter and works across Rust/Python/TS. (GitHub)
lua/plugins/test.lua
return {
{ "nvim-neotest/neotest" },
{ "rouge8/neotest-rust" }, -- Rust (cargo/nextest)
{ "nvim-neotest/neotest-python" },
{ "nvim-neotest/neotest-jest" }, -- or neotest-vitest for TS
config = function()
require("neotest").setup({
adapters = {
require("neotest-rust")({ args = { "--no-capture" } }), -- example args; customize
require("neotest-python")({ runner = "pytest" }),
require("neotest-jest")({ jestCommand = "npm test --" }),
},
})
local map = vim.keymap.set
map("n","<leader>tt", function() require("neotest").run.run() end, {desc="Test: nearest"})
map("n","<leader>tf", function() require("neotest").run.run(vim.fn.expand("%")) end, {desc="Test: file"})
map("n","<leader>ts", function() require("neotest").summary.toggle() end, {desc="Test: summary"})
map("n","<leader>to", function() require("neotest").output.open({ enter = true }) end, {desc="Test: output"})
end,
}
-
neotest-rust
usescargo nextest
adapter; install withcargo install cargo-nextest
if you want nextest. (GitHub)
16) Formatting and linting
Use built-ins where possible:
-
Rust:
rustfmt
&clippy
(already installed via rustup). -
Python:
black
(formatter),ruff
(linter). -
TS/JS:
prettier
(orprettierd
),eslint
. -
Bash:
shfmt
,shellcheck
.
A tiny on-save formatter:
lua/plugins/format.lua
return {
"stevearc/conform.nvim",
opts = {
formatters_by_ft = {
rust = { "rustfmt" },
python = { "black" },
javascript = { "prettier" },
typescript = { "prettier" },
typescriptreact = { "prettier" },
json = { "prettier" },
yaml = { "prettier" },
bash = { "shfmt" },
lua = { "stylua" },
},
format_on_save = { timeout_ms = 2000, lsp_fallback = true },
},
}
Install tools:
# Python
pip3 install --user black ruff
# TypeScript/JS
sudo npm i -g prettier eslint typescript typescript-language-server
# Lua (optional)
sudo dnf install -y stylua
# Bash tools installed earlier (shellcheck, shfmt)
17) Diagnostics drawer
Optional but great: Trouble shows a tidy panel for LSP diagnostics, references, and more.
-
Toggle with
:Trouble diagnostics
-
Lazy config already added; see
core.lua
.
18) Built-in spell and quick toggles
-
Spellcheck is on by default in
init.lua
. -
Toggle quickly:
vim.keymap.set("n","<leader>us", function() vim.o.spell = not vim.o.spell end, { desc="UI: toggle spell" })
19) AI integration (OpenAI & local/free)
Two solid routes:
-
OpenAI/ChatGPT inside Neovim:
gp.nvim
is powerful and minimal-deps. SetOPENAI_API_KEY
and go. (GitHub) -
Local model with Ollama:
avante.nvim
can be wired to local providers; community docs show Ollama setups. Results vary by model; still evolving. (GitHub)
lua/plugins/ai.lua
return {
-- Option A: OpenAI via gp.nvim (great with a ChatGPT sub using API)
{
"Robitx/gp.nvim",
cmd = { "GpChatNew", "GpAppend", "GpRewrite", "GpWhisper" },
config = function()
require("gp").setup({
providers = {
openai = { -- also supports Azure, Anthropic, Ollama, etc.
endpoint = "https://api.openai.com/v1/chat/completions",
secret = os.getenv("OPENAI_API_KEY"),
model = "gpt-4o-mini", -- pick your paid model
},
},
})
vim.keymap.set("v","<leader>aa", ":GpAppend<CR>", { desc = "AI: append selection" })
vim.keymap.set("v","<leader>ar", ":GpRewrite<CR>", { desc = "AI: rewrite selection" })
vim.keymap.set("n","<leader>ac", ":GpChatNew<CR>", { desc = "AI: new chat" })
end,
},
-- Option B: local models with Ollama (Avante)
{
"yetone/avante.nvim",
enabled = false, -- flip to true if you want this path
config = function()
require("avante").setup({
provider = "openai_compatible", -- point to your local endpoint (e.g., Ollama via an OpenAI shim)
openai_compatible = { endpoint = "http://localhost:11434/v1", model = "qwen2.5-coder" },
})
end,
},
}
Notes:
-
gp.nvim
supports multiple providers, including local (Ollama) paths. (GitHub) -
avante.nvim
is fast-moving; check its README/wiki for provider wiring details. (GitHub)
20) Bash, Python, TypeScript/CDK specifics
-
Bash:
bash-language-server
+shellcheck
+shfmt
already configured via mason and conform. -
Python:
pyright
+black
+ruff
, debug with DAP (python
adapter is ensured by mason-nvim-dap config). -
TypeScript/CDK:
tsserver
viatypescript-language-server
, format with Prettier, test vianeotest-jest
orneotest-vitest
.
Mason installs/updates LSP servers easily from within Neovim:
:Mason
21) Minimal workflow cheatsheet
-
Find files / grep:
<Space> f f
→:Telescope find_files
,<Space> f g
→:Telescope live_grep
-
Git hunks:
]h
/[h
, stage/reset hunk<Space> h s
/<Space> h r
, blame<Space> h b
-
LSP:
gd
,gr
,K
,<Space> l r
(rename),<Space> l a
(code action),<Space> l f
(format) -
Diagnostics:
:Trouble diagnostics
-
Debug:
:DapContinue
,:DapToggleBreakpoint
, UI auto-opens -
Tests:
<Space> t t
(nearest),<Space> t f
(file),<Space> t s
(summary) -
AI: visual select →
<Space> a a
(append) /<Space> a r
(rewrite); chat<Space> a c
-
Terminal:
:ToggleTerm
(runcargo test
,pytest
,npm test
quickly)
22) Post-install: first launch and health
nvim
Inside Neovim:
:checkhealth
:Lazy
:Mason
-
Use
:Lazy
to ensure plugins are installed/updated (lazy.nvim docs). (lazy.folke.io) -
For Rust, open a Rust project file—rustaceanvim auto-activates rust-analyzer; no extra LSP config needed. (GitHub)
-
For DAP, ensure codelldb is installed via
:Mason
→ DAP tab; use the nvim-dap codelldb wiki snippet if you want more control. (GitHub) -
For neotest adapters, see their READMEs for advanced options (e.g.,
cargo nextest
flags). (GitHub)
23) Troubleshooting
-
Fedora Neovim version:
sudo dnf info neovim
. If you need the very latest (0.10+), you can build from source (Fedora Magazine article shows steps). (Fedora Magazine) -
Rust debug troubles: verify
codelldb
path and see the nvim-dap codelldb wiki. Also consider examples discussed in issues/discussions. (GitHub) -
AI plugins:
gp.nvim
requires onlycurl
by default; setOPENAI_API_KEY
. For local, followavante.nvim
provider notes and community guides; model quality and “apply edits” UX can vary by model. (GitHub)
24) Why this stack?
-
Vanilla-first: Built-in LSP + Treesitter. No over-the-top distributions or heavy keymap rewrites.
-
Lazy but powerful:
lazy.nvim
gives simple, declarative plugin specs. (GitHub) -
Rust-centric:
rustaceanvim
is the modern Rust plugin; “don’t double-configure rust_analyzer.” (GitHub) -
Serious tooling:
nvim-dap
+codelldb
for debugging;neotest
for unified test UX. (GitHub)
Comments
Post a Comment