doc/insights.txt — rendered from the plugin's own vimdoc
*insights.txt* Project analysis: symbols, metrics, tree, fileinfo. *insights.nvim*
CONTENTS
1. Introduction ................. |insights-intro| 2. Requirements ................. |insights-requirements| 3. Installation ................. |insights-installation| 4. Commands ..................... |insights-commands| 4.1 Symbols .................. |insights-symbols| 4.2 Metrics .................. |insights-metrics| 4.3 Tree ..................... |insights-tree| 4.4 File info ................ |insights-fileinfo| 4.5 Cache .................... |insights-cache| 4.6 Compress ................. |insights-compress| 4.7 Imports .................. |insights-imports| 4.8 Conflicts ................ |insights-conflicts| 4.9 Unimported ............... |insights-unimported| 4.10 Devserver ............... |insights-devserver| 4.11 Smells ................... |insights-smells| 5. Automatic triggers ........... |insights-autocmds| 6. Configuration ................ |insights-config| 6.1 symbols .................. |insights-config-symbols| 6.2 metrics .................. |insights-config-metrics| 6.3 tree ..................... |insights-config-tree| 6.4 fileinfo ................. |insights-config-fileinfo| 6.5 keymaps .................. |insights-config-keymaps| 6.6 compress ................. |insights-config-compress| 6.7 imports .................. |insights-config-imports| 6.8 conflicts ................ |insights-config-conflicts| 6.9 unimported ............... |insights-config-unimported| 6.10 devserver ............... |insights-config-devserver| 7. Symbol types ................. |insights-symbol-types| 7.1 Function types ........... |insights-symbol-functions| 7.2 Lua TS types ............. |insights-symbol-lua-ts| 8. Pickers ...................... |insights-pickers| 9. Lua API ...................... |insights-api| 10. Health check ................. |insights-health| 11. Troubleshooting .............. |insights-troubleshooting|
1. INTRODUCTION
insights.nvim is a project-analysis plugin that combines several
previously separate tools into a single unified interface:
• Symbol index — ripgrep-based function/method finder across 11 languages,
with optional Tree-sitter Lua scanner and persistent cache.
• Code metrics — Lua file statistics: lines, comments, annotations, word
counts, and ratio analysis per file and folder.
• Code smells — magic-number and hardcoded-(unconfigured)-constant scans.
• Imports — multi-language import/require usage report, reverse
lookup, unused-import detection, dependency graph.
• File tree — Async project file tree writer, file counter, and
clipboard copy.
• File info — Floating window with filesystem metadata (fs.stat)
for the current buffer.
• Compress — Archive a project directory (tar/zip/PowerShell).
• Automatic checks — git conflicts, unused imports, and dev servers, each
switched off independently.
All subcommands are exposed under a single unified command:
:Insights <subcommand> [args]
Tab-completion works at every level.
2. REQUIREMENTS
• Neovim ≥ 0.9 (required)
• lib.nvim (required, shared notify + cross-platform helpers,
ui.kit for the dev-server prompt)
• rg (ripgrep) (required for symbol indexing)
• git (optional, for the conflict scan)
• telescope.nvim (optional, for telescope symbol picker)
• fzf-lua (optional, for fzf symbol picker)
• nvim-treesitter (optional, for Tree-sitter Lua scanner)
On Windows the file tree uses PowerShell; on Unix it uses find + sed.
3. INSTALLATION
insights.nvim is lazy by design; load it oncmd = "Insights". *insights-lazy-warning* Exception: the automatic triggers (|insights-autocmds|) are registered bysetup(), so lazy-loading oncmdmeans their autocmds never fire — nothing registers until:Insightsis run by hand. If you useconflicts,unimported, ordevserver, load the plugin at startup (lazy = false) instead. Keepcmd = "Insights"only when all three areenable = false. lazy.nvim:
{
"StefanBartl/insights.nvim",
dependencies = { "StefanBartl/lib.nvim" },
cmd = "Insights", -- lazy = false if you use the autocmds
keys = {
{ "<leader>ps", desc = "Project symbols (telescope)" },
{ "<leader>pS", desc = "Project symbols (fzf)" },
},
config = function()
require("insights").setup()
end,
}
packer.nvim:
use {
"StefanBartl/insights.nvim",
requires = { "StefanBartl/lib.nvim" },
cmd = "Insights",
config = function()
require("insights").setup()
end,
}
vim-plug:
Plug 'StefanBartl/lib.nvim'
Plug 'StefanBartl/insights.nvim'
" after plug#end()
require("insights").setup()
4. COMMANDS
*:Insights* :Insights {subcommand} [args] Unified dispatcher. {subcommand} is one of: symbols, metrics, smells, tree, count, clipboard, fileinfo, cache, compress, imports, conflicts, unimported, devserver. Tab-completion is available at every argument position. Built vialib.nvim.bindings.usercmd.composer— dispatch and completion are driven from one route tree, forwarding to the same handler functions as before this migration. An unknown {subcommand} now reports composer's own usage block (every registered command, one per line).metrics' flags only complete once the leading--is typed (previously offered alongside directory names at every position).
4.1 Symbols
*:Insights-symbols*
:Insights symbols [scope] [type] [ui] [rebuild]
Build or load the symbol index and open a picker.
[scope] cwd Search the current working directory (default).
buffer Search only the current buffer.
[type] functions Function declarations and assignments (default).
tables Lua table constructor definitions (Tree-sitter).
strings Lua string literals, deduplicated (Tree-sitter).
[ui] telescope Open in telescope.nvim.
fzf Open in fzf-lua.
scratch Open in a read-only scratch buffer.
(unset) Auto-detect: telescope → fzf → scratch.
rebuild Force a cache rebuild before opening the picker.
(Only applies to functions scope; tables/strings are not cached.)
Arguments can appear in any order. Examples:
:Insights symbols
:Insights symbols buffer
:Insights symbols cwd telescope
:Insights symbols rebuild
:Insights symbols fzf rebuild
:Insights symbols buffer tables
:Insights symbols cwd strings
:Insights symbols buffer tables scratch
Note:tablesandstringsrequire nvim-treesitter with theluaparser (:TSInstall lua). They work on Lua files only. Picker keymaps (telescope / fzf): <Enter> Jump to the symbol definition. <C-p> Toggle preview (telescope only). Scratch buffer keymaps: q / <Esc> Close the buffer. gf Followpath:lineon the current line. Display format in the picker:
path/to/file.lua:42 [module] my_function
The label in square brackets is the symbol type (see |insights-symbol-types|).
4.2 Metrics
*:Insights-metrics* :Insights metrics [flags] [dir] Analyze the project under [dir] and display a statistics report in a scratch buffer. The report is also written to the file configured in |insights-config-metrics|. [dir] Directory to analyze. Default: the current working directory. Tab-completion suggests directories and flags. Relative paths and~are expanded. Pass an explicit directory when the editor's cwd differs from the project you want to measure. Report sections (all configurable — see |insights-config-metrics|): • Total / Folder / File tables — lines L1=code, L2=comments, L3=no-annotations, L4=annotations, L5=blank; words W1-W5; shown as counts and/or percentages. • Folder ratios — comment%, annotation%, doc%, code%, avg lines/file, annotation-to-comment ratio, with deviations from the global averages. • Top-N lists — largest files by lines and words; folders by annotation ratio. • Ratio guidelines — heuristic healthy ranges. • Documentation & config files — Markdown / TXT / JSON counts (summary and optional per-file detail). Flags (override the configured defaults for this invocation): --ratios / --no-ratios toggle the ratio analysis --deviations / --no-deviations toggle deviations in the ratio table --lua-only Lua files only (no docs) --misc-only documentation files only (no Lua) --no-misc skip documentation files --misc-detailed per-file listing of documentation files --no-top skip the top-N lists --top-files-lines-only print ONLY the top files by lines --top-files-words-only print ONLY the top files by words --percent-only / --numbers-only value display mode --reverse / --no-reverse summary first vs. files first --topn=N number of items in top-N lists --colwidth=N table column width --file=PATH analyze a single file --current analyze the current buffer Directories named.git,node_modules,.cache,debuglog, anddocsare skipped (matched as whole path segments below the analyzed root). Examples:
:Insights metrics
:Insights metrics --ratios --deviations ~/projects/app
:Insights metrics --lua-only --no-top
:Insights metrics --misc-only --misc-detailed
:Insights metrics --current
4.3 Tree
*:Insights-tree* :Insights tree Write the project file tree to the configured output file (see |insights-config-tree|). Lists all project files relative to cwd, one per line, sorted, excluding configured patterns. *:Insights-count* :Insights count Count the total number of project files (respects exclude_patterns). *:Insights-clipboard* :Insights clipboard Copy the content of the previously written tree file to the system clipboard. Run |:Insights-tree| first if no tree file exists.
4.4 File info
*:Insights-fileinfo*
:Insights fileinfo
Toggle a centered floating window showing filesystem metadata for the
current buffer:
Path, Type, Size, Permissions, UID, GID, Accessed, Modified, Changed.
Calling the command again while the window is open closes it.
Also available via the keymap configured in |insights-config-fileinfo|
(default: <leader>fi).
Keys inside the float:
q / <Esc> Close the window.
4.5 Cache
*:Insights-cache-build* :Insights cache build Rebuild the symbol cache for the current working directory. Runs ripgrep over the project and saves the result to the configured cache directory (see |insights-config-symbols|). *:Insights-cache-info* :Insights cache info Print cache statistics: number of indexed symbols, timestamp, CWD, file size, and cache file path. *:Insights-cache-clear* :Insights cache clear Delete the symbol cache for the current working directory.
4.6 Compress
*:Insights-compress* :Insights compress [path] [outdir] Compress a project directory using the configured engine. Both arguments are optional. [path] Directory to compress. Default: current working directory. Tab-completion suggests directories. [outdir] Output directory override (overrides compress.outdir for this invocation). Tab-completion suggests directories. Two files are always created in the output directory:
compressed/<name>.tar.gz — archive (engine=tar)
compressed/<name>.zip — archive (engine=zip or powershell)
compressed/file-list.txt — all archived paths, one per line
Output directory resolution:
compress.outdir = "" (default) → <path>/compressed/
compress.outdir = "/some/dir" → /some/dir/<name>-compressed/
Available engines:
auto tar on Unix, Compress-Archive on Windows (default)
tar find + tar → .tar.gz (requires tar + find)
zip find + zip → .zip (requires zip + find)
powershell Get-ChildItem + Compress-Archive → .zip (Windows)
Runs asynchronously. A notification is shown on completion (or failure).
Requires compress.enable = true (default).
Examples:
:Insights compress
:Insights compress /home/user/myproject
:Insights compress . ~/backups
4.7 Imports
*:Insights-imports* :Insights imports [filter/lang...] [telescope|fzf|graph] Scan source files under the current working directory for import/require statements across six languages — Lua, Python, JavaScript/TypeScript, Go, Rust, and C/C++ — and display a report in a scratch buffer (default) or a telescope/fzf picker over the occurrence list. The scratch report is also written to the file configured in |insights-config-imports|. [filter] Zero or more module filters. A module matches a filter when it equals the filter or begins withfilter./filter::(prefix on the module hierarchy).libmatcheslib,lib.nvim,lib.usrcmdsbut notmylib. A filter that names a configured group (see |insights-config-imports|) expands to that group's prefix list. Multiple filters are OR-combined. [lang] A language id/alias (lua,python/py,javascript/js/ts,go,rust/rs,c/cpp) scopes the report to just that language. Multiple language tokens are OR-combined; combined with module filters, both conditions apply (AND). [ui]telescopeorfzfopens a picker over the occurrence list instead of the scratch buffer (still writesoutput_file).graphrenders the same filtered data as a Graphviz dependency graph PNG instead — see |insights-imports-graph| below. Tab-completion suggests configured group names, language ids, and the picker tokens. How modules are detected: Lua uses Tree-sitter by default — only genuinerequire("…")calls in the syntax tree are counted, so the wordrequireinside comments or string literals is ignored — with a ripgrep line-scan fallback when the Lua parser is unavailable (selectable viaimports.engine, see |insights-config-imports|). The other five languages use a regex/text scan of each file's source (there is no Tree-sitter query for them yet); this still resolves grouped/multi-line syntax correctly (Goimport ( … )blocks, Pythonfrom x import ( … ), nested Rustuse a::{ … }). The backend used per language is shown in the report header. Report sections: Count Each module with its occurrence count, sorted descending, tagged with its language ([lua],[py],[js],[go],[rs],[c]). A module with no matching local source file is tagged(extern)— e.g.vim,react,fmt. Occurrences Every call as `path:line [lang] module imported-name (.field)`, sorted by language, then module, then file, line. External/local classification per language: Lua no matching.luafile underlua/<path>or<path>(cwd) Python relative imports (from . import x) are always local; absolute ones need a matching<path>.pyor<path>/__init__.pyJS/TS relative/absolute specifiers (./x,/x) are local; bare specifiers (react,@scope/pkg) are npm packages Go local iff the import path matches (or is a subpackage of) the project's own module path ingo.mod; withoutgo.mod, everything is reported external Rustcrate::…/self::…/super::…are local; everything else is an external crate C/C++#include <...>(system) vs.#include "..."(local) — the include form itself decides, no filesystem lookup Scratch buffer keymaps: q / <Esc> Close the buffer. gf Followpath:lineon the current line (the import site). gd Go to the definition behind a Lua import on the current line. gp Preview that definition in a floating window. Go to definition (gd/gp) is currently Lua-only: it resolves the required module to the file that defines it — without executingrequire(...)— and reveals the definition of the accessed field. On an Occurrence line the jump lands on the field's definition (e.g.…notify notify (.create)opensfunction M.create); on a Count line it opens the module file. Field location is Tree-sitter-accurate with a regex fallback. Module resolution searches project-locallua/paths first, then the Neovim loader cache,package.path, and the runtimepath. Placing the cursor on a non-Lua entry and pressinggd/gpjust notifies that it isn't supported yet. The view, float border, and keys are configured under |insights-config-imports|. The scan runs asynchronously and does not block the editor: file discovery usesrg --files-with-matches(or a plain glob when ripgrep is unavailable), then reads + parses matched files in scheduled chunks. The report opens when the scan completes. Examples:
:Insights imports
:Insights imports python
:Insights imports js fzf
:Insights imports lib
:Insights imports insights
:Insights imports lib foo.bar
*:Insights-imports-reverse* :Insights imports reverse {module} Given a module, list every file that imports it — the reverse of the main report.{module}matches by the same exact/prefix rule as the main filters. Opens a scratch buffer listingpath:line [lang] imported-nameper occurrence, grouped implicitly by sorting on file then line. Example:
:Insights imports reverse insights.config
*insights-imports-graph* :Insights imports graph [filter/lang...] Render the same (filtered) import data as a Graphviz dependency graph instead of a text report — every entry is already an edge (filenameimportsmodule), just never drawn as one until this. Nodes: importing files (filled blue); external modules only appear whenimports.graph.include_externalis true (default false — a real project imports far more external modules than it has source files, and drawing them turns the graph into noise instead of showing project structure). Needs Graphviz (the CLI named byimports.graph.layout, default"dot") on PATH — reading a dependency graph out of source text needs a real layout engine, no pure-Lua substitute exists; reported as a clear error rather than a silent no-op when missing. Rendered toimports.graph.outdir/{project}-imports.pngand shown inline through images.nvim (https://github.com/StefanBartl/images.nvim) if installed, else just reported as a file path to open manually. Deliberately scoped to the dependency graph only, the one place in insights.nvim where the data is already graph-shaped. Call-tree and symbol-distribution graphs don't exist as data anywhere else in this plugin (symbolsis a flat, uncorrelated list) — building that analysis from scratch would be a separate, much larger feature. Example:
:Insights imports graph
:Insights imports python graph
*:Insights-imports-unused* :Insights imports unused [filter/lang...] List bound import names that never appear again in their file — a crude textual check (whole-word count of the bound identifier across the file), not a reference analysis. False positives are possible: re-exports via string, reflection, and shadowed names all look "unused" to this heuristic. Blank/wildcard bindings (Go_, a bare*) are always skipped. Accepts the same language/module filters as the main report. Example:
:Insights imports unused python
4.8 Conflicts
*:Insights-conflicts* :Insights conflicts Ask git for files in the unmerged state (git diff --diff-filter=U), put them in the|quickfix|list, and open it with|:copen|. Does nothing outside a git repository or when the repo has no conflicts. Also runs automatically on VimEnter — see |insights-autocmds|. Configure underconflicts(|insights-config-conflicts|).
4.9 Unimported
*:Insights-unimported* :Insights unimported Report component tags used in the current buffer that have no matching import or local definition. A tag whose name starts with an uppercase letter (<Card />) is treated as a component reference; lowercase tags are HTML elements and ignored. A name counts as bound when it is imported (import Card …,import { Card } …) or declared locally (const/let/var/function/class). This is a textual check, not a type-checker: it never reads other files, so it cannot tell whether an import actually resolves. Names that are deliberately never imported (globals, framework injections) belong inunimported.ignore. Runs automatically on BufWritePost for the configured filetypes — see |insights-autocmds|. Configure underunimported(|insights-config-unimported|).
4.10 Devserver
*:Insights-devserver*
:Insights devserver [list|kill]
list List the dev servers tracked in this session, with their pid and
whether they will be killed on exit (default).
kill Kill every tracked dev server now, regardless of the answer given
to the prompt.
Tracking itself is automatic — see |insights-autocmds|. Configure
under devserver (|insights-config-devserver|).
4.11 Smells
*:Insights-smells* :Insights smells [--magic-numbers-only|--constants-only] [dir] Two scans over a project's Lua source, distinct from |insights-metrics|'s size/ratio analysis — candidates, not verdicts, for both. [dir] defaults to the current working directory; tab-completion suggests directories. Magic numbers A number written straight into a call with no name to hold a config key against:vim.defer_fn(fn, 3000),vim.wait(500),timer:start(N, N),timeout = N,vim.o.columns * 0.N,vim.o.lines * 0.N. A defer/wait/timer value of 50 or under is "get off the current tick", not a preference, and is never flagged. Hardcoded A module-levellocal NAME = VALUEwhose name describes constants behaviour (timeout, delay, limit, width, count, …) and whose value isSCREAMING_CASEor a plain integer other than 0/1, but which never made it into the project's own config surface (any file underconfig//@types/, or named*defaults*/config/init.lua). --magic-numbers-only Skip the hardcoded-constants scan. --constants-only Skip the magic-numbers scan. Opens the report in a scratch buffer. Has nosetup()config of its own — each run is scoped by its flags/directory argument only. Example:
:Insights smells
:Insights smells --magic-numbers-only
5. AUTOMATIC TRIGGERS
Most of the plugin only acts when asked. Three features also run on their own. Each registers its autocmds insetup()and is switched off with itsenablekey; a disabled feature registers nothing. conflicts VimEnter quickfix unresolved conflicts unimported BufWritePost check component imports devserver TermOpen, TermRequest, detect and kill dev servers VimLeavePre Bothconflictsandunimportedare silent when they find nothing, so a clean project produces no messages at startup or on write. *insights-devserver-tracking*
Dev-server tracking
When a terminal's command matches one ofdevserver.patterns(npm run dev,astro dev,vite, …), a prompt (lib.nvim'sui.kitconfirm dialog) asks once whether that server should be killed when Neovim exits. Answering yes kills its process tree on VimLeavePre; answering no — or pressing <Esc> — leaves it alone. Each terminal is asked about once; the answer holds for that terminal's lifetime. Only terminals started by this Neovim instance are tracked. The kill targets that terminal's recorded pid: its process tree viataskkill /Ton Windows, its process group viakill -TERM -<pid>elsewhere. A server running in another shell or a tmux pane is never touched — the plugin only kills processes it can account for, rather than sweeping the machine for everything matching a name. A command typed into an already-open shell is only detected if the program sets the terminal title (OSC 0/2), which most dev servers do. Starting the server as the terminal's own command (:terminal npm run dev) always works. To skip the prompt and always kill matching servers:
devserver = { prompt = false, kill_on_exit = true }
6. CONFIGURATION
Call setup() once during Neovim startup:
require("insights").setup({
-- options here (see below)
})
All keys are optional. Unset keys use the defaults shown below.
6.1 symbols
symbols = {
enable = true,
default_scope = "cwd", -- "cwd" | "buffer"
languages = {
lua = true,
python = true,
javascript = true,
typescript = true,
go = true,
rust = true,
c = true,
cpp = true,
java = true,
ruby = true,
php = true,
},
-- When true, Lua symbols are scanned via Tree-sitter (more precise
-- names for complex patterns); all other languages still use rg.
use_treesitter_for_lua = false,
indexing = {
exclude_patterns = {
".git/", "node_modules/", ".cache/",
"build/", "dist/", "target/",
},
max_file_size_kb = 1024, -- 0 = no limit
follow_symlinks = false,
},
cache = {
enabled = true,
dir = vim.fn.stdpath("cache") .. "/insights/symbols",
ttl_seconds = 3600, -- seconds before cache is considered stale; 0 = never
},
progress_style = "auto", -- indicator while a cwd index is built
},
default_scopeScope used when:Insights symbolsis called without an explicit scope argument. Either"cwd"or"buffer".use_treesitter_for_luaWhentrue, Lua files are scanned with Tree-sitter's AST (viabufadd/bufload). This produces more precise names for all Lua definition patterns but is slower than regex for large projects. Requiresnvim-treesitterwith theluaparser installed.indexing.exclude_patternsGlob patterns passed torg --glob '!<pattern>'to skip files and directories. Usenode_modules/(no leading*/) for any occurrence of that directory; use*/node_modules/*to match only at the root level.cache.ttl_secondsHow long a cached index is considered fresh. The cache is also invalidated automatically when source files are modified (mtime check). Set to0to disable TTL-based expiry (mtime check still applies).progress_styleProgress indicator while a cwd index is built. One of"auto"(default),"notify","statusline","fidget","float","kit". Building the index runs onergpass per enabled language pattern, each scanning the whole tree — on a large project that is seconds of no output. The indicator counts the passes and shows the running symbol total. Requireslib.nvim, which provides it vialib.nvim.progress. Withoutlib.nviminstalled the option is silently a no-op. Note that the build is synchronous: it returns symbols to its caller rather than taking a callback, and that public API is unchanged. The indicator can still update live becauseinsights.scan.rgwaits on eachrgviavim.wait, which drains scheduled callbacks. That specifically — not libuv timers, which keep ticking either way — is what a progress handle needs:lib.nvim.progressschedules both its delay guard and its statusline redraw. Under a plainvim.fn.systemlista handle never becomes visible. Cancelling is not offered:"float"/"kit"would close the indicator without stopping the build.
6.2 metrics
metrics = {
enable = true,
output_file = vim.fn.stdpath("state") .. "/insights/metrics.md",
analyze_lua = true, -- analyze Lua source files
analyze_misc = true, -- analyze Markdown / TXT / JSON files
show_file_tables = true, -- detailed per-file table (L1-L5 / W1-W5)
show_folder_tables = true, -- per-folder aggregate table
show_total_summary = true, -- grand-total row
show_ratios = true, -- folder ratio analysis
show_deviations = true, -- deviations from the global averages
show_top_lists = true, -- top-N files by lines/words
show_misc_detailed = true, -- per-file listing for misc files
percent_mode = "both", -- "both" | "percent" | "numbers"
reverse_order = true, -- summary first (vs. files first)
top_n = 50, -- items in top-N lists
col_width = 7, -- data column width in tables
exclude_type_files = true, -- exclude @types files from ratio analysis
},
output_filePath where the report is written (in addition to the scratch buffer). The directory is created automatically. Set to""to skip writing a file. Ending it inanalyze_lua/analyze_miscWhich file groups to analyze.analyze_misccovers Markdown (*.md), text/help (*.txt), and JSON (*.json) files.show_file_tables/show_folder_tables/show_total_summaryToggle the three Lua detail tables (per file, per folder, grand total).show_ratios/show_deviationsshow_ratiosadds the folder ratio table and the annotation-ratio ranking;show_deviationsadds per-folder deviation columns against the global averages.show_top_listsAdd the "Top N Files by Lines/Words" lists (usestop_n).show_misc_detailedWhentrue, list every documentation file individually (in addition to the per-type summary).percent_modeHow table cells display values:"both"(e.g.120 (60.0%)),"percent", or"numbers".reverse_orderWhentrue, the total summary and ratios come before the folder/file tables; whenfalse, files come first.top_n/col_widthNumber of items in top-N lists, and the width of data columns in the tables.exclude_type_filesWhentrue,@typesfiles are excluded from ratio analysis (they would otherwise skew annotation ratios) but still counted in totals. All of these can be overridden per invocation with the flags documented under |:Insights-metrics|.
6.3 tree
tree = {
enable = true,
exclude_patterns = { "*/.git/*", "*/node_modules/*", "*/.cache/*" },
outdir = vim.fn.stdpath("state") .. "/insights/tree",
outfile_fmt = "%s-tree.txt",
},
outfile_fmtPrintf-style format string for the output filename.%sis replaced with the project name (tail of the current working directory). Example: for/home/user/myprojectwithoutfile_fmt = "%s-tree.txt"the output ismyproject-tree.txtinsideoutdir.
6.4 fileinfo
fileinfo = {
enable = true,
keymap = "<leader>fi", -- false to disable the keymap
},
6.5 keymaps
keymaps = {
symbols_telescope = "<leader>ps", -- false to disable
symbols_fzf = "<leader>pS", -- false to disable
},
commandsSet tofalseto skip registering the:Insightsuser command entirely (use the Lua API instead). Default:true.
6.6 compress
compress = {
enable = true,
engine = "auto", -- "auto"|"tar"|"zip"|"powershell"
outdir = "", -- "" = compressed/ next to source
},
enableSet tofalseto hide thecompresssubcommand and skip compress-related health checks.engine*insights-compress-engine* Compression backend. LuaLS completes the valid string values: "auto" OS detection: tar on Unix, powershell on Windows. Default. "tar" find + tar → .tar.gz (Unix/macOS, requires tar + find) "zip" find + zip → .zip (Unix/macOS, requires zip + find) "powershell" PowerShell Compress-Archive → .zip (Windows)outdirBase directory for output. Two behaviours: "" (default) — placecompressed/adjacent to the source dir. "/path" — create<outdir>/<name>-compressed/for each project. The command's second argument overrides this for a single invocation.
6.7 imports
imports = {
enable = true,
progress_style = "auto", -- indicator for the async cwd scan + `unused` re-read
engine = "auto", -- "auto" | "treesitter" | "ripgrep" — Lua only
output_file = vim.fn.stdpath("state") .. "/insights/imports.md",
languages = {
lua = true, python = true, javascript = true,
go = true, rust = true, c = true,
},
groups = {
lib = { "lib", "lib.nvim", "lib.usrcmds" },
},
classify_external = true,
definition = {
view = "edit", -- "edit" | "float"
border = "rounded",
keymaps = { jump = "gd", preview = "gp" },
},
graph = {
include_external = false,
outdir = vim.fn.stdpath("cache") .. "/insights/graph",
layout = "dot", -- Graphviz layout engine on PATH
},
},
enableSet tofalseto hide theimportssubcommand.engineDetection backend for Lua require() calls only — the other five languages always use a regex/text scan (no Tree-sitter query implemented for them): "auto" (default) Tree-sitter when the Lua parser is available, otherwise the ripgrep line scan. "treesitter" Force the AST scan. Falls back to ripgrep (with a warning) if the Lua parser is missing. "ripgrep" Force the line scan. Faster and dependency-light, but the wordrequirein comments/strings is matched too. The Tree-sitter backend requiresnvim-treesitterwith theluaparser (:TSInstall lua).output_filePath where the report is written (in addition to the scratch buffer). The directory is created automatically. Set to""to skip writing a file.languagesWhich languages |:Insights-imports| scans; set an entry tofalseto skip it entirely (e.g. a Python-only project could set everything butpythontofalse). A bare language id/alias used as a filter argument scopes a single run to that language without changing this table.groupsNamed filter groups. Each name maps to a list of module prefixes. Passing the group name as a filter to |:Insights-imports| expands to those prefixes. Group names are offered in tab-completion.classify_externalWhentrue, modules with no matching local source file are tagged(extern)in the count table. Resolution is per-language — see |insights-imports| for the exact rule each language uses.definitionControls "go to definition" from the imports report (gd/gp), currently Lua-only. view "edit" jumps in the current window; "float" opens a preview window. Affectsgd;gpalways uses a float. border Border style for the floating preview ("rounded", "single", …). keymaps Buffer-local keys in the report:jump(default "gd") andpreview(default "gp"). Set either tofalseto disable it.graphControls |insights-imports-graph| (:Insights imports graph). include_external Draw external modules as graph nodes too. Default false — usually far more noise than signal. outdir Directory the rendered PNG is written to. layout Graphviz layout engine on PATH: "dot" (default, hierarchical), "neato"/"fdp"/"sfdp" (force-directed), "twopi"/"circo" (radial/circular).
6.8 conflicts
conflicts = {
enable = true,
events = { "VimEnter" },
git_cmd = "git",
diff_filter = "U",
open_qf = true,
notify = true,
},
enablefalsedisables the feature entirely: no autocmd is registered and:Insights conflictsreports that it is disabled.eventsAutocmd events that trigger the scan. Set to{}to never scan automatically and use the command only.git_cmdThe git executable.diff_filterAgit status --porcelaincode to match, in either the index or the worktree column. "U" (the default) means "unmerged" and matches the full conflict code set (UU, AA, DD, AU, UD, UA, DU), not just paths containing the literal letter U.open_qffalsefills the quickfix list without opening the window.notifyfalsesuppresses the notification listing the conflicting files. The quickfix list is still populated.
6.9 unimported
unimported = {
enable = true,
events = { "BufWritePost" },
filetypes = { "astro", "javascriptreact", "typescriptreact",
"vue", "svelte" },
ignore = {},
},
enablefalsedisables the feature entirely: no autocmd is registered and:Insights unimportedreports that it is disabled.eventsAutocmd events that trigger the check. Set to{}for command-only use.filetypesFiletypes the check applies to. Buffers of any other filetype are skipped, including by the autocmd.ignoreComponent names never reported, for names that are legitimately never imported (globals, framework injections):
ignore = { "Fragment", "Astro" }
6.10 devserver
devserver = {
enable = true,
prompt = true,
kill_on_exit = true,
patterns = {
"astro dev", "npm run dev", "pnpm dev", "yarn dev", "bun dev",
"vite", "next dev", "nuxt dev", "ng serve", "rails server",
},
},
enablefalsedisables the feature entirely: no terminal is watched, nothing is killed, and:Insights devserverreports that it is disabled.prompttrue(default) asks — once per terminal — whether the detected server should be killed on exit, via lib.nvim'sui.kitconfirm dialog.falseskips the dialog and applieskill_on_exitto every match.kill_on_exitThe answer used whenprompt = false. Ignored whenprompt = true, where the user's answer decides.prompt = false, kill_on_exit = falsetherefore disables killing while still tracking matches for:Insights devserver list.patternsPlain substrings (not Lua patterns), matched case-insensitively against the terminal's command. Replace the list to track a different set of servers:
patterns = { "npm run dev", "cargo watch" }
See |insights-devserver-tracking| for what is and is not detected.
7. SYMBOL TYPES
The picker displays a type label for each symbol in square brackets.
7.1 Function types
Symbols of the default functions type:
local local function foo()
global function foo() / top-level def foo(): in Python
module function M.foo() / M.foo = function()
method receiver method in Go, class method in Python/Java/Ruby
anonymous const foo = () => / foo = function()
exported export function foo() in JavaScript/TypeScript
unknown pattern matched but type could not be inferred
7.2 Lua TS types
Lua-specific types, produced by the Tree-sitter scanner (|:Insights-symbols| withtablesorstrings):
table table constructor: local t = {} / state.win = {} / { field = {} }
string unique string literal: "require path", event name, magic value
8. PICKERS
:Insights symbols auto-selects a picker in this order:
1. telescope.nvim (if installed)
2. fzf-lua (if installed)
3. scratch buffer (always available)
Force a specific picker by passing its name as an argument:
:Insights symbols telescope
:Insights symbols fzf
:Insights symbols scratch
*insights-picker-telescope*
Telescope picker
Displays symbols as path:line [type] name.
Default actions:
<Enter> edit file at symbol line
<C-p> toggle file preview
*insights-picker-fzf*
fzf-lua picker
Same display format. Default action:
<Enter> edit file at symbol line
*insights-picker-scratch*
Scratch buffer
Read-only buffer, one symbol per line in the format:
lua/insights/init.lua:14 [module] M.setup
Keymaps:
q / <Esc> close buffer
gf follow path:line under cursor
9. LUA API
After callingsetup(), the following functions are available: *insights.get_symbols()*require("insights").get_symbols([scope [, force_rebuild]])Returns(entries, message)whereentriesis a list of symbol tables andmessageis a status string.scope"cwd"|"buffer"| nil (uses default_scope)force_rebuildboolean, force a cache rebuild Each entry has:
{
filename = "lua/foo/bar.lua",
lnum = 42,
col = 0,
name = "my_function",
func_type = "module",
language = "lua",
signature = "my_function(a, b)",
text = "function M.my_function(a, b)",
}
*insights.get_tables()*require("insights").get_tables([scope])Scan Lua table definitions via Tree-sitter. Returns(entries, message).scope"buffer"(default) |"cwd"Each entry hasfilename,lnum,col,name,func_type = "table". Requiresnvim-treesitterwith theluaparser. *insights.get_strings()*require("insights").get_strings([scope])Scan unique Lua string literals via Tree-sitter. Returns(entries, message).scope"buffer"(default) |"cwd"Each entry hasfilename,lnum,col,name,func_type = "string". Requiresnvim-treesitterwith theluaparser. *insights.run_metrics()*require("insights").run_metrics()Run the Lua metrics analysis for the current project and open the report in a scratch buffer. *insights.run_imports()*require("insights").run_imports([filters], [ui])Scan import/require usage for the current project (Lua, Python, JS/TS, Go, Rust, C/C++) and open the report.filtersoptional list of module prefixes / language ids / group names to filter by, e.g.{ "insights" },{ "lib" }, or{ "python" }.uioptional"telescope"or"fzf"to open a picker over the occurrence list instead of the scratch buffer. *insights.run_imports_reverse()*require("insights").run_imports_reverse(module)List every file that importsmoduleand open the report in a scratch buffer. *insights.run_imports_unused()*require("insights").run_imports_unused([filters])List bound import names that never appear again in their file (heuristic, see |insights-imports|) and open the report in a scratch buffer.filtersoptional list of module prefixes / language ids / group names. *insights.write_tree()*require("insights").write_tree([callback])Write the project file tree.callbackreceives(success, message, outpath). *insights.show_fileinfo()*require("insights").show_fileinfo()Toggle the file info float for the current buffer. *insights.run_conflicts()*require("insights").run_conflicts()Scan for unresolved merge conflicts and populate the quickfix list. Returns the number of conflicting files (0 outside a git repository). *insights.check_unimported()*require("insights").check_unimported([bufnr])Returns a list of component names used inbufnr(default: the current buffer) with no matching import or local definition, and notifies about them. The buffer's filetype is not checked — pass any buffer. Userequire("insights.unimported").check_buf(bufnr)for the same list without the notification. *insights.devservers()*require("insights").devservers()Dev servers tracked in this session, keyed by terminal channel:
{
[3] = {
pid = 12345,
cmd = "npm run dev",
kill_on_exit = true,
},
}
10. HEALTH CHECK
:checkhealth insights
Reports:
Neovim version Requires ≥ 0.9; notes vim.system availability (0.10+).
External tools rg (required); PowerShell (Windows) or find/sed (Unix)
for the file tree.
Optional pickers telescope.nvim, fzf-lua.
Optional PDF export pdfport.nvim, only relevant when metrics.output_file
ends in .pdf.
Tree-sitter nvim-treesitter installation status.
Configuration Active language list, scope, cache settings.
Automatic triggers conflicts / unimported / devserver status, plus the
tools they need (git, taskkill or kill).
Compress Engine availability (tar/find/zip/powershell), outdir writability.
Hover contribution Whether insights registers into hover.nvim, and the
import-index freshness (cold/stale/warm) it reads from.
Cache Symbol count, last-indexed timestamp, file path.
Declared tools Cross-check against docs/install.json
(lib.nvim.deps) (:Lib deps show insights.nvim).
11. TROUBLESHOOTING
No symbols found
• Isrgin yourPATH? Check with:checkhealth insights. • Is the current working directory correct? (:pwd) • Are the target languages enabled insymbols.languages? • Run:Insights cache buildto force a fresh index.
Symbols are stale
• The cache is invalidated automatically when source files change (mtime). • To force a rebuild::Insights symbols rebuildor:Insights cache build. • Lowersymbols.cache.ttl_secondsor set it to0to disable TTL.
Tree command fails
• On Unix,findandsedmust be available. • On Windows, PowerShell 5.1+ is required. • Checktree.outdiris writable.
File tree clipboard empty
• Run:Insights treefirst to generate the tree file. • The clipboard write usessetreg("+", …); ensure the+register is available (Neovim has clipboard support built in).
Tree-sitter Lua scanner shows no results
•nvim-treesittermust be installed with theluaparser::TSInstall lua• The scanner loads each file withbufadd/bufload; very large projects may be slow — use the rg scanner for those. • This applies to all three TS-based types:functions,tables,strings.
Tables or strings scan returns nothing
• Verify the current buffer (or cwd) contains Lua files. • Forbufferscope, the buffer must havefiletype=lua. • Thetablesscanner finds{}constructor assignments only; barerequire("mod")calls are not matched as tables. • Thestringsscanner deduplicates by content, so repeated literals appear once regardless of how many times they occur.
Picker not opening
• If you specifiedtelescopeorfzf, verify the plugin is installed. • Fall back to:Insights symbols scratchwhich has no dependencies.
Compress fails
• Run:checkhealth insightsto see which engine tools are available on your system. •tarengine:tarandfindmust be inPATH. •zipengine:zipandfindmust be inPATH. •powershellengine: PowerShell 5.1+ required (Compress-Archive). • Verify the output directory (or its parent) is writable. • Paths with spaces: the PowerShell engine uses single-quoted strings which handle spaces, but if problems occur use a simpleroutdir.
Dev server was never detected
• Is the command indevserver.patterns? Check the exact command with:echo b:term_titleor:lua =vim.api.nvim_get_chan_info(vim.b.terminal_job_id).argvand add a substring of it to the list. • A command typed into an already-running shell is only seen if the program sets the terminal title. Start it as the terminal's command instead::terminal npm run dev. • A server started outside Neovim (another shell, tmux pane) is never detected. This is by design — see |insights-devserver-tracking|.
Dev server was not killed on exit
• Confirm it was tracked and approved::Insights devserver list.kill on exit: falsemeans the prompt was answered "no" or cancelled. •:checkhealth insightsshows whethertaskkill(Windows) orkill(Unix) is available. • The process must be a child of the terminal Neovim started. A server that daemonises itself away from that process tree outlives the kill.
Dev-server prompt never appears
•devserver.prompt = falseapplieskill_on_exitsilently by design. • The prompt fires once per terminal; it does not re-ask for a terminal already answered. • It needs lib.nvim'sui.kit;:checkhealth insightsreports a missing or outdated lib.nvim.
Unimported reports a component that is imported
• The check is textual and single-file: it looks for animportline or a local declaration binding that exact name. An import generated at build time or injected globally is invisible to it — add such names tounimported.ignore.
Conflicts reports files that are not conflicted
• Only stdout fromgit diff --diff-filter=Uis parsed, so warnings on stderr are not treated as file names. If the list still looks wrong, rungit diff --name-only --diff-filter=Uyourself to compare.