Skip to content
Fallow home
All docs pages

Neovim

See unused code, duplication, circular dependencies, and complexity in Neovim as you edit. Uses the built-in LSP client, with hover, quick-fix code actions, and code lens.

Get fallow findings in Neovim while you edit, with the same results as the CLI and CI. Neovim's built-in LSP client starts fallow-lsp, the language server that the VS Code extension also uses. The editor does no analysis of its own.

The vim.lsp.config and vim.lsp.enable API below requires Neovim 0.11+. On older versions, attach fallow-lsp through nvim-lspconfig or a manual vim.lsp.start.

What works

  • Real-time diagnostics for these issues:
    • unused files, exports, types, dependencies, and enum and class members
    • unresolved imports, unlisted dependencies, and duplicate exports
    • circular dependencies and code duplication
  • Opt-in security candidate diagnostics. These are off by default. To turn them on, raise the security-sink or security-client-server-leak rule to warn or error in your fallow config. Each candidate shows as an information squiggle. A candidate is unverified, so check it before you act. The hover shows the confidence signals first and points to fallow security --file for the full trace. A quick-fix dismisses the candidate.
  • Opt-in component health hints. When you raise prop-drilling, thin-wrapper, or duplicate-prop-shape to warn or error, each finding shows as a hint diagnostic on the component.
  • Config pattern checks. An ignoreDependencies glob or an ignoreFindings pattern that matches nothing gets an information diagnostic on its entry in the config file.
  • Hover information with export usage counts, unused status, and duplicate block locations
  • Quick-fix code actions to remove unused exports or delete unused files
  • Finding ids. Each dead-code diagnostic has the stable finding_id of the finding in data.findingId. It is the same id as in fallow dead-code --format json, so you can join a diagnostic to a CLI or CI report. A security candidate diagnostic also has data.findingId, with the same id as in fallow security --format json.
  • Code lens with export reference counts, where Neovim shows code lens

Installation

  1. Install Fallow so fallow-lsp is on your PATH

    npm install -g fallow

    You can also add it as a project devDependency (npm install -D fallow) and point cmd at node_modules/.bin/fallow-lsp.

  2. Confirm Neovim can see the binary

    fallow-lsp --version
  3. Register and enable the server

    Add the configuration below to your Neovim config, then open a TypeScript or JavaScript project.

Configuration

vim.lsp.config("fallow", {
	cmd = { "fallow-lsp" },
	filetypes = { "javascript", "typescript", "javascriptreact", "typescriptreact" },
	root_markers = { ".fallowrc.json", "package.json", ".git" },
	init_options = {
		-- Every issue type is enabled by default. List only the ones you
		-- want to turn off; any key you omit stays enabled.
		issueTypes = {
			["circular-dependencies"] = false,
		},
	},
})

vim.lsp.enable("fallow")

init_options is optional. cmd, filetypes, and root_markers are enough to attach the server.

Issue-type toggles

To hide an issue type in the editor, set it to false in the LSP initialization options. Your project config does not change. The keys are fallow issue types in kebab-case, such as unused-exports, unresolved-imports, and circular-dependencies. To get the current list, a client can send the custom fallow/issueTypes request.

Scope to changed files

To see diagnostics only for files changed since a git ref, pass the ref as changedSince. This works like fallow dead-code --changed-since and the VS Code fallow.changedSince setting. Use it to adopt fallow on a legacy codebase without a flood of old findings:

init_options = {
	changedSince = "fallow-baseline",
},

Parse the project at startup

The first analysis runs when you open the first file. To parse the project earlier, set prewarm. The server then parses the files when it starts, but publishes no diagnostics. The first analysis starts from the parsed files:

init_options = {
	prewarm = true,
},

prewarm is off by default. It needs a package.json in the workspace root. It also needs a client that registers watched files, because the server keeps its project session only for such a client.

Project session between saves

When the client registers watched files, the server keeps one project session between saves. A save then parses only the changed files. A change to package.json, a tsconfig file, or a fallow config file loads the session again. On a large repository the kept session holds more memory between saves: about 100 MB on the Next.js repository. To load a new session on each save, set FALLOW_LSP_REUSE_SESSION=0 for the server:

cmd_env = { FALLOW_LSP_REUSE_SESSION = "0" },

When diagnostics appear. Fallow uses the LSP 3.17 pull model and refreshes diagnostics on save. The first analysis runs when the server attaches. A buffer you just opened shows findings when that first pass completes, or after the next save. Some findings belong to files you have not opened, such as package.json for unlisted dependencies. Fallow pushes those findings, so you still see them.

Binary resolution

Neovim runs cmd exactly as configured. If fallow-lsp is not on Neovim's PATH, point cmd at an absolute path:

vim.lsp.config("fallow", {
	cmd = { "/absolute/path/to/fallow-lsp" },
	filetypes = { "javascript", "typescript", "javascriptreact", "typescriptreact" },
	root_markers = { ".fallowrc.json", "package.json", ".git" },
})

Verifying the setup

  1. Open a TypeScript or JavaScript project.

  2. Run :checkhealth vim.lsp.

  3. Confirm fallow is attached:

    :lua vim.print(vim.lsp.get_clients({ name = "fallow" }))

See also