The noyalib-lsp Manual
A YAML language server: formatting, diagnostics, and hover, built on noyalib. noyalib-lsp is one of the six crates of the noyalib family and
releases in lockstep with the core library at the identical =0.0.X.
This manual is the rendered form of the Markdown in the repository’s
docs/
directory; the repository copy is canonical. The family manual for the
core library, with the user guide, migrations, and policies, is at
https://sebastienrousseau.github.io/noyalib/manual/.
Pages
Install, quick start, and the full surface are in the README. The CHANGELOG is the complete release record.
Editor setup for noyalib-lsp
The noyalib LSP server speaks the standard Language Server Protocol over stdio, so any editor with LSP client support can wire it up. This page collects the working configurations for the major editors.
For the full list of methods the server implements, see
protocol-coverage.md.
Install
cargo install noyalib-lsp
The binary lands in ~/.cargo/bin/noyalib-lsp. The remainder of
this document assumes that path is on PATH; if not, substitute
the absolute path in each editor’s config.
VS Code
The recommended path is the noyalib VS Code extension, which drives the LSP server and configures it automatically. Install it from the Marketplace:
ext install sebastienrousseau.noyalib
The listing goes live with the first release published under the
sebastienrousseau publisher. Until then, every CI run attaches a
.vsix you can install with “Extensions: Install from VSIX…”.
For a manual setup (e.g. you want to drive your own LSP client),
add to settings.json:
{
"yaml.server.path": "noyalib-lsp",
"[yaml]": {
"editor.defaultFormatter": "sebastienrousseau.noyalib",
"editor.formatOnSave": true
}
}
Neovim
nvim-lspconfig
-- ~/.config/nvim/init.lua
local lspconfig = require("lspconfig")
local configs = require("lspconfig.configs")
if not configs.noyalib then
configs.noyalib = {
default_config = {
cmd = { "noyalib-lsp" },
filetypes = { "yaml" },
root_dir = lspconfig.util.find_git_ancestor,
single_file_support = true,
},
}
end
lspconfig.noyalib.setup({
on_attach = function(client, bufnr)
-- Format on save.
vim.api.nvim_create_autocmd("BufWritePre", {
buffer = bufnr,
callback = function()
vim.lsp.buf.format({ bufnr = bufnr })
end,
})
end,
})
Native vim.lsp (Neovim 0.11+)
vim.lsp.config.noyalib = {
cmd = { "noyalib-lsp" },
filetypes = { "yaml" },
root_markers = { ".git" },
}
vim.lsp.enable({ "noyalib" })
Helix
~/.config/helix/languages.toml:
[[language]]
name = "yaml"
language-servers = ["noyalib-lsp"]
auto-format = true
[language-server.noyalib-lsp]
command = "noyalib-lsp"
Zed
~/.config/zed/settings.json:
{
"lsp": {
"noyalib-lsp": {
"binary": {
"path": "noyalib-lsp",
"arguments": []
}
}
},
"languages": {
"YAML": {
"language_servers": ["noyalib-lsp"],
"format_on_save": "on"
}
}
}
Emacs
lsp-mode
;; ~/.emacs.d/init.el
(with-eval-after-load 'lsp-mode
(add-to-list 'lsp-language-id-configuration '(yaml-mode . "yaml"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "noyalib-lsp")
:major-modes '(yaml-mode yaml-ts-mode)
:server-id 'noyalib-lsp)))
(add-hook 'yaml-mode-hook #'lsp-deferred)
(add-hook 'before-save-hook #'lsp-format-buffer)
eglot (Emacs 29+)
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs
'((yaml-mode yaml-ts-mode) . ("noyalib-lsp"))))
(add-hook 'yaml-mode-hook 'eglot-ensure)
(add-hook 'yaml-ts-mode-hook 'eglot-ensure)
Sublime Text
Install the LSP package, then add to
Preferences → Package Settings → LSP → Settings:
{
"clients": {
"noyalib-lsp": {
"enabled": true,
"command": ["noyalib-lsp"],
"selector": "source.yaml"
}
}
}
Kakoune
~/.config/kak/kakrc:
hook global WinSetOption filetype=yaml %{
lsp-enable-window
set-option global lsp_servers %{
[noyalib-lsp]
root_globs = [".git"]
args = []
}
}
JetBrains IDEs (IntelliJ, RustRover, GoLand, etc.)
Install the LSP4IJ plugin, then in Settings → LSP:
- Add a new server named
noyalib-lsp - Command:
noyalib-lsp - File mappings:
*.yaml,*.yml
Verifying the server is working
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}' \
| noyalib-lsp
Should respond with a JSON-RPC result containing the server’s
capabilities. If it hangs or errors, verify:
which noyalib-lspreturns a path- The binary is executable (
chmod +xif needed) - The version matches your editor’s expectations
(
noyalib-lsp --version)
Troubleshooting
Diagnostics don’t appear
Most editors require the LSP client to be configured for the YAML file type. Confirm with:
:LspInfo " Neovim with nvim-lspconfig
:lsp-info " Helix
M-x lsp-describe-session " Emacs / lsp-mode
Format-on-save doesn’t run
The server provides textDocument/formatting. If the editor
doesn’t trigger it on save, set the editor’s “format on save”
option and ensure the LSP client is the registered formatter
for YAML (some editors prefer the built-in tree-sitter formatter
unless told otherwise — see each editor’s section above for the
explicit override).
Hover descriptions show null
Hover content depends on a JSON Schema being available for the
document. Configure the schema via the editor’s standard YAML
schema mapping (e.g. yaml.schemas in VS Code, or the
yaml-language-server schema selector
inline pragma). With no schema, hover returns the parsed value
type only.
High CPU on large files
Large YAML files (>10 MB) trigger a full-document re-parse on
every keystroke under the default didChange debounce. Either:
- Increase the debounce in your editor’s LSP client settings
- Use
--max-document-lengthto refuse very large files - File an issue if the parse is structurally slow — the bench
numbers in
crates/noyalib-lsp/benches/lsp_handlers.rsare the reference
Related
- Protocol coverage — which LSP methods are implemented
- Crate README — full server overview
LSP protocol coverage
Which LSP methods noyalib-lsp implements, which it acknowledges
but treats as no-ops, and which it deliberately does not support.
Coverage is conservative: every implemented method has end-to-end
tests in crates/noyalib-lsp/tests/ and behavioural tests
exercising the JSON-RPC envelope.
LSP version targeted: 3.17.
Implemented
| Method | Direction | Behaviour |
|---|---|---|
initialize | client → server | Returns server capabilities (formatting, hover, didOpen / didChange / didClose, publishDiagnostics) |
initialized | client → server | Acknowledged; server-side initialisation runs here |
shutdown | client → server | Acknowledged; server prepares for exit |
exit | notification | Server exits with code 0 (or 1 if shutdown was not requested) |
textDocument/didOpen | notification | Document tracked in the open-documents map; publishDiagnostics runs against the new content |
textDocument/didChange | notification | Document content updated; publishDiagnostics runs against the new content |
textDocument/didClose | notification | Document removed from the open-documents map |
textDocument/formatting | request | Returns a TextEdit[] that rewrites the document via noyalib::cst::format. Lossless: comments, anchors, indent style preserved |
textDocument/hover | request | Returns the parsed value type at the cursor; if a JSON Schema is associated with the document, returns the schema’s description field for the value at the cursor path |
textDocument/publishDiagnostics | server → client | Emitted on every didOpen / didChange. Carries parse errors with span attached; carries schema-validation errors when a schema is associated |
Acknowledged but no-op
These method names are recognised so the client doesn’t see “method not found” errors, but the server returns an empty result. They’re candidates for future implementation when there’s a proven user request.
| Method | Why no-op |
|---|---|
textDocument/didSave | The CST formatter doesn’t need a save signal — didChange already flushes |
workspace/didChangeConfiguration | No tunable configuration exposed yet; future noyalib.fmt.indent etc. will land here |
workspace/didChangeWatchedFiles | Diagnostics run on the open buffer; on-disk changes are picked up on the next didOpen |
Deliberately unsupported
| Method | Reason |
|---|---|
textDocument/completion | YAML’s grammar makes meaningful completions context-heavy. The LSP intentionally defers to the YAML schema-aware completion in editor extensions like yaml-language-server until we have a clear value proposition |
textDocument/codeAction | No quick-fix logic implemented yet. Schema validation errors carry recovery hints in their messages but no WorkspaceEdit |
textDocument/rename | Anchor / alias renaming is a real ask but requires careful handling of cross-document references; planned post-v0.0.1 |
textDocument/references | Same as rename — anchor reference graph |
textDocument/definition | Same family |
textDocument/documentSymbol | Outline view; planned but not yet implemented |
textDocument/foldingRange | Editors typically derive folding from indentation directly |
workspace/symbol | Cross-document symbols; out of scope for a YAML server |
Methods not listed above return methodNotFound per the LSP spec.
Diagnostic coverage
textDocument/publishDiagnostics payloads are produced by:
- Parse errors — every
noyalib::Error::ParseWithLocationbecomes aDiagnosticwith severityError, span attached. See crate noyalib’s errors.md for the full variant list. - Schema validation errors — when a JSON Schema is
associated with the document (via the editor’s schema-mapping
config or an inline
# yaml-language-server: $schema=...pragma), each violation becomes aDiagnosticwith severityWarning. The diagnostic’scodefield carries the schema keyword that failed (type,enum,required, etc.).
Diagnostics are produced eagerly on every didOpen / didChange.
There is no debounce — the parser is fast enough that 10ms
keystroke cadence on a 50KB document is comfortably under one
event loop tick.
Capabilities advertised at initialize
{
"capabilities": {
"textDocumentSync": {
"openClose": true,
"change": 1
},
"documentFormattingProvider": true,
"hoverProvider": true,
"diagnosticProvider": {
"interFileDependencies": false,
"workspaceDiagnostics": false
}
},
"serverInfo": {
"name": "noyalib-lsp",
"version": "0.0.1"
}
}
textDocumentSync.change is 1 (full-document sync). Incremental
sync (2) would shave a small amount off the wire bandwidth on
very large files but adds protocol complexity that hasn’t paid off
in benchmarks. Revisit if profiling shows it matters.
Where the JSON-RPC envelope is handled
crates/noyalib-lsp/src/
├── lib.rs # Server type, handle_message dispatch
├── main.rs # stdio loop, main()
├── format.rs # textDocument/formatting handler
├── hover.rs # textDocument/hover handler
└── diagnostics.rs # publishDiagnostics builder
The dispatch in lib.rs::handle_message is a simple match on the
incoming method name; each handler returns a HandleOutcome
(reply / notify / silent) that tells the transport layer
what to send back.
Testing
| Suite | What it covers |
|---|---|
crates/noyalib-lsp/tests/protocol.rs | End-to-end JSON-RPC round-trips (subprocess-driven; not run under llvm-cov) |
Per-handler unit tests in each *.rs | Direct handler invocation without the JSON-RPC envelope |
crates/noyalib-lsp/benches/lsp_handlers.rs | Per-handler latency on small + Kubernetes-shaped inputs |
The full pyramid is documented in
docs/TESTING.md.
Related
- Editor setup — wiring per editor
- Crate README — install + crate-level overview
Language-server comparison
| Surface | noyalib-lsp | Generic YAML language server | Native noyalib |
|---|---|---|---|
| Lossless CST edits | Yes | Implementation-dependent | Yes |
| JSON Schema 2020-12 diagnostics | Yes | Implementation-dependent | Library API |
| LSP stdio server | Yes | Yes | No |
| Shared parser with noya-cli | Yes | No | Yes |
This table compares integration shape, not completeness or performance. Consult the upstream documentation and test the schema dialects, editor extensions, and formatting behaviour required by a deployment.
See protocol coverage for the implemented methods and editor setup for host configuration.
Benchmarks
The lsp_handlers Criterion harness measures representative handler paths.
cargo bench --bench lsp_handlers
Record the CPU, operating system, Rust version, commit, document size, feature set, and complete command with results. Compare runs on the same machine and power profile. CI builds benchmark targets as a regression smoke test; shared runner timing is not a release gate.
Architecture
noyalib-lsp is a Language Server Protocol implementation for YAML,
built on noyalib’s parser, CST formatter, and error locations. The
transport is stdio with the standard Content-Length framing.
Layers
src/main.rsis the transport shim: it frames messages and drivesServer::handle_message. All behaviour lives in the library socargo testcovers it without an editor.src/lib.rsholdsServer, which owns the document store of open buffers, andhandle_message, which parses the JSON-RPC envelope, routes by method, and returns aHandleOutcomeof one of three kinds:reply(a response),notify(a server-initiated notification such as diagnostics), orsilent. The capability set advertised atinitializeis fixed:textDocumentSync(full),textDocument/formatting, andtextDocument/hover, withtextDocument/publishDiagnosticspushed after each change.src/diagnostics.rsturns noyalib parse errors into LSP diagnostic objects (collect) and wraps them in thepublishDiagnosticsnotification (publish_diagnostics).src/format.rsimplements formatting as a singleTextEditreplacing the whole document with noyalib’s CST formatter output (full_document_edits). One edit keeps the response self-contained; the client needs no cross-document reasoning to apply it.src/hover.rsanswerstextDocument/hover(hover_at): a small Markdown card with the position and the document’s overall type when the buffer parses, or the parse error at the cursor when it does not.byte_offset_ofmaps an LSP line and column to a byte offset. Schema-driven hover descriptions are tracked as future work.
Data flow
Editor sends didOpen or didChange with the full text; the server
stores it, parses it, and pushes diagnostics. Formatting and hover read
the stored text on request. Nothing is written to disk.
Testing
Unit tests drive Server::handle_message with raw JSON-RPC strings.
Two libFuzzer targets feed it arbitrary messages (fuzz_handle_message)
and arbitrary document text through didChange (fuzz_did_change);
CI replays the seed corpus on every push. docs/protocol-coverage.md
lists which LSP methods are implemented.
Lockstep
The crate pins noyalib at the identical =0.0.X and releases with
it (core ADR-0005).
Engineering policies
Version and dependency policy
noyalib-lsp releases in strict lockstep with noyalib. Version 0.0.58 pins
the core at exactly =0.0.58. Release work uses feat/v0.0.58; each subsequent
iteration increments exactly 0.0.1.
Minimum Rust version
The minimum supported Rust version is 1.89.0, declared in the manifest and verified by CI. A floor increase is a breaking-axis change and requires a changelog entry.
Compatibility
LSP methods, capability declarations, diagnostic codes, edit semantics, and
command-line behaviour are public. During 0.0.x, the patch component is the
breaking axis.
Family-wide policies live in the core
POLICIES.md.
Repository standard compliance grade
Assessment date: 2026-09-20. Source rubric:
/Users/seb/Code/REPO-STANDARD.md. This is a conservative static assessment of
checked-in evidence, not a substitute for successful CI runs.
Result
15/24 signals (62%). README template: pass. Strict candidate tier: L1.
| Category | Signals | Summary |
|---|---|---|
| Identity and README | 3/3 | Canonical structure and toolchain policy present |
| Documentation | 2/3 | Manual, architecture, and reference docs present |
| Build and install UX | 2/3 | Native build and task runner present |
| Releases and binaries | 2/3 | Automated releases present; full L3 evidence remains open |
| Packaging | 1/3 | Registry distribution only; Repology and reproducibility are open |
| CI quality gates | 2/3 | Matrix and coverage signals present |
| Supply chain | 2/3 | Foundation controls present; advisory-audit detection remains open |
| Community | 1/3 | Foundation governance present; L2 template and docs-lint signals are open |
The cumulative tier remains L1. Priority work is packaging/distribution tracking, explicit advisory-audit CI, and audit-visible issue/PR templates and markdown lint enforcement.
Detailed README reference
noyalib-lsp
Language Server Protocol implementation for noyalib — YAML formatting, validation, and hover information delivered to any LSP-aware editor over stdio JSON-RPC.
Contents
- Install — Cargo, distro packages
- Requirements — toolchain floor, platforms, the core pin
- Quick Start — smoke test
- Why this approach? — design rationale
- Surface — LSP methods supported
- Editor configuration — VS Code, Zed, Neovim, Helix, Sublime
- Examples — JSON-RPC scripts
- Performance — formatter cost
- When not to use noyalib-lsp
- Documentation
- License
Install
cargo install noyalib-lsp
Releases ship the crate archive with a CycloneDX SBOM and
sigstore bundles. Pre-built binary tarballs and distro packages
are not published yet; cargo install is the supported path.
MSRV: Rust 1.89.0, the floor shared across the noyalib family and the
maintainer’s other Rust crates (noyalib ADR-0014). The
toolchain-driven floor is lower: criterion 0.8 (the benchmark dev-dependency) declares
rust-version = 1.86, so cargo check --all-targets and the
bench suite fail on 1.85 (criterion@0.8.2 requires rustc 1.86),
though cargo check --lib still builds. We publish the number we
verify.
Requirements
- Rust 1.89.0 or newer to build from source:
rust-versionin the manifest, enforced by themsrv-coreCI job on every push. - Any tier-1 platform. CI runs the tests on Linux, macOS, and Windows with the stable, beta, and nightly toolchains; stable is the gate, beta and nightly are early warning.
- The matching core. This crate pins
noyalibat the identical=0.0.Xand releases in lockstep with it; Cargo resolves that pin for you. - An LSP client (any editor in
docs/editor-setup.md); the server speaks stdio withContent-Lengthframing.
Quick Start
# Smoke test — drives a one-shot LSP handshake over stdio.
noyalib-lsp --version
# As a child process spawned by your editor (the typical path).
# See "Editor configuration" below for VS Code / Zed / Neovim /
# Helix / Sublime examples.
Why this approach?
The market has two YAML language servers (yaml-language-server,
taplo-style hybrids) and both make tradeoffs noyalib-lsp avoids:
- Byte-faithful formatting.
textDocument/formattingruns through noyalib’s lossless CST. An already-canonical document produces an emptyTextEdit[]— your editor doesn’t churn whitespace on save. Comments stay where they were; indent width follows the file’s dominant style; only quoting and inter-key whitespace normalise. - Real diagnostics. Parse errors flow through
textDocument/publishDiagnosticswith line / column locations the editor’s gutter can highlight directly. No best-effort parsers, no recovery hand-waving. - Schema-aware hover. When a JSON Schema is attached, hover surfaces the resolved field type. Schema descriptions land in the hover card in a follow-up.
- Stdio transport. Standard
Content-Length-framed JSON-RPC 2.0. Works with every LSP-compliant client; no client-specific protocol extensions. - Pure-Rust, zero
unsafe. Same#![forbid(unsafe_code)]guarantee as the noyalib core library.
The whole thing is ~5 KLOC of Rust; the heavy lifting is in the
noyalib library, the LSP wrapper just bridges JSON-RPC to the
library’s CST + parser surface.
Surface
| LSP method | What it does |
|---|---|
initialize / initialized / shutdown / exit | Full LSP lifecycle handshake. |
textDocument/didOpen / didChange / didClose | Full-text document sync (TextDocumentSyncKind = 1). |
textDocument/publishDiagnostics | Parse-error diagnostics emitted on every open + change. Line / column locations. |
textDocument/formatting | Full-document TextEdit[] from the CST formatter. Empty array when the document is already canonical. |
textDocument/hover | Markdown card with cursor position + document type. Schema-driven descriptions tracked for follow-up. |
Server capabilities response includes textDocumentSync = 1,
documentFormattingProvider = true, hoverProvider = true.
Future capabilities (rangeFormatting, documentSymbols,
codeActions) are gated behind the same lossless-CST surface
in the library.
Editor configuration
Visual Studio Code
The extension lives in editors/vscode. Once the
listing is live it installs from the Marketplace:
ext install sebastienrousseau.noyalib
CI also packages a .vsix on every push, so you can download it from a
workflow run (or build it with npm install && npm run package in that
directory) and use “Extensions: Install from VSIX…”. Either way it
starts the noyalib-lsp binary on your PATH; point at another one
with:
{
"noyalib.path": "/usr/local/bin/noyalib-lsp",
"[yaml]": {
"editor.defaultFormatter": "sebastienrousseau.noyalib",
"editor.formatOnSave": true
}
}
Zed
~/.config/zed/settings.json:
{
"languages": {
"YAML": {
"language_servers": ["noyalib-lsp"],
"format_on_save": "on"
}
},
"lsp": {
"noyalib-lsp": {
"binary": { "path": "noyalib-lsp" }
}
}
}
Neovim (via nvim-lspconfig)
require("lspconfig.configs").noyalib = {
default_config = {
cmd = { "noyalib-lsp" },
filetypes = { "yaml" },
root_dir = require("lspconfig.util").find_git_ancestor,
},
}
require("lspconfig").noyalib.setup {
on_attach = function(_, bufnr)
vim.api.nvim_create_autocmd("BufWritePre", {
buffer = bufnr,
callback = function() vim.lsp.buf.format() end,
})
end,
}
Helix
~/.config/helix/languages.toml:
[[language]]
name = "yaml"
language-servers = ["noyalib-lsp"]
auto-format = true
[language-server.noyalib-lsp]
command = "noyalib-lsp"
Sublime Text (via LSP package)
~/.config/sublime-text/Packages/User/LSP.sublime-settings:
{
"clients": {
"noyalib-lsp": {
"enabled": true,
"command": ["noyalib-lsp"],
"selector": "source.yaml"
}
}
}
Examples
Editor-driving demos under
examples/:
| Script | What it shows |
|---|---|
handshake.sh | One-shot initialize / initialized / shutdown / exit round-trip. Smoke test for protocol compliance. |
format-on-save.sh | didOpen → textDocument/formatting. Returns the TextEdit[] an editor would apply on save. |
hover-cursor.sh | didOpen → textDocument/hover at a specific (line, column). |
Each script pipes a sequence of Content-Length-framed JSON-RPC
messages into noyalib-lsp over stdio and prints the response
stream. POSIX-shell only — no jq, no node dependencies.
chmod +x crates/noyalib-lsp/examples/*.sh
crates/noyalib-lsp/examples/handshake.sh
Performance
textDocument/formatting for a 1 MiB YAML document on Apple
M-series ≈ 12 ms (the same wall-clock as noyafmt --write
since both share the CST). For per-keystroke formatting under
human-perceivable latency, the server returns an empty
TextEdit[] when the document is already canonical, so the
editor avoids a round-trip on every didChange after a save.
textDocument/publishDiagnostics runs on every didChange;
parse cost on a freshly-edited buffer is dominated by the byte
range that changed, not the buffer size.
When not to use noyalib-lsp
- You want OpenAPI / AsyncAPI / Kubernetes-CRD hover docs out
of the box. That comes from the JSON Schema attached to
the buffer. noyalib-lsp doesn’t ship a schema registry; you
point it at a schema explicitly via the
yaml.schemas(style) settings exposed by your editor. - You need WebDAV-style multi-document operations
(
workspace/applyEditfor cross-file refactor). Not implemented yet; tracked as a v0.1.x capability extension.
Documentation
The four entry points, identical across every repo in the family:
-
User Manual — this crate’s rendered book: its guides, architecture, and release notes; the family manual for the core library is at https://sebastienrousseau.github.io/noyalib/manual/
-
API reference — rustdoc on docs.rs
-
Developer docs — this repo’s dev entry point, pointing at the family guide
-
Ecosystem map — the six crates, the lockstep model, the scorecard
-
Engineering policies (MSRV, SemVer, security, performance, concurrency, platform support, feature flags):
docs/POLICIES.md -
Security policy:
SECURITY.md -
API reference: https://docs.rs/noyalib-lsp
-
Editor setup (VS Code, Neovim, Emacs, Helix, Zed, Sublime):
docs/editor-setup.md -
Protocol coverage (which LSP methods are implemented):
docs/protocol-coverage.md -
Workspace README: https://github.com/sebastienrousseau/noyalib#readme
-
LSP specification: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
Conformance
Every push runs the official yaml-test-suite
through this server’s diagnostics, from the same vendored suite and the same
core commit as the noyalib core: 406 of 406 (valid cases produce no
diagnostic, invalid cases produce one at the failing position). A two-document
configuration that uses most of YAML at once (tests/fixtures/ultra-complex/)
opens with no diagnostics. Details and the family table:
noyalib.com/conformance.
License
Dual-licensed under Apache 2.0 or MIT, at your option.
noyalib-lsp v0.0.17 Release Notes
Lockstep release with noyalib v0.0.17 (ADR-0005: publishes =0.0.17
pinned to the core).
What changed
textDocument/formattingis no longer a silent no-op. It now callsnoyalib::cst::formatinstead of a byte-faithful CST round-trip, so format-on-save actually reformats. Regression-tested.noyalibpin=0.0.16->=0.0.17; crate version bumped in lockstep.
Repository hardening (CI/docs only)
Coverage, MSRV, CodeQL, and OpenSSF Scorecard gates plus upstream cargo-vet audit imports were added across the satellites in this cycle; branch protection now makes commit signing unskippable. No user-facing change.
Upgrading
Requires Rust 1.86.0+.
noyalib-lsp v0.0.16 Release Notes
Lockstep release with noyalib v0.0.16 (ADR-0005 strict-lockstep: the
LSP server publishes =X.Y.Z pinned to the core).
What changed
noyalibpin=0.0.15→=0.0.16and the crate version bumped in lockstep.- MSRV raised 1.85.0 → 1.86.0, matching the single lockstep floor adopted in v0.0.16. The crate builds cleanly on 1.86.
- Release workflow publish steps made idempotent for clean re-runs.
Known issues at v0.0.16 — corrected after release (ships in v0.0.17)
Two items were not in the v0.0.16 tag and are on main for the next
release. They are called out here so the record is accurate:
textDocument/formattingwas a silent no-op.full_document_editsused a byte-faithful CST round-trip (parse_document().to_string()), which equals its input for every parseable document, so the server always returned an empty edit list — format-on-save did nothing. The parent changelog stated this was fixed in v0.0.16; it was not. It is now fixed onmainby callingnoyalib::cst::format, with regression tests asserting a non-canonical document produces a real normalizing edit.crossbeam-epochRUSTSEC-2026-0204 (invalid-pointer-dereference) was present in the v0.0.16 lockfile via a transitive dependency and bumped to the patched 0.9.20 onmainafterward.
Engineering / CI (post-release, no user-facing change)
- Signed-history enforcement, upstream audit imports, and a
dependabot-vetauto-refresh workflow. - New CI gates brought to parity with the core: a coverage gate (measured 95.5 % regions / 97.3 % functions / 95.5 % lines against the 93/96/94 floor), an MSRV gate, CodeQL, and OpenSSF Scorecard.
What did not change
- The JSON-RPC / stdio LSP transport and message framing.
#![forbid(unsafe_code)]— intact.
Upgrading
noyalib-lsp = "0.0.16"
Requires Rust 1.86.0+. If you rely on textDocument/formatting,
wait for v0.0.17, where it actually reformats.