none-ls.nvim: the repo name changed, the Lua module name did not
null-ls.nvim reloaded / Use Neovim as a language server to inject LSP diagnostics, code actions, and more via Lua.
At a glance
- What is it?
- nvimtools/none-ls.nvim is null-ls.nvim with the repository renamed and the API deliberately frozen, which leaves a fork whose own documentation still introduces itself by the old name, whose five hook points and single pre-commit gate are inherited wholesale, and whose only new namespace belongs to third-party sources.
- Who is it for?
- Use it if you were already running null-ls.nvim and want a maintained owner rather than an archived upstream, since the migration really is a one string swap and the Lua side is deliberately unchanged. Weigh two things first: nobody can merge their own pull request, so check that the collaborator pool can actually review what you want to send, and note that a clean :checkhealth tells you nothing about whether your command resolved.
- Can I use it commercially?
- Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly Lua, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- nvimtools/none-ls.nvim3,262 ★ · -1
| Repository | 2026-09-24 | 2026-11-13 |
|---|---|---|
| nvimtools/none-ls.nvim | 3.3k | 3.3k |
-1 Stars in the last 50 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Only the repo name changed and the document still introduces itself as null-ls
The claim at the top is unusually literal. The project calls itself null-ls.nvim Reloaded, maintained by the community, and then states that only the repo name is changed for compatibility concerns and that all the API and future changes will keep in place as-is.
Everything after that line bears the claim out. The page opens with an h1 reading none-ls.nvim and, a few lines later, a second h1 reading null-ls.nvim. From there the prose is the upstream document: Motivation, Status, Features, Setup, Documentation, Contributing, Examples, FAQ. The word null-ls appears 29 times in the README and none-ls appears 4 times, and the 4 are the fork's own additions rather than the upstream text.
That is not sloppiness so much as the compatibility mechanism working as advertised. A fork that kept two identities in one file is a fork whose readers can tell at a glance which half they are reading.
The metadata is where the rename is thinner. There are no GitHub releases at all, so the fork has no version stream of its own to publish into; a subscriber of this project has nothing to upgrade to and no tag to pin. The licence is the Unlicense, and the tree pairs a plain LICENSE file with a LICENSES directory and a REUSE.toml, which is the SPDX reuse compliance layout where every file carries an explicit declared licence. That is more licence machinery than the Unlicense itself requires, and it is the one place the fork clearly did add structure of its own.
The Lua module is still null-ls, so the new name only appears in third-party sources
The migration instruction is three lines and then the words That's it. Replace jose-elias-alvarez/null-ls.nvim with nvimtools/none-ls.nvim in your choice of package manager.
That single swap is the whole migration because the module name did not move. Every Lua example in the README opens with local null_ls = require("null-ls"), and the helpers example adds require("null-ls.helpers"). A user who changes one string in their plugin manager keeps a config that requires null-ls, references null_ls.builtins, and registers custom sources built on null_ls.methods.
The new namespace is real but it belongs to other people. The only require of a none-ls module in the setup example is require("none-ls.diagnostics.eslint"), and it carries a comment saying it requires none-ls-extras.nvim. So none-ls. is the convention for out-of-tree sources while the plugin's own modules stay under null-ls., which is what lets a third-party plugin be named for the fork without colliding with the upstream module tree.
That split is the thing to understand before configuring anything. The package manager entry, the Lua require path and the source namespace are three different names for this project, and only the first of them was renamed.
Merging your own pull request requires a second collaborator to review it
The Community section is four sentences long and sets out the whole governance model. Open a pull request to become a collaborator. If you contributed to null-ls.nvim before, simply open an issue or comment on that commit.
So collaborator status is granted by contributing, with a shortcut for people who already worked on the upstream project. That is a sensible rule for a fork whose entire codebase was written by somebody else, because it establishes that a reviewer knows the code before being asked to review it.
The second half is the part that constrains everyone. If you want to make changes, open a new pull request, find another collaborator to review your changes, as a review is required for the PR to be merged by yourself, subject to change if there are more collaborators in the future. Nobody merges their own work. The author has to locate a second person, and that person has to be a collaborator already.
For a project with 3261 stars, 121 forks and 14 open issues, the practical constraint is the size of the reviewer pool rather than the size of the queue. The README hedges the rule once, in parentheses, promising it may change as more collaborators arrive, so the current text is best read as a statement of intent about a young project rather than a permanent policy.
The inherited status section still says beta and still asks for fixes to HEAD builds
The Status section is upstream's, word for word, and it makes three claims none of which the fork has revisited. It says null-ls is in beta status. It says null-ls is developed on and tested against the latest stable version of Neovim. And it says support for versions built from HEAD is provided on a best-effort basis, with users encouraged to contribute fixes to any issues exclusive to those versions.
A fork that promises all API and future changes will keep in place as-is inherits a beta label along with that promise. Nothing in the document says the fork has exited beta, and nothing says it has not.
The HEAD clause is the more interesting inheritance. It is a routing rule as much as a support statement: problems that only reproduce on a nightly Neovim build are, by the project's own account, the user's to diagnose and contribute back rather than something to report. Since Neovim's release cadence moves faster than a community fork's, that clause is where expectations and reality are most likely to diverge.
The Motivation section carries the same vintage. It credits telescope.nvim and trouble.nvim with making LSP features pleasant in Neovim and frames the project as a way to hook non-LSP sources into an LSP client that VS Code and coc.nvim users already have. That was a fair description when it was written, and it is still the clearest statement of what the plugin is for.
Troubleshooting step one is to diff against the pre-fork document and step two blames the CLI
The first FAQ entry opens with a capitalised instruction not to open an issue on the Neovim repository. The reasoning is stated plainly: null-ls is not an actual LSP server, so the maintainers need to determine whether an issue is specific to this plugin before anything is sent upstream.
Then come five steps. Make sure your configuration is in line with the latest version of this document. Enable debug mode and check the output of your sources, with the note that if the CLI program is not properly configured or is otherwise not running as expected, that is an issue with the program, not null-ls. Check the documentation for available configuration options. Open a discussion if you are having trouble configuring or want to know how to achieve a specific result. Open an issue if you believe the issue is with null-ls itself, providing what the issue template asks for.
Two of those five steps push responsibility outward before they push it inward. Step one asks you to compare your config against the latest version of the document, and the document in question is the upstream text that still calls itself null-ls. Step two hands the diagnosis to the external program if that program is misbehaving, which is the correct call for a source that shells out to something like stylua or markdownlint, but it means a genuine plugin bug is only reached at step five.
Debug mode is the load-bearing instruction and it is referenced by anchor rather than explained in the same section, so the diagnostic path is: find the anchor, enable it, read your source's own output, and only then decide whose bug it is.
A green :checkhealth does not mean your command resolves
There is a second FAQ entry that exists entirely to lower the value of Neovim's own health output. Checking whether a given command is executable is tricky, the entry says, and the plugin's health check does not handle all cases. The internal command resolution is independent of the health check output, which is for informational purposes.
That is a candid admission and a useful one. A plugin that wraps external binaries has two separate questions: can I find the executable, and will my wrapper find it the same way the user does. If the health check answers a different question from the resolver, then a clean checkhealth is not evidence about the thing you actually care about, and the only way to find out is the debug output from the previous entry.
The practical consequence is that checkhealth should be read as a hint rather than a verdict. If a source silently produces nothing, the absence of a health warning is not a reason to look elsewhere, and the README's own wording gives you no licence to think otherwise.
It also tells you where the project's confidence is. The two FAQ entries that exist at all are about failure modes the maintainers could not make go away, and both point at the same remedy: enable debug mode and read what your source actually did.
Five hook points, six documentation files and three kinds of source in three lines
The Features section names five things a source can attach to: code actions, diagnostics at both file and project level, formatting including range formatting, hover, and completion. Built-in sources exist for each of the five, and the documentation set is split by purpose, with MAIN as what the README calls the definitive source, plus BUILTINS, BUILTIN_CONFIG, CONFIG, HELPERS and CONTRIBUTING.
The setup example is three lines of substance and shows all three kinds of source at once:
local null_ls = require("null-ls")
null_ls.setup({
sources = {
null_ls.builtins.formatting.stylua,
null_ls.builtins.completion.spell,
require("none-ls.diagnostics.eslint"), -- requires none-ls-extras.nvim
},
})The first entry is a built-in formatter, stylua. The second is a built-in completion source, spell. The third is a require of none-ls.diagnostics.eslint with a comment naming none-ls-extras.nvim as the provider. So a single sources list mixes a bundled formatter, a bundled completion source and a third-party diagnostic source, and the only thing distinguishing them is where the table comes from.
The design claim underneath is stated once in Motivation: reduce the boilerplate for setting up general-purpose language servers and improve performance by removing the need for external processes. A source is a Lua table with a method, a filetypes list and a generator, not a subprocess, which is what makes a builtin like stylua cheap and what makes the third-party require in the example a natural extension.
The one runtime dependency the README names is plenary.nvim, which it says you are probably already using. The Makefile turns out to lean on the same plugin for testing, so plenary is both a runtime requirement and the test harness.
One quality gate through pre-commit, and a generated target maintainers must not run
The Makefile has six phony targets and they divide cleanly. Two run tests: test drives headless Neovim with plugins disabled through plenary's test_harness over the test/spec directory, and test-file runs plenary's busted against a single file. Two manage the pre-commit setup: install-hooks installs the hooks, and check runs pre-commit across all files.
That means the project's single quality gate is pre-commit. There is no separate lint step in the Makefile, no type check and no build, and the .pre-commit-config.yaml is the place every style and correctness rule for the repository has to live.
The last two targets are the interesting ones. clean removes a .tests directory. autogen runs a script at scripts/autogen.sh and carries a comment above it reading do not run manually, with a parenthetical that it runs via CI. So the repository has at least one generated artifact that its own maintainers are told to produce only in CI, which is a sensible guard against a locally generated version drifting into a commit.
Around the Makefile sit the rest of the conventions: selene.toml and stylua.toml for the Lua linter and formatter, .prettierrc and .prettierignore, .editorconfig, .cbfmt.toml, .nvim.settings.json, and vim.yml. Four separate formatter configurations for a codebase whose deliverable is a Neovim plugin is a fair indicator of a project that treats formatting as something to enforce automatically rather than argue about in review.
Editorial conclusion
Use it if you were already running null-ls.nvim and want a maintained owner rather than an archived upstream, since the migration really is a one string swap and the Lua side is deliberately unchanged. Weigh two things first: nobody can merge their own pull request, so check that the collaborator pool can actually review what you want to send, and note that a clean :checkhealth tells you nothing about whether your command resolved. Skip it if you were counting on tagged releases to pin a version, because this repository publishes none.
Frequently asked questions
Do I have to change my Lua code to move from null-ls.nvim to none-ls.nvim?
No. The README states that only the repo name is changed for compatibility concerns and that all API and future changes keep in place as-is. The migration is to replace jose-elias-alvarez/null-ls.nvim with nvimtools/none-ls.nvim in your package manager, and every example still does require("null-ls").
Does none-ls.nvim publish tagged releases?
The repository has no GitHub releases. The only version stream is the upstream project it was forked from, and the fork's own statement is that the API and future changes will keep in place as-is.
What can a none-ls.nvim source hook into?
Five LSP features: code actions, diagnostics at file and project level, formatting including range formatting, hover, and completion. Built-in sources exist for each, and custom sources are built with the helpers described in doc/HELPERS.md.
Why did :checkhealth pass while my none-ls.nvim source produced nothing?
Because the health check is informational only. The README says checking whether a command is executable is tricky, that the health check does not handle all cases, and that the plugin's internal command resolution is independent of the health check output.
Can I merge my own pull request to none-ls.nvim?
No. You have to find another collaborator to review the change, because a review is required for a pull request to be merged by its author. Collaborator status comes from opening a pull request, or from opening an issue if you already contributed to null-ls.nvim before.
What does none-ls.nvim depend on?
plenary.nvim, which the README says you are probably already using. The Makefile runs the test suite through the same plugin, using plenary's busted and test_harness inside headless Neovim with plugins disabled.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nvimtools-none-ls-nvim)Star history chart for your README
[](https://hysenlabs.com/projects/nvimtools-none-ls-nvim)Was this useful?