Fransys

Tech blog — Architecture, Cloud & DevOps

BlogServicesContactAbout

Follow me

githubGitHublinkedinLinkedinmailMail

© 2026 Fransys • Fransys

Fransys

Categories

  • All posts
  • Tags
  • productivity10
  • nas10
  • ai8
  • security7
  • self-hosting7
  • linux6
  • claude-code6
  • neovim5
  • docker5
  • editor4
  • networking4
  • mcp3
  • vpn3
  • lua2
  • terminal2
neovimluaeditorproductivity

Telescope, Treesitter and essential coding plugins for Neovim

Published on
February 6, 2026·7 min read
Avatar François GUERLEZFrançois GUERLEZ

Beyond the LSP

So, LSP is good - it's the base layer. But that alone? Not enough. To build a real development environment, you need plugins that handle search, code manipulation, Git. The essentials, basically. I'm going to walk you through the ones I actually use every single day, and honestly, I'd be lost without them.

Telescope: the hunt tool

Telescope is my Swiss Army knife. The plugin I probably use 10 times a day. It's a fuzzy finder - you point it at files, text, buffers, help docs, literally everything you can think of searching for, and it finds it from a single, unified interface.

Performance with fzf-native

Look, out of the box Telescope... it's not snappy on a huge codebase. Thousands of files? You feel it. I installed telescope-fzf-native - it's a C binary that completely replaces the search algorithm. The difference? Night and day. Literally.

The keybindings (the ones you actually press)

local builtin = require("telescope.builtin")

vim.keymap.set("n", "<leader>ff", builtin.find_files, { desc = "Find files" })
vim.keymap.set("n", "<leader>fg", builtin.live_grep, { desc = "Live grep" })
vim.keymap.set("n", "<leader>fb", builtin.buffers, { desc = "Buffers" })
vim.keymap.set("n", "<leader>fh", builtin.help_tags, { desc = "Help tags" })
vim.keymap.set("n", "<leader>fr", builtin.oldfiles, { desc = "Recent files" })
vim.keymap.set("n", "<leader>fw", builtin.grep_string, { desc = "Grep word under cursor" })
vim.keymap.set("n", "<leader>/", builtin.current_buffer_fuzzy_find, { desc = "Fuzzy find in buffer" })

You're mostly using <leader>ff to jump to a file, <leader>fg to scan the entire project for text. And <leader>/? That's my killer move - search within the current buffer. It's 100x better than vanilla /.

Configuration and filters (pro tip: ignore patterns are everything)

require("telescope").setup({
  defaults = {
    path_display = { "truncate" },
    file_ignore_patterns = {
      "node_modules/",
      ".git/",
      ".next/",
      "dist/",
      "%.lock",
      "%.min%.js",
      "%.min%.css",
    },
    mappings = {
      i = {
        ["<C-j>"] = require("telescope.actions").move_selection_next,
        ["<C-k>"] = require("telescope.actions").move_selection_previous,
        ["<C-q>"] = require("telescope.actions").send_selected_to_qflist
          + require("telescope.actions").open_qflist,
      },
    },
  },
})

Here's what actually matters:

  • Ignore patterns : I've blacklisted node_modules, .git, .next, dist, lock files, and minified assets. On a Next.js project, without this, you'd get 50,000 garbage results. No thanks.
  • Truncated path display : Long paths are noise. Truncating them makes the UI cleaner.
  • C-j/C-k in insert mode : Navigate without leaving insert mode. Small thing, massive quality-of-life improvement.
  • C-q : Sends results to quickfix. Invaluable when you need to do a bulk search-and-replace across multiple files.

The preview pane? It uses Treesitter for syntax highlighting. Not regex voodoo - actual parsing.

Treesitter: the real deal

Treesitter - it's not just a syntax highlighter (if it were, I'd be disappointed). It's an actual incremental parser that understands code structure. It knows where functions start and end, where comments are, how blocks nest. Not primitive regex - real AST parsing.

18 parsers that cover everything I do

require("nvim-treesitter.configs").setup({
  ensure_installed = {
    "bash", "c", "css", "dockerfile", "go", "html",
    "javascript", "json", "lua", "markdown", "markdown_inline",
    "python", "rust", "tsx", "typescript", "vim", "vimdoc", "yaml",
  },
  auto_install = true,
  highlight = { enable = true },
  indent = { enable = true },
})

18 total. Bash, C, CSS, Docker, Go, HTML, JavaScript, JSON, Lua, Markdown, Python, Rust, TypeScript, Vim, YAML. That's like 95% of what I code in. New language? auto_install = true means it just installs itself. Magic.

Autotag for HTML/JSX (genuinely a game-changer in React)

nvim-ts-autotag - you close an HTML tag, Treesitter closes it for you automatically. Better yet: you rename the opening tag? The closing tag updates too. Before this, I'd leave tags hanging open. Now? Impossible.

Tailwind CSS (three separate plugins because why not)

Frontend work? I've got three Tailwind plugins running:

  • tailwind-tools.nvim : Color preview right in the buffer. Long class lists are "concealed" for readability. Class order? Auto-sorted. Pure luxury.

Editing essentials

nvim-autopairs

{
  "windwp/nvim-autopairs",
  event = "InsertEnter",
  config = function()
    local npairs = require("nvim-autopairs")
    npairs.setup({
      check_ts = true,
    })
    -- Integration with nvim-cmp
    local cmp_autopairs = require("nvim-autopairs.completion.cmp")
    require("cmp").event:on("confirm_done", cmp_autopairs.on_confirm_done())
  end,
}

