Taming AI Code Bloat: Adding Line Counts to Nvim-Tree
Tools like GitHub Copilot and modern LLMs have dramatically accelerated development velocity. With tab completions, automated boilerplate generation, and rapid prototyping, writing code has never been faster. However, this velocity introduces a subtle software architecture anti-pattern: silent file bloat.
Before AI assistance, writing 2,000 lines of code in a single file took deliberate effort and plenty of typing fatigue, giving developers time to pause and say, "This file is getting out of hand; I need to split this into smaller modules." Today, an AI can generate hundreds of lines in seconds. Without visual feedback in our project navigation, a once-compact module can swell to 1,500 or 2,000 lines before anyone notices during a pull request review.
To tackle this architectural drift directly at the source, I added a custom decorator to nvim-tree.lua that displays the line count directly next to every filename in the file tree.
Why Visual File Metrics Matter
File trees usually display git status, file icons, and bookmarks. While useful, none of these indicators answer the fundamental architectural question: "Which parts of my codebase are becoming monoliths?"
By placing line counts right beside file names:
- Bloated files stand out immediately: A file with (1840) lines catches the eye far quicker than one with (65) lines.
- Refactoring becomes proactive: Instead of waiting for a file to become unmaintainable, I identify candidates for decomposition while simply navigating my project.
- AI generation stays grounded: It serves as a continuous reminder to prompt AI tools for modular separation rather than appending more functions to existing files.
Building the Custom Line Count Decorator
nvim-tree.lua provides an extensible decorator API (require("nvim-tree.api").Decorator). By subclassing this base class, we can append custom annotations and icons after any tree node.
Here is the implementation of the decorator in lua/configs/nvim-tree/decorators/line_count.lua:
local api = require("nvim-tree.api")
local LineCountDecorator = api.Decorator:extend()
function LineCountDecorator:new()
self.enabled = true
self.highlight_range = "none"
self.icon_placement = "after" -- places the annotation after the filename
end
local function count_lines(path)
-- Use loaded buffer line count if available to avoid disk I/O
local bufnr = vim.fn.bufnr(path)
if bufnr ~= -1 and vim.api.nvim_buf_is_loaded(bufnr) then
return vim.api.nvim_buf_line_count(bufnr)
end
-- Fall back to reading the file from disk safely
local ok, lines = pcall(vim.fn.readfile, path)
return ok and #lines or nil
end
function LineCountDecorator:icons(node)
if node.type ~= "file" then
return nil
end
local count = count_lines(node.absolute_path)
if not count then
return nil
end
return { { str = "(" .. count .. ")", hl = { "Comment" } } }
end
return LineCountDecorator
How It Works
- Inheritance: We inherit from api.Decorator:extend() and enable it with self.icon_placement = "after" so the badge appears immediately after the filename.
- Buffer Awareness: count_lines() first checks if the file is currently loaded in a Neovim buffer using vim.api.nvim_buf_line_count(). This keeps the count updated in real time as you edit, without waiting for disk writes.
- Safe Fallback: If the file is not loaded in memory, it safely reads the file lines via pcall(vim.fn.readfile, path).
- Subtle Styling: The count is wrapped in parentheses and highlighted using the standard Comment highlight group so that it provides vital context without overpowering the tree view.
Registering the Decorator in Nvim-Tree
To activate the decorator, import it and insert it into the renderer.decorators array inside your Nvim-Tree configuration (lua/configs/nvim-tree/init.lua):
local M = {}
local LineCountDecorator = require("configs.nvim-tree.decorators.line_count")
M.opts = {
filters = { dotfiles = false },
disable_netrw = true,
hijack_netrw = true,
git = { enable = true },
view = {
width = 30,
side = "left",
},
renderer = {
highlight_git = true,
icons = {
show = {
file = true,
folder = true,
folder_arrow = true,
git = true,
},
},
decorators = {
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
LineCountDecorator,
"Cut",
},
},
actions = {
open_file = {
quit_on_open = false,
resize_window = true,
},
},
}
function M.setup()
require("nvim-tree").setup(M.opts)
end
return M
The Result
When expanding directories in Nvim-Tree, every file entry clearly indicates its size:
▾ controllers/
├── auth_controller.lua (92)
├── dashboard_controller.lua (1420)
└── user_controller.lua (115)
At a glance, dashboard_controller.lua immediately signals a code smell. Before running a git commit, I can promptly plan its decomposition into smaller services or helper modules.
Conclusion
AI tooling provides immense power, but our editor environment must evolve to keep our codebases structured and clean. Adding custom line count decorators to nvim-tree.lua is a lightweight, low-overhead enhancement that restores architectural awareness right where we navigate code every day.