Open-source project
dalance/svls avatar
dalance/svls

svls, a Rust language server for SystemVerilog built on svlint

SystemVerilog language server

587 stars32 forksRustMIT

At a glance

What is it?
svls is a SystemVerilog language server written in Rust, whose single listed feature is a linter built on the svlint project, with configuration for include paths, defines and plugins. It suits hardware developers who want svlint's rules inside their editor, and its limits are the limits of a linter rather than a full completion engine.
Who is it for?
Adopt svls if you write SystemVerilog and already rely on svlint, and want its rules reported in the editor through the standard language server protocol rather than as a separate command in your build script. Do not adopt it expecting full language server features, because the documented feature list is a linter and nothing more, and do not adopt it if you need include resolution beyond the configured include_paths.
Can I use it commercially?
Yes. MIT 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 10 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A language server whose feature list is one line

svls is described as a SystemVerilog language server, written in Rust, and the Feature section of the README contains a single bullet: a linter based on svlint.

That is a deliberately narrow scope, and it is the whole design decision in the project. A full language server for SystemVerilog would need a parser, a type checker, symbol resolution across modules and an elaboration model, and there are other projects in the space attempting that. svls takes the position that the rules you already trust are the useful part, and the mechanism to deliver them is the language server protocol rather than a compiler plugin or a CI script.

The dependency list in Cargo.toml confirms the architecture. The two core crates are svlint at 0.9.5 and sv-parser at 0.13.5, and the server itself is tower-lsp 0.20 running on tokio with the io-std, macros, rt and test-util features. clap handles the command line, simplelog and log handle logging, and serde with serde_json and toml cover configuration.

So the parsing is not svls's own work, it comes from sv-parser, and the rules come from svlint. What svls contributes is the server shell: configuration, the LSP wiring, and the plugin loading.

Three install routes: binary, snap and cargo

Installation is offered three ways, which tells you how the project expects different users to consume it.

The first is a downloaded binary. You take it from the release page and extract it to a directory on your PATH. The releases are published as versioned archives, and the Makefile shows what goes into them: a release build for x86_64-unknown-linux-musl zipped for Linux, a build for x86_64-pc-windows-msvc archived with 7z for Windows, and two macOS builds, x86_64-apple-darwin and aarch64-apple-darwin, each zipped. The musl target for Linux is a deliberate choice, since a statically linked binary runs on any distribution regardless of its libc.

The second is snapcraft, with a single command:

bash
sudo snap install svls

The third is Cargo, for people who already have a Rust toolchain:

bash
cargo install svls

The binary route suits an editor user who does not want a Rust toolchain installed, the snap route suits Ubuntu users, and the Cargo route suits CI images and anyone who wants to pin a version. The package is published on crates.io as svls, with a Snapcraft badge and an AUR package listed in the badges, so the Arch community is covered too.

Two configuration files that do different jobs

Configuration is split across two files, and understanding which does what saves a lot of confusion.

The first is .svls.toml at the root of the repository, and it is read by the language server itself. It has two sections. The verilog section holds include_paths, which are include paths from the root of the repository, defines, which are define strings, and plugins, which are paths to svlint plugins from the working directory. The option section holds linter, a boolean showing whether the linter feature is enabled.

The example given in the README is:

toml
[verilog]
include_paths = ["src/header"]
defines = ["DEBUG", "VAR=1"]
plugins = ["path/to/libfoo.so", "path/to/libbar.so"]

[option]
linter = true

The second file is .svlint.toml, also at the repository root, and that one belongs to the linter. If .svlint.toml cannot be used, all lint rules are enabled, which is a permissive default rather than a restrictive one, and the README points to the svlint manual for the configuration details.

The two-file split is sensible but worth internalising: turning a rule off is done in .svlint.toml, and the include paths and defines that affect how the code is parsed are done in .svls.toml.

Editor recipes for six clients, and what differs between them

The README documents setup for six editors, and the differences between them are the interesting part.

For Visual Studio Code the answer is an extension, svls-vscode, installed from the marketplace. For Vim and Neovim there are three separate recipes depending on the plugin: LanguageClient-neovim, vim-lsp and coc.nvim. For Neovim there is nvim-lspconfig, and for Emacs there are two paths, lsp-mode and verilog-ext. Helix gets a toml entry in languages.toml.

The nvim-lspconfig recipe is a good illustration, because it shows the detail that catches people out: the server needs a root_dir function, and the README uses the git ancestor finder so the server is launched from the repository root rather than from the directory of the file being edited. Without that, a .svls.toml at the repository root is not found.

lua
{
  "neovim/nvim-lspconfig",
  opts = {
    servers = {
      svls = {
        root_dir = function(fname)
          return require("lspconfig.util").find_git_ancestor(fname)
        end,
        cmd = { "svls" },
        filetypes = { "verilog", "systemverilog" },
      },
    },
  },
}

Notice the filetypes list includes both verilog and systemverilog, while the coc.nvim recipe lists only systemverilog. Small inconsistency, but the kind of thing that decides whether the server starts when you open a .v file.

The Emacs lsp-mode recipe is by far the longest in the README, combining flycheck, company with the flx fuzzy matching extension, company-lsp as the completion backend, lsp-ui for the sidebar and peek views, and verilog-mode with lsp-register-client. The verilog-ext recipe is the shortest, two lines that set the eglot and lsp servers to ve-svls.

Build plumbing, release metadata and a pre-1.0 version number

The repository is small and conventional. The root holds src/, a Makefile, Cargo.toml, a snapcraft.yaml, a CHANGELOG.md and a LICENSE, plus a movie/ directory and a .github directory whose workflow is named Regression, which is the badge at the top of the README.

The Makefile is more interesting than a typical one because it handles releases for three platforms. The release_lnx target builds for x86_64-unknown-linux-musl and zips the binary, release_win builds for x86_64-pc-windows-msvc and archives the executable with 7z, and there are separate macOS targets for x86_64-apple-darwin and aarch64-apple-darwin. The default target runs the test suite, and there is a watch target that runs cargo watch test, plus clean.

The version string is assembled from the Cargo.toml version, the git revision, the rustc version and the build time, and exported as LONG_VERSION, which is how the server reports itself to the client.

Release metadata is automated through the package.metadata.release section, with cargo-release style templates that rewrite the CHANGELOG.md placeholders, replacing Unreleased and ReleaseDate, and update the version in snapcraft.yaml with a regular expression match. The Cargo.toml also carries commented-out patch entries pointing at local sv-parser and svlint checkouts, which is the hook for testing a change to the parser or the linter before it lands upstream.

The version itself is 0.2.14, with 0.2.13 in September 2025, 0.2.12 in June 2024 and the last push to master on 2026-10-14. It is a 0.2.x line, and the licence is MIT.

Editorial conclusion

Adopt svls if you write SystemVerilog and already rely on svlint, and want its rules reported in the editor through the standard language server protocol rather than as a separate command in your build script. Do not adopt it expecting full language server features, because the documented feature list is a linter and nothing more, and do not adopt it if you need include resolution beyond the configured include_paths. Before you wire it into an editor, write a .svls.toml at the repository root with your include paths and defines, decide whether to keep a separate .svlint.toml since the linter falls back to enabling all rules when it cannot be used, and check the editor configuration snippets for your client, since each one differs.

Frequently asked questions

What does the svls SystemVerilog language server actually provide?

The documented feature list has one item, a linter based on svlint. The server is built in Rust on top of svlint and sv-parser, with tower-lsp and tokio providing the language server protocol side, so it delivers lint diagnostics rather than a full completion or type checking engine.

How do I install svls?

You can download a binary from the release page and extract it to a directory on your PATH, install it with cargo install svls if you have a Rust toolchain, or install it from snapcraft with sudo snap install svls. The Makefile also builds release archives for Linux, Windows and both macOS architectures.

What goes in the .svls.toml file?

The file lives at the root of the repository. The verilog section holds include_paths, which are include paths from the repository root, defines, which are define strings, and plugins, which are paths to svlint plugins from the working directory. The option section holds linter, a boolean for whether the linter feature is enabled.

How is svls configured for Neovim with nvim-lspconfig?

The README recipe registers the server under neovim/nvim-lspconfig with a root_dir function that uses the git ancestor finder, so the server starts from the repository root, the command set to svls and filetypes covering verilog and systemverilog.

What licence is svls released under?

MIT, as stated in the Cargo.toml and in the README. The crate is published on crates.io as svls, with a Snapcraft package and an AUR package also listed.

Official sources

  1. dalance/svls on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dalance-svls.svg)](https://hysenlabs.com/projects/dalance-svls)

Was this useful?