commit c72fd19497bf7ce0668dec11eadeb7821e342d02
Author: Chris Roberts <chris.roberts@learningunix.net>
Date: Wed, 6 May 2026 18:47:09 -0500
Initial Neovim configuration
Full Neovim 0.12 setup with LSP, autocompletion, auto-pairs, linting,
and formatting for Python, Bash, YAML, Ansible, and OpenTofu.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat:
12 files changed, 602 insertions(+), 0 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
@@ -0,0 +1,93 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What this repo is
+
+Neovim configuration for daily use with Python, Bash/Shell, Ansible, OpenTofu, and YAML.
+This directory is symlinked to `~/.config/nvim/`.
+
+## Install prerequisites
+
+```bash
+# Core
+sudo pacman -S neovim nodejs npm
+
+# System linters and formatters (not managed by Mason)
+sudo pacman -S shellcheck shfmt python-lsp-server python-ruff ansible-lint yamllint
+
+# tree-sitter CLI (required by nvim-treesitter to compile yaml/toml/json/hcl parsers)
+# Neovim 0.12 bundles parsers for bash, lua, python, markdown, vim, vimdoc — no CLI needed for those
+sudo pacman -S tree-sitter-cli
+
+# Optional: stylua (Lua formatter for this config)
+sudo pacman -S stylua
+```
+
+## First launch
+
+Open `nvim` — lazy.nvim bootstraps itself, installs all plugins, then Mason installs LSP servers.
+Run `:MasonInstall prettier` manually if YAML formatting is needed.
+
+## Structure
+
+```
+init.lua -- entry point: loads core modules, bootstraps lazy.nvim
+lua/core/
+ options.lua -- vim options (tabs, search, clipboard, etc.)
+ keymaps.lua -- all non-plugin keymaps
+ autocmds.lua -- filetype detection (yaml.ansible), yank highlight, trim whitespace
+lua/plugins/
+ treesitter.lua -- syntax highlighting (python, bash, yaml, hcl, etc.)
+ lsp.lua -- mason + mason-lspconfig + nvim-lspconfig (all servers)
+ completion.lua -- nvim-cmp + LuaSnip + friendly-snippets
+ autopairs.lua -- nvim-autopairs (auto-close brackets/quotes)
+ lint.lua -- nvim-lint (linting) + conform.nvim (formatting)
+ ui.lua -- catppuccin, lualine, nvim-tree, telescope, gitsigns, which-key
+```
+
+## LSP servers (auto-installed by Mason)
+
+| Language | Server | Notes |
+|---|---|---|
+| Python | `pylsp` | Uses ruff plugin for linting |
+| Bash/Shell | `bashls` | Requires Node |
+| YAML | `yamlls` | Schema store enabled |
+| Ansible | `ansiblels` | Activates on `yaml.ansible` filetype |
+| OpenTofu/Terraform | `terraformls` | Works with both |
+| Lua | `lua_ls` | Configured for Neovim globals |
+
+## Ansible file detection
+
+Files matching `*/tasks/**/*.yml`, `*/handlers/**/*.yml`, `*/roles/**/*.yml`, `*/playbooks/**/*.yml`,
+`*/group_vars/**`, `*/host_vars/**`, `site.yml`, `playbook.yml` are automatically set to the
+`yaml.ansible` filetype, which activates `ansiblels` instead of `yamlls`.
+
+For project-level detection, create a `.ansible-lint` file at the project root.
+
+## Key mappings (leader = Space)
+
+| Key | Action |
+|---|---|
+| `<leader>ff` | Find files (Telescope) |
+| `<leader>fg` | Live grep |
+| `<leader>fb` | Open buffers |
+| `<leader>e` | Toggle file explorer |
+| `<leader>w` | Save |
+| `<leader>q` | Quit |
+| `<leader>cf` | Format file |
+| `<leader>rn` | Rename symbol (LSP) |
+| `<leader>ca` | Code action (LSP) |
+| `<leader>d` | Show diagnostic float |
+| `[d` / `]d` | Prev/next diagnostic |
+| `gd` | Go to definition |
+| `K` | Hover documentation |
+| `<Tab>` | Next completion / jump snippet |
+
+## Adding new language support
+
+1. Add the LSP server name to `ensure_installed` in `lua/plugins/lsp.lua`
+2. Add a `lspconfig.<server>.setup()` call in the same file
+3. Add linter entries to `lint.linters_by_ft` in `lua/plugins/lint.lua`
+4. Add formatter entries to `formatters_by_ft` in `lua/plugins/lint.lua`
+5. Add the treesitter grammar to `ensure_installed` in `lua/plugins/treesitter.lua`
diff --git a/init.lua b/init.lua
@@ -0,0 +1,21 @@
+vim.g.mapleader = " "
+vim.g.maplocalleader = " "
+
+require("core.options")
+require("core.keymaps")
+require("core.autocmds")
+
+-- Bootstrap lazy.nvim
+local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
+if not vim.uv.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)
+
+require("lazy").setup("plugins", {
+ change_detection = { notify = false },
+})
diff --git a/lazy-lock.json b/lazy-lock.json
@@ -0,0 +1,29 @@
+{
+ "LuaSnip": { "branch": "master", "commit": "a62e1083a3cfe8b6b206e7d3d33a51091df25357" },
+ "catppuccin": { "branch": "main", "commit": "426dbebe06b5c69fd846ceb17b42e12f890aedf1" },
+ "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
+ "cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
+ "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
+ "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
+ "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" },
+ "conform.nvim": { "branch": "master", "commit": "dca1a190aa85f9065979ef35802fb77131911106" },
+ "fidget.nvim": { "branch": "main", "commit": "889e2e96edef4e144965571d46f7a77bcc4d0ddf" },
+ "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
+ "gitsigns.nvim": { "branch": "main", "commit": "dd3f588bacbeb041be6facf1742e42097f62165d" },
+ "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
+ "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
+ "lualine.nvim": { "branch": "master", "commit": "131a558e13f9f28b15cd235557150ccb23f89286" },
+ "mason-lspconfig.nvim": { "branch": "main", "commit": "0c2823e0418f3d9230ff8b201c976e84de1cb401" },
+ "mason.nvim": { "branch": "main", "commit": "cb8445f8ce85d957416c106b780efd51c6298f89" },
+ "nvim-autopairs": { "branch": "master", "commit": "59bce2eef357189c3305e25bc6dd2d138c1683f5" },
+ "nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
+ "nvim-lint": { "branch": "master", "commit": "eab58b48eb11d7745c11c505e0f3057165902461" },
+ "nvim-lspconfig": { "branch": "master", "commit": "31026a13eefb20681124706a79fc1df6bf11ab27" },
+ "nvim-tree.lua": { "branch": "master", "commit": "f9bfc0059eca24546b69a7006110463da4808f8f" },
+ "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
+ "nvim-web-devicons": { "branch": "master", "commit": "4fc505ac7bd7692824a142e96e5f529c133862f8" },
+ "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
+ "telescope-fzf-native.nvim": { "branch": "main", "commit": "b25b749b9db64d375d782094e2b9dce53ad53a40" },
+ "telescope.nvim": { "branch": "master", "commit": "f04ab730b8f9c6bf3f54a206d0dcddfd70c52d59" },
+ "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
+}
diff --git a/lua/core/autocmds.lua b/lua/core/autocmds.lua
@@ -0,0 +1,33 @@
+-- Detect Ansible YAML files and set yaml.ansible filetype
+-- so ansiblels activates instead of (or alongside) yamlls
+vim.api.nvim_create_autocmd({ "BufNewFile", "BufRead" }, {
+ pattern = {
+ "*/tasks/**/*.yml", "*/tasks/**/*.yaml",
+ "*/handlers/**/*.yml", "*/handlers/**/*.yaml",
+ "*/roles/**/*.yml", "*/roles/**/*.yaml",
+ "*/playbooks/**/*.yml", "*/playbooks/**/*.yaml",
+ "*/group_vars/**", "*/host_vars/**",
+ "**/site.yml", "**/site.yaml",
+ "**/playbook.yml", "**/playbook.yaml",
+ },
+ callback = function()
+ vim.bo.filetype = "yaml.ansible"
+ end,
+})
+
+-- Highlight on yank
+vim.api.nvim_create_autocmd("TextYankPost", {
+ callback = function()
+ vim.highlight.on_yank()
+ end,
+})
+
+-- Remove trailing whitespace on save
+vim.api.nvim_create_autocmd("BufWritePre", {
+ pattern = { "*.py", "*.sh", "*.yml", "*.yaml", "*.tf", "*.lua" },
+ callback = function()
+ local pos = vim.api.nvim_win_get_cursor(0)
+ vim.cmd([[%s/\s\+$//e]])
+ vim.api.nvim_win_set_cursor(0, pos)
+ end,
+})
diff --git a/lua/core/keymaps.lua b/lua/core/keymaps.lua
@@ -0,0 +1,47 @@
+local map = vim.keymap.set
+
+-- Split navigation
+map("n", "<C-h>", "<C-w>h")
+map("n", "<C-l>", "<C-w>l")
+map("n", "<C-j>", "<C-w>j")
+map("n", "<C-k>", "<C-w>k")
+
+-- Natural line movement
+map({ "n", "x" }, "j", "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true })
+map({ "n", "x" }, "k", "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true })
+
+-- Escape clears search highlight
+map("n", "<Esc>", "<cmd>nohlsearch<CR>")
+
+-- File ops
+map("n", "<leader>w", "<cmd>write<CR>", { desc = "Save" })
+map("n", "<leader>q", "<cmd>quit<CR>", { desc = "Quit" })
+
+-- Buffer navigation
+map("n", "<S-h>", "<cmd>bprevious<CR>", { desc = "Prev buffer" })
+map("n", "<S-l>", "<cmd>bnext<CR>", { desc = "Next buffer" })
+
+-- Stay in visual mode after indent
+map("v", "<", "<gv")
+map("v", ">", ">gv")
+
+-- Move lines
+map("n", "<A-j>", "<cmd>m .+1<CR>==")
+map("n", "<A-k>", "<cmd>m .-2<CR>==")
+map("v", "<A-j>", ":m '>+1<CR>gv=gv")
+map("v", "<A-k>", ":m '<-2<CR>gv=gv")
+
+-- Telescope
+map("n", "<leader>ff", "<cmd>Telescope find_files<CR>", { desc = "Find files" })
+map("n", "<leader>fg", "<cmd>Telescope live_grep<CR>", { desc = "Live grep" })
+map("n", "<leader>fb", "<cmd>Telescope buffers<CR>", { desc = "Buffers" })
+map("n", "<leader>fr", "<cmd>Telescope oldfiles<CR>", { desc = "Recent files" })
+
+-- Diagnostics
+map("n", "<leader>d", vim.diagnostic.open_float, { desc = "Show diagnostic" })
+map("n", "[d", vim.diagnostic.goto_prev, { desc = "Prev diagnostic" })
+map("n", "]d", vim.diagnostic.goto_next, { desc = "Next diagnostic" })
+map("n", "<leader>dl", vim.diagnostic.setloclist, { desc = "Diagnostic list" })
+
+-- File explorer
+map("n", "<leader>e", "<cmd>NvimTreeToggle<CR>", { desc = "Explorer" })
diff --git a/lua/core/options.lua b/lua/core/options.lua
@@ -0,0 +1,45 @@
+local opt = vim.opt
+
+opt.number = true
+opt.relativenumber = true
+
+opt.tabstop = 2
+opt.shiftwidth = 2
+opt.expandtab = true
+opt.autoindent = true
+opt.smartindent = true
+
+opt.wrap = false
+opt.scrolloff = 8
+
+opt.ignorecase = true
+opt.smartcase = true
+opt.hlsearch = false
+opt.incsearch = true
+
+opt.termguicolors = true
+opt.signcolumn = "yes"
+opt.cursorline = true
+opt.updatetime = 250
+opt.timeoutlen = 300
+
+opt.mouse = "a"
+opt.clipboard = "unnamedplus"
+opt.splitbelow = true
+opt.splitright = true
+opt.undofile = true
+opt.swapfile = false
+opt.backup = false
+
+opt.completeopt = "menu,menuone,noselect"
+opt.encoding = "utf-8"
+opt.fileencoding = "utf-8"
+
+-- Python indentation (PEP 8)
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "python",
+ callback = function()
+ vim.opt_local.tabstop = 4
+ vim.opt_local.shiftwidth = 4
+ end,
+})
diff --git a/lua/plugins/autopairs.lua b/lua/plugins/autopairs.lua
@@ -0,0 +1,23 @@
+return {
+ {
+ "windwp/nvim-autopairs",
+ event = "InsertEnter",
+ dependencies = { "hrsh7th/nvim-cmp" },
+ config = function()
+ require("nvim-autopairs").setup({
+ check_ts = true, -- use treesitter to avoid pairing inside strings/comments
+ ts_config = {
+ python = { "string", "comment" },
+ bash = { "string" },
+ },
+ fast_wrap = {
+ map = "<M-e>", -- Alt-e wraps the word under cursor in a pair
+ },
+ })
+
+ -- Auto-insert closing bracket when confirming a completion item
+ local cmp_autopairs = require("nvim-autopairs.completion.cmp")
+ require("cmp").event:on("confirm_done", cmp_autopairs.on_confirm_done())
+ end,
+ },
+}
diff --git a/lua/plugins/completion.lua b/lua/plugins/completion.lua
@@ -0,0 +1,92 @@
+return {
+ {
+ "hrsh7th/nvim-cmp",
+ event = "InsertEnter",
+ dependencies = {
+ "hrsh7th/cmp-nvim-lsp",
+ "hrsh7th/cmp-buffer",
+ "hrsh7th/cmp-path",
+ "hrsh7th/cmp-cmdline",
+ "L3MON4D3/LuaSnip",
+ "saadparwaiz1/cmp_luasnip",
+ "rafamadriz/friendly-snippets", -- snippet collection for many languages
+ },
+ config = function()
+ local cmp = require("cmp")
+ local luasnip = require("luasnip")
+
+ require("luasnip.loaders.from_vscode").lazy_load()
+
+ cmp.setup({
+ snippet = {
+ expand = function(args)
+ luasnip.lsp_expand(args.body)
+ end,
+ },
+
+ mapping = cmp.mapping.preset.insert({
+ ["<C-b>"] = cmp.mapping.scroll_docs(-4),
+ ["<C-f>"] = cmp.mapping.scroll_docs(4),
+ ["<C-Space>"] = cmp.mapping.complete(),
+ ["<C-e>"] = cmp.mapping.abort(),
+ ["<CR>"] = cmp.mapping.confirm({ select = false }),
+
+ ["<Tab>"] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_next_item()
+ elseif luasnip.expand_or_jumpable() then
+ luasnip.expand_or_jump()
+ else
+ fallback()
+ end
+ end, { "i", "s" }),
+
+ ["<S-Tab>"] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_prev_item()
+ elseif luasnip.jumpable(-1) then
+ luasnip.jump(-1)
+ else
+ fallback()
+ end
+ end, { "i", "s" }),
+ }),
+
+ sources = cmp.config.sources({
+ { name = "nvim_lsp" },
+ { name = "luasnip" },
+ { name = "buffer" },
+ { name = "path" },
+ }),
+
+ formatting = {
+ format = function(entry, item)
+ local source_labels = {
+ nvim_lsp = "[LSP]",
+ luasnip = "[Snip]",
+ buffer = "[Buf]",
+ path = "[Path]",
+ }
+ item.menu = source_labels[entry.source.name] or ""
+ return item
+ end,
+ },
+ })
+
+ -- Completion in search
+ cmp.setup.cmdline({ "/", "?" }, {
+ mapping = cmp.mapping.preset.cmdline(),
+ sources = { { name = "buffer" } },
+ })
+
+ -- Completion in command line
+ cmp.setup.cmdline(":", {
+ mapping = cmp.mapping.preset.cmdline(),
+ sources = cmp.config.sources({
+ { name = "path" },
+ { name = "cmdline" },
+ }),
+ })
+ end,
+ },
+}
diff --git a/lua/plugins/lint.lua b/lua/plugins/lint.lua
@@ -0,0 +1,47 @@
+return {
+ -- Async linting
+ {
+ "mfussenegger/nvim-lint",
+ event = { "BufWritePost", "BufReadPost", "InsertLeave" },
+ config = function()
+ local lint = require("lint")
+
+ lint.linters_by_ft = {
+ python = { "ruff" },
+ sh = { "shellcheck" },
+ bash = { "shellcheck" },
+ yaml = { "yamllint" },
+ ["yaml.ansible"] = { "ansible_lint" },
+ terraform = { "tflint" },
+ }
+
+ vim.api.nvim_create_autocmd({ "BufWritePost", "InsertLeave", "BufReadPost" }, {
+ callback = function()
+ lint.try_lint()
+ end,
+ })
+ end,
+ },
+
+ -- Formatting
+ {
+ "stevearc/conform.nvim",
+ event = "BufWritePre",
+ cmd = "ConformInfo",
+ opts = {
+ formatters_by_ft = {
+ python = { "ruff_format" },
+ sh = { "shfmt" },
+ bash = { "shfmt" },
+ yaml = { "prettier" },
+ lua = { "stylua" },
+ terraform = { "terraform_fmt" },
+ ["yaml.ansible"] = { "prettier" },
+ },
+ format_on_save = {
+ timeout_ms = 500,
+ lsp_fallback = true,
+ },
+ },
+ },
+}
diff --git a/lua/plugins/lsp.lua b/lua/plugins/lsp.lua
@@ -0,0 +1,93 @@
+return {
+ { "williamboman/mason.nvim", opts = {} },
+
+ -- Kept as a dep: mason-lspconfig uses it for server cmd/filetype definitions.
+ -- We do NOT call require('lspconfig').server.setup() — that API is deprecated
+ -- in Neovim 0.11+. Configuration goes through vim.lsp.config() below.
+ { "neovim/nvim-lspconfig" },
+
+ {
+ "williamboman/mason-lspconfig.nvim",
+ dependencies = {
+ "williamboman/mason.nvim",
+ "neovim/nvim-lspconfig",
+ "hrsh7th/cmp-nvim-lsp",
+ },
+ config = function()
+ -- Keymaps set once per buffer when any LSP server attaches
+ vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(args)
+ local bufnr = args.buf
+ local m = function(keys, func, desc)
+ vim.keymap.set("n", keys, func, { buffer = bufnr, desc = desc })
+ end
+ m("gd", vim.lsp.buf.definition, "Go to definition")
+ m("gD", vim.lsp.buf.declaration, "Go to declaration")
+ m("gr", vim.lsp.buf.references, "References")
+ m("gi", vim.lsp.buf.implementation, "Go to implementation")
+ m("K", vim.lsp.buf.hover, "Hover docs")
+ m("<leader>rn", vim.lsp.buf.rename, "Rename")
+ m("<leader>ca", vim.lsp.buf.code_action, "Code action")
+ m("<leader>cf", function()
+ require("conform").format({ async = true, lsp_fallback = true })
+ end, "Format file")
+ end,
+ })
+
+ -- Apply nvim-cmp capabilities to every server
+ vim.lsp.config("*", {
+ capabilities = require("cmp_nvim_lsp").default_capabilities(),
+ })
+
+ -- Per-server settings via native vim.lsp.config (Neovim 0.11+)
+ vim.lsp.config("pylsp", {
+ settings = {
+ pylsp = {
+ plugins = {
+ ruff = { enabled = true },
+ pyflakes = { enabled = false },
+ pycodestyle = { enabled = false },
+ mccabe = { enabled = false },
+ },
+ },
+ },
+ })
+
+ vim.lsp.config("yamlls", {
+ filetypes = { "yaml" }, -- ansiblels handles yaml.ansible
+ settings = {
+ yaml = {
+ validate = true,
+ schemaStore = { enable = true },
+ },
+ },
+ })
+
+ vim.lsp.config("ansiblels", {
+ filetypes = { "yaml.ansible" },
+ })
+
+ vim.lsp.config("lua_ls", {
+ settings = {
+ Lua = {
+ diagnostics = { globals = { "vim" } },
+ workspace = { checkThirdParty = false },
+ telemetry = { enable = false },
+ },
+ },
+ })
+
+ -- Install servers; mason-lspconfig calls vim.lsp.enable() for each one
+ require("mason-lspconfig").setup({
+ ensure_installed = {
+ "pylsp", -- Python
+ "bashls", -- Bash/Shell (requires node)
+ "yamlls", -- YAML (requires node)
+ "ansiblels", -- Ansible (requires node)
+ "terraformls", -- OpenTofu / Terraform
+ "lua_ls", -- Lua
+ },
+ })
+ end,
+ },
+}
diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua
@@ -0,0 +1,17 @@
+return {
+ {
+ "nvim-treesitter/nvim-treesitter",
+ build = ":TSUpdate",
+ -- Neovim 0.12 bundles parsers for: bash, lua, python, markdown, vim, vimdoc.
+ -- nvim-treesitter only needs to install what's missing.
+ -- Requires tree-sitter-cli: sudo pacman -S tree-sitter-cli
+ config = function()
+ require("nvim-treesitter").install({
+ "yaml",
+ "toml",
+ "json",
+ "hcl", -- Terraform / OpenTofu .tf files
+ })
+ end,
+ },
+}
diff --git a/lua/plugins/ui.lua b/lua/plugins/ui.lua
@@ -0,0 +1,62 @@
+return {
+ -- Colorscheme
+ {
+ "catppuccin/nvim",
+ name = "catppuccin",
+ priority = 1000,
+ config = function()
+ vim.cmd.colorscheme("catppuccin-mocha")
+ end,
+ },
+
+ -- Statusline
+ {
+ "nvim-lualine/lualine.nvim",
+ dependencies = { "nvim-tree/nvim-web-devicons", "catppuccin/nvim" },
+ opts = { options = { theme = "catppuccin" } },
+ },
+
+ -- File explorer
+ {
+ "nvim-tree/nvim-tree.lua",
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ opts = {
+ view = { width = 30 },
+ renderer = { group_empty = true },
+ },
+ },
+
+ -- Fuzzy finder
+ {
+ "nvim-telescope/telescope.nvim",
+ dependencies = {
+ "nvim-lua/plenary.nvim",
+ { "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
+ },
+ config = function()
+ local telescope = require("telescope")
+ telescope.setup({})
+ telescope.load_extension("fzf")
+ end,
+ },
+
+ -- LSP loading indicator (bottom-right)
+ { "j-hui/fidget.nvim", opts = {} },
+
+ -- Indent guides
+ {
+ "lukas-reineke/indent-blankline.nvim",
+ main = "ibl",
+ opts = {},
+ },
+
+ -- Git decorations in the sign column
+ { "lewis6991/gitsigns.nvim", opts = {} },
+
+ -- Popup showing available keybindings after pressing leader
+ {
+ "folke/which-key.nvim",
+ event = "VeryLazy",
+ opts = {},
+ },
+}