Auto-closing parens, brackets, braces, quotes - everything that needs closing. But here's the clever bit: check_ts = true asks Treesitter first. No stray parens in comments, no quote disasters in strings. Smart. And cmp integration? When you accept a completion, the pairs close too.

Comment.nvim (comment code without thinking)

{
  "numToStr/Comment.nvim",
  opts = {},
}

gcc toggles a line between commented and uncommented. gc plus a motion? gcap comments an entire paragraph. gc3j comments 3 lines. The plugin knows the comment syntax for the language. No thinking required.

nvim-surround (manipulate the "wrappers")

{
  "kylechui/nvim-surround",
  version = "*",
  event = "VeryLazy",
  opts = {},
}

Have a word you want to wrap in quotes? Parentheses? Backticks? This is the tool.

  • ys{motion}{char} - add a wrapper. ysiw" wraps the word under the cursor in quotes.
  • cs{old}{new} - change. cs"' replaces double quotes with singles. I do this constantly.
  • ds{char} - delete. ds( removes the surrounding parens.

todo-comments.nvim (because we leave TODOs everywhere)

{
  "folke/todo-comments.nvim",
  event = "VimEnter",
  dependencies = { "nvim-lua/plenary.nvim" },
  opts = { signs = false },
}

You scatter TODO and FIXME throughout your code? This plugin highlights them in distinct colors. TODO in blue, FIXME in red, BUG in orange. You see them and remember to fix them.

Formatting with Conform.nvim (never do this manually)

Automatic formatting is handled by Conform.nvim:

{
  "stevearc/conform.nvim",
  event = "BufWritePre",
  config = function()
    require("conform").setup({
      formatters_by_ft = {
        javascript = { "prettier" },
        typescript = { "prettier" },
        typescriptreact = { "prettier" },
        javascriptreact = { "prettier" },
        css = { "prettier" },
        html = { "prettier" },
        json = { "prettier" },
        yaml = { "prettier" },
        markdown = { "prettier" },
        lua = { "stylua" },
        python = { "ruff_format" },
      },
      format_on_save = {
        timeout_ms = 3000,
        lsp_format = "fallback",
      },
    })

    vim.keymap.set({ "n", "v" }, "<leader>cf", function()
      require("conform").format({ async = true, lsp_format = "fallback" })
    end, { desc = "Format file or selection" })
  end,
}

Here's the strategy:

  • Prettier for all web stuff - JS, TS, CSS, HTML, JSON, YAML, Markdown. I spent 2 hours debugging this the first time, but it's bulletproof now.
  • Stylua for Lua (including my Neovim config). Because even your editor should be formatted correctly.
  • ruff_format for Python (faster than black and looks like actual code).
  • Format on save with a 3-second timeout. Takes too long? Falls back to LSP.
  • LSP fallback - if nothing else works, ask the LSP to format.
  • <leader>cf for manual formatting (rare, but useful for debugging).

Mason installs the binaries automatically via mason-conform. I've never had to install them by hand.

Gitsigns: seeing Git in the margin (it's beautiful, trust me)

Gitsigns displays Git changes directly in the sign column, line by line. Little colored blocks showing what changed.

{
  "lewis6991/gitsigns.nvim",
  opts = {
    on_attach = function(bufnr)
      local gitsigns = require("gitsigns")
      local map = function(mode, l, r, opts)
        opts = opts or {}
        opts.buffer = bufnr
        vim.keymap.set(mode, l, r, opts)
      end

      -- Jump between changes
      map("n", "]h", gitsigns.next_hunk, { desc = "Next hunk" })
      map("n", "[h", gitsigns.prev_hunk, { desc = "Previous hunk" })

      -- Actions on hunks
      map("n", "<leader>hs", gitsigns.stage_hunk, { desc = "Stage hunk" })
      map("n", "<leader>hr", gitsigns.reset_hunk, { desc = "Reset hunk" })
      map("n", "<leader>hp", gitsigns.preview_hunk, { desc = "Preview hunk" })
      map("n", "<leader>hb", gitsigns.blame_line, { desc = "Blame line" })
    end,
  },
}

The symbols in the gutter? They say "added", "changed", "deleted". The keybindings let you jump between hunks (]h / [h), stage/reset individual hunks, preview the diff before committing, or see who modified that line (blame). All without leaving Neovim.

The bottom line

That's the arsenal. Telescope for instant searching. Treesitter for deep code understanding. Conform that formats everything cleanly without being asked. Gitsigns showing you the history in the margin. Not flashy plugins - each does one thing and does it right. That's the Unix philosophy I love about Neovim.


Neovim from scratch series — This article is part of a complete series on configuring Neovim from scratch.

Previous: Native LSP in Neovim 0.11: zero plugins, zero compromises | Next: Terminal, Git and global search integrated in Neovim

Previous post

← Migrating Synology data to a Debian NAS without losing anything

Next post

Docker on NAS: network architecture and best practices→
← Back to blog

Table of Contents

  • Beyond the LSP
  • Telescope: the hunt tool
  • Performance with fzf-native
  • The keybindings (the ones you actually press)
  • Configuration and filters (pro tip: ignore patterns are everything)
  • Treesitter: the real deal
  • 18 parsers that cover everything I do
  • Autotag for HTML/JSX (genuinely a game-changer in React)
  • Tailwind CSS (three separate plugins because why not)
  • Editing essentials
  • nvim-autopairs
  • Comment.nvim (comment code without thinking)
  • nvim-surround (manipulate the "wrappers")
  • todo-comments.nvim (because we leave TODOs everywhere)
  • Formatting with Conform.nvim (never do this manually)
  • Gitsigns: seeing Git in the margin (it's beautiful, trust me)
  • The bottom line