No description
  • Rust 99.7%
  • Makefile 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-29 11:19:34 +02:00
src AI code quality gates and improvements 2026-09-29 11:19:34 +02:00
tests AI code quality gates and improvements 2026-09-29 11:19:34 +02:00
.gitignore save project 2026-09-06 18:21:34 +02:00
AGENTS.md AI code quality gates and improvements 2026-09-29 11:19:34 +02:00
Cargo.lock save project 2026-09-06 18:21:34 +02:00
Cargo.toml AI code quality gates and improvements 2026-09-29 11:19:34 +02:00
LICENSE save project 2026-09-06 18:21:34 +02:00
Makefile save project 2026-09-06 18:21:34 +02:00
README.md AI code quality gates and improvements 2026-09-29 11:19:34 +02:00
rust-toolchain.toml save project 2026-09-06 18:21:34 +02:00
rustfmt.toml save project 2026-09-06 18:21:34 +02:00

slsls

[WARN] This is AI-slopped experimental project

A minimal Language Server Protocol (LSP) server for SaltStack .sls files.

Three validation passes run on each open/change:

  • Jinja block-closure checking — flags unclosed opening blocks ({% if %} without {% endif %}), closing blocks without a matching opener ({% endif %} alone), and mismatched closers ({% if %} closed by {% endfor %}). Tracks nesting, handles Jinja whitespace-control delimiters ({%- ... -%}, {%+ ... +%}), recognizes keywords without surrounding whitespace ({% if(x) %}), ignores block-looking content inside {% raw %} blocks, and never treats tag delimiters found inside Jinja string literals as real tags.
  • Jinja expression checking (minijinja) — when the block structure is sound, the whole document is compiled as a Jinja template; compile failures are reported as jinja-invalid-expression. Salt-only tags such as {% import_yaml %} and {% load_yaml as x %}...{% endload %} blocks and do statements with inline conditionals are understood and not flagged. Jinja2 implicit tuples ({% for x in 'a','b' %}, {% if v in 'a','b' %}) are accepted as well.
  • YAML syntax checking — Jinja tags are removed or replaced before parsing so the YAML parser never sees them; syntax errors are reported at their original source locations.

No other LSP language features are implemented.

Build

cargo build --release

The resulting executable is target/release/sls-lsp.

Neovim setup

Neovim 0.11+ with the built-in LSP client:

vim.filetype.add({
  extension = {
    sls = "sls",
  },
})

vim.lsp.config("sls_lsp", {
  cmd = { "sls-lsp" },
  filetypes = { "sls" },
  root_markers = { ".git" },
})

vim.lsp.enable("sls_lsp")

Verify with:

:set filetype?
:checkhealth vim.lsp
:lua =vim.lsp.get_clients({ bufnr = 0 })

Diagnostics

Code Description
jinja-unclosed-block Opening block without its closer.
jinja-unexpected-closing-block Closing block with no matching opener.
jinja-mismatched-closing-block Closing block does not match the top of the stack.
jinja-invalid-expression Jinja template compile error (minijinja).
yaml-syntax-error YAML parse error (Jinja tags are rendered out first).

All diagnostics share the source sls-jinja-blocks, use error severity, and report positions as zero-based line numbers and UTF-16 code unit columns.

When a Jinja tag is malformed (an unterminated {%, {{ or {#), only the Jinja error is reported; YAML checking is skipped for that revision because the rendered view of the file would not be faithful.

Quick fixes are offered per Jinja closure issue and only for diagnostics overlapping the requested range: add a missing closer (inserted at the end of the document, keeping the opener's indentation; when deeper blocks are still open, their closers are appended first so nested fixes compose in one shot), remove an unexpected closer, or replace a mismatched closer. Expression errors offer no automated fix.

Supported messages

initialize, initialized, shutdown, exit, textDocument/didOpen, textDocument/didChange (full document sync), textDocument/didClose, textDocument/codeAction (quick fixes per Jinja issue).

Development

cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features
cargo build --release

Or use the Makefile:

make quality   # fmt + clippy + test + audit + machete
make all       # quality + release build

Lint attributes

src/lib.rs and src/main.rs enforce:

  • #![forbid(unsafe_code)]
  • #![deny(clippy::unwrap_used)]
  • #![deny(clippy::expect_used)]
  • #![warn(clippy::pedantic)]
  • #![deny(warnings)]

These are enforced at compile time — any unwrap(), expect(), unsafe block, or clippy pedantic finding fails cargo clippy. Pedantic findings are fixed in code rather than silenced with allow.

The same lints are repeated in the [lints.rust] / [lints.clippy] tables in Cargo.toml. Crate-root attributes only govern their own crate, so the tables are what extend the rules to the integration tests in tests/; that is why test code uses match, let...else and panic! rather than unwrap()/expect().

Toolchain

rust-toolchain.toml pins the stable channel with rustfmt + clippy components. The project uses Rust edition 2024.