Skip to content

Editor integration

The lsp command turns diagnostics into an editor-ready payload: LSP Diagnostic objects and CodeAction quick-fixes, derived entirely from the parser's output.


The payload

sh
aowlsuggest lsp <file>          # or: … lsp --stdin --filename:buf.nim

emits one JSON object:

json
{
  "uri": "file:///abs/path/buf.nim",
  "diagnostics": [  LSP Diagnostic objects ],
  "codeActions": [  LSP CodeAction quick-fixes ]
}

Diagnostics

Each LSP Diagnostic uses fully 0-based ranges (the parser's 1-based line becomes 0-based; its column is already 0-based), the stable code, a source of "aowlparser", and — when the diagnostic has one — relatedInformation built from the related block, so an editor renders the secondary location (the ( an unclosed bracket was opened at) as its own marker instead of buried text.

json
{
  "range": { "start": {"line":0,"character":5}, "end": {"line":0,"character":6} },
  "severity": 1,
  "code": "assignment-in-condition",
  "source": "aowlparser",
  "message": "'=' assigns; this 'if' condition needs a comparison"
}

Severities map error → 1, warning → 2, hint → 4.

Code actions & "did you mean" ranking

Each diagnostic contributes every plausible auto-fix as a CodeAction carrying a WorkspaceEdit. When there is more than one — a mismatched bracket, for instance, can be repaired at the close or at the open — they are emitted as a ranked set: the first is marked isPreferred (what fix would apply), the rest are equally valid alternatives the editor offers the user.

json
[
  { "title": "change ']' to ')'", "kind": "quickfix", "isPreferred": true,
    "edit": { "changes": { "file:///…": [ { "range": , "newText": ")" } ] } } },
  { "title": "change the opening '(' to '['", "kind": "quickfix",
    "isPreferred": false,
    "edit": { "changes": { "file:///…": [ { "range": , "newText": "[" } ] } } }
]

The WorkspaceEdit ranges are converted from the same byte-offset edits the fix command applies, so an editor accepting the preferred action gets exactly the repair fix --write would have made.

Unsaved buffers

Editors check a buffer before it hits disk. --stdin feeds the current buffer in; --filename:NAME supplies the path to report in the uri and in diagnostics. This means the whole surface — diagnostics, related locations, and quick-fixes — works on in-flight edits, not just saved files. See Commands.

The LSP server

aowlsuggest lsp-server is a persistent Language Server over stdio, so an editor drives it directly rather than shelling out per file. It speaks JSON-RPC 2.0 with LSP's Content-Length framing and implements:

messagebehaviour
initializeadvertises textDocumentSync: 1 (Full) and codeActionProvider
textDocument/didOpenstore the buffer, publish diagnostics
textDocument/didChangereplace the buffer (full sync), re-publish
textDocument/didSavere-publish for the saved buffer
textDocument/didClosedrop the buffer, clear its diagnostics
textDocument/codeActionquick-fixes for diagnostics in the requested range
shutdown / exitclean teardown

Each didOpen/didChange runs aowlparser on the current buffer and pushes a textDocument/publishDiagnostics notification; the diagnostics and the codeAction responses are the exact objects described above. A minimal session:

→ initialize                     ← { capabilities: { textDocumentSync: 1, codeActionProvider: true } }
→ didOpen  "if x = 5:\n …"        ← publishDiagnostics [ assignment-in-condition ]
→ codeAction (line 0)            ← [ { title: "change '=' to '=='", … } ]
→ didChange "let ok = 1\n"        ← publishDiagnostics [ ]
→ shutdown / exit

CI formats: SARIF

For batch use, lint --format:sarif emits SARIF 2.1.0 — the format GitHub code scanning ingests to annotate pull requests. Rules are the distinct codes seen, described from the explain knowledge base; regions use SARIF's 1-based line and column convention. Two things make the SARIF production-grade:

  • fixes. Every diagnostic with a verified auto-fix (and its "did you mean" alternatives) carries a SARIF fix — a precise deletedRegion plus insertedContent — so GitHub renders it as a one-click Apply fix button in the PR, applying the exact edit fix --write would make.
  • partialFingerprints. Each result carries a primaryLocationLineHash derived from the rule id and the diagnostic's line content (not its line number), so GitHub tracks the same alert across commits instead of churning it when an unrelated edit shifts the line. An automationDetails.id keeps several uploads from colliding.
yaml
# a CI step (needs permissions: security-events: write)
- run: aowlsuggest lint . --format:sarif > aowlsuggest.sarif
- uses: github/codeql-action/upload-sarif@v3
  with: { sarif_file: aowlsuggest.sarif }

Prefer a hard pass/fail gate? lint returns non-zero on any error, on any run failure, and — with --max-warnings:N — when warnings exceed N:

sh
aowlsuggest lint --pedantic --max-warnings:0 .    # fail on any error OR warning

--format:json (a per-file breakdown plus a summary) is available too, so lint slots into any CI without SARIF.

aoughwl — self-hosted platform for things n stuff. Contact / Support on Discord for access to the private backends.