Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. which noyalib-lsp returns a path
  2. The binary is executable (chmod +x if needed)
  3. 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-length to refuse very large files
  • File an issue if the parse is structurally slow — the bench numbers in crates/noyalib-lsp/benches/lsp_handlers.rs are the reference

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

MethodDirectionBehaviour
initializeclient → serverReturns server capabilities (formatting, hover, didOpen / didChange / didClose, publishDiagnostics)
initializedclient → serverAcknowledged; server-side initialisation runs here
shutdownclient → serverAcknowledged; server prepares for exit
exitnotificationServer exits with code 0 (or 1 if shutdown was not requested)
textDocument/didOpennotificationDocument tracked in the open-documents map; publishDiagnostics runs against the new content
textDocument/didChangenotificationDocument content updated; publishDiagnostics runs against the new content
textDocument/didClosenotificationDocument removed from the open-documents map
textDocument/formattingrequestReturns a TextEdit[] that rewrites the document via noyalib::cst::format. Lossless: comments, anchors, indent style preserved
textDocument/hoverrequestReturns 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/publishDiagnosticsserver → clientEmitted 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.

MethodWhy no-op
textDocument/didSaveThe CST formatter doesn’t need a save signal — didChange already flushes
workspace/didChangeConfigurationNo tunable configuration exposed yet; future noyalib.fmt.indent etc. will land here
workspace/didChangeWatchedFilesDiagnostics run on the open buffer; on-disk changes are picked up on the next didOpen

Deliberately unsupported

MethodReason
textDocument/completionYAML’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/codeActionNo quick-fix logic implemented yet. Schema validation errors carry recovery hints in their messages but no WorkspaceEdit
textDocument/renameAnchor / alias renaming is a real ask but requires careful handling of cross-document references; planned post-v0.0.1
textDocument/referencesSame as rename — anchor reference graph
textDocument/definitionSame family
textDocument/documentSymbolOutline view; planned but not yet implemented
textDocument/foldingRangeEditors typically derive folding from indentation directly
workspace/symbolCross-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:

  1. Parse errors — every noyalib::Error::ParseWithLocation becomes a Diagnostic with severity Error, span attached. See crate noyalib’s errors.md for the full variant list.
  2. 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 a Diagnostic with severity Warning. The diagnostic’s code field 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

SuiteWhat it covers
crates/noyalib-lsp/tests/protocol.rsEnd-to-end JSON-RPC round-trips (subprocess-driven; not run under llvm-cov)
Per-handler unit tests in each *.rsDirect handler invocation without the JSON-RPC envelope
crates/noyalib-lsp/benches/lsp_handlers.rsPer-handler latency on small + Kubernetes-shaped inputs

The full pyramid is documented in docs/TESTING.md.

Language-server comparison

Surfacenoyalib-lspGeneric YAML language serverNative noyalib
Lossless CST editsYesImplementation-dependentYes
JSON Schema 2020-12 diagnosticsYesImplementation-dependentLibrary API
LSP stdio serverYesYesNo
Shared parser with noya-cliYesNoYes

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.rs is the transport shim: it frames messages and drives Server::handle_message. All behaviour lives in the library so cargo test covers it without an editor.
  • src/lib.rs holds Server, which owns the document store of open buffers, and handle_message, which parses the JSON-RPC envelope, routes by method, and returns a HandleOutcome of one of three kinds: reply (a response), notify (a server-initiated notification such as diagnostics), or silent. The capability set advertised at initialize is fixed: textDocumentSync (full), textDocument/formatting, and textDocument/hover, with textDocument/publishDiagnostics pushed after each change.
  • src/diagnostics.rs turns noyalib parse errors into LSP diagnostic objects (collect) and wraps them in the publishDiagnostics notification (publish_diagnostics).
  • src/format.rs implements formatting as a single TextEdit replacing 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.rs answers textDocument/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_of maps 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.

CategorySignalsSummary
Identity and README3/3Canonical structure and toolchain policy present
Documentation2/3Manual, architecture, and reference docs present
Build and install UX2/3Native build and task runner present
Releases and binaries2/3Automated releases present; full L3 evidence remains open
Packaging1/3Registry distribution only; Repology and reproducibility are open
CI quality gates2/3Matrix and coverage signals present
Supply chain2/3Foundation controls present; advisory-audit detection remains open
Community1/3Foundation 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 logo

noyalib-lsp

Language Server Protocol implementation for noyalib — YAML formatting, validation, and hover information delivered to any LSP-aware editor over stdio JSON-RPC.

Build Crates.io Docs.rs lib.rs OpenSSF Scorecard OpenSSF Best Practices


Contents


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-version in the manifest, enforced by the msrv-core CI 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 noyalib at the identical =0.0.X and 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 with Content-Length framing.

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/formatting runs through noyalib’s lossless CST. An already-canonical document produces an empty TextEdit[] — 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/publishDiagnostics with 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 methodWhat it does
initialize / initialized / shutdown / exitFull LSP lifecycle handshake.
textDocument/didOpen / didChange / didCloseFull-text document sync (TextDocumentSyncKind = 1).
textDocument/publishDiagnosticsParse-error diagnostics emitted on every open + change. Line / column locations.
textDocument/formattingFull-document TextEdit[] from the CST formatter. Empty array when the document is already canonical.
textDocument/hoverMarkdown 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/:

ScriptWhat it shows
handshake.shOne-shot initialize / initialized / shutdown / exit round-trip. Smoke test for protocol compliance.
format-on-save.shdidOpen → textDocument/formatting. Returns the TextEdit[] an editor would apply on save.
hover-cursor.shdidOpen → 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/applyEdit for 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:


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/formatting is no longer a silent no-op. It now calls noyalib::cst::format instead of a byte-faithful CST round-trip, so format-on-save actually reformats. Regression-tested.
  • noyalib pin =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

  • noyalib pin =0.0.15 → =0.0.16 and 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/formatting was a silent no-op. full_document_edits used 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 on main by calling noyalib::cst::format, with regression tests asserting a non-canonical document produces a real normalizing edit.
  • crossbeam-epoch RUSTSEC-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 on main afterward.

Engineering / CI (post-release, no user-facing change)

  • Signed-history enforcement, upstream audit imports, and a dependabot-vet auto-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.