Language Server

Every editor.
Zero org round-trips.

Nimbus ships an LSP 3.17 server for Apex. Schema-aware completion, live SOQL column validation, inline coverage hints, mutation score annotations, and one-click test runs — in VSCode, Neovim, Zed, Helix, JetBrains, Emacs, or anything that speaks LSP. All running against your local project. No org, no latency, no setup other thannimbus lsp.

See compile errors at write time, not deploy time — like "Method does not exist or incorrect signature" and the rest of the Apex errors reference.

What it does, today

Eight capabilities, all wired to the same parser, schema cache, and test data that powers the Nimbus CLI. The heavy lifting was already in the daemon; the LSP is a thin protocol layer over state we had.

Schema-aware completion

Type a.│ where a is an Account — 71 real fields pop up, sourced from your local .nimbus/schemas/. Custom __c fields included. No org round-trip. No 2-second lag.

Live SOQL column validation

Write [SELECT Namee FROM Account] — the typo gets a yellow squiggle before you reach the closing bracket. The same validator runs against Postgres when you execute the query.

apiVersion compatibility check

A class compiled at apiVersion=52.0 calling Database.getCursor (v66) gets a warning on the call site, not after a failed deploy. Reads the sibling *.cls-meta.xml; bump the apiVersion and the squiggle clears immediately. Suppress per line with // nimbus:ignore.

Coverage inlay hints

After a test run, each method displays its hit count inline: 3×, not covered, or the surviving mutant description (☠ survived: Shifted loop bound +1). Makes gaps impossible to miss.

Mutation score on class headers

Ghost text next to every class: ☣ 86.5% mutation. A quick glance tells you whether the tests in this file are actually testing anything.

Test flakiness warnings

Any @IsTest method that has failed intermittently across recent runs shows ⚠ flaky (12%) inline. Reads from .nimbus/history/, no separate tool to check.

Code lenses with one-click actions

▶ Run test / ◉ Debug / 🪄 Coverage above every @IsTest. ☣ Mutate on production classes. ⏱ Bench on test classes. No extension required — every LSP client renders them.

Document outline

Full tree of classes, methods, fields, inner classes, enums. Jump by symbol with Cmd+Shift+O in VSCode, :Outline in Helix, or whatever your editor's convention is.

Go-to-definition

F12 on a class name jumps to its .cls file. Works across force-app/, src/, and stubs/. Last-good-parse cache means it still works when the current file is transiently broken.

The one-screen demo

Type a SOQL literal with a deliberate typo. The yellow squiggle appears before you finish the line — sourced from the same schema cache the test runner uses, not from a remote org.

Click the bulb, apply "Fix: Name" (on the way), and the column name snaps to the nearest real match. Run the test inline with ▶ Run test. The whole loop — edit, validate, run — never leaves the editor and never touches the network.

No other Apex LSP does any of this, because no other Apex LSP has a local schema cache, a coverage collector, and a test runner sitting behind it. Nimbus does.

apex
public class OrderBook {
  public static void run(Id accountId) {
    // Type 'a.' → 71 Account fields auto-complete.
    Account a = [SELECT Id, Name FROM Account
                 WHERE Id = :accountId LIMIT 1];
    System.debug(a.Name);

    // Yellow squiggle on 'Namesaa' — live, from schema.
    List<Contact> cs = [
      SELECT Id, Namesaa
      FROM Contact WHERE AccountId = :accountId
    ];
  }
}

// Above every @IsTest method:
//   ▶ Run test  |  ◉ Debug  |  🪄 Coverage
// Ghost text beside tested lines:
//   3×   (hit count from last run)
//   ☠ survived: Changed integer 2 to 3
//   ⚠ flaky (12%) of recent runs failed

Six editors, six snippets

One binary, one protocol, every editor. Copy the config that matches yours. Nothing to install beyond nimbus.

VSCodeAuto-starts via the Nimbus extension
bash
// .vscode/settings.json (extension handles everything)
{
  "nimbus.lsp.enabled": true
}
Neovim (0.11+)Native LSP config
bash
-- ~/.config/nvim/init.lua
vim.lsp.config.nimbus = {
  cmd = { 'nimbus', 'lsp' },
  filetypes = { 'apex' },
  root_markers = { 'sfdx-project.json' },
}
vim.lsp.enable('nimbus')
vim.filetype.add({
  extension = { cls = 'apex', trigger = 'apex' },
})
ZedSettings file only
bash
// ~/.config/zed/settings.json
{
  "lsp": {
    "nimbus": { "binary": { "path": "nimbus", "arguments": ["lsp"] } }
  },
  "languages": { "Apex": { "language_servers": ["nimbus"] } },
  "file_types": { "Apex": ["cls", "trigger"] }
}
Helixlanguages.toml
bash
# ~/.config/helix/languages.toml
[language-server.nimbus-lsp]
command = "nimbus"
args = ["lsp"]

[[language]]
name = "apex"
file-types = ["cls", "trigger"]
roots = ["sfdx-project.json"]
language-servers = ["nimbus-lsp"]
JetBrainsVia LSP4IJ plugin
bash
# Settings → Language Servers → +
Name:    Nimbus Apex
Command: nimbus lsp
Mapping: *.cls, *.trigger
Emacseglot or lsp-mode
bash
;; eglot (Emacs 29+, built-in)
(with-eval-after-load 'eglot
  (add-to-list 'eglot-server-programs
               '(apex-mode . ("nimbus" "lsp"))))

Nimbus LSP vs Salesforce's

Salesforce ships their own Apex Language Server. It works, but every capability is bottlenecked on a live org connection and none of the test-data overlays exist there.

NimbusSalesforce Apex LSP
Zero-latency completionReads .nimbus/schemas/Account.json. Instant.Round-trip to the org describe API. 200–2000ms.
Works offlineEvery feature runs against local files.Requires an active org connection.
Coverage inlay hintsGhost text per method, every test run.None.
Mutation annotationsPer-class score + surviving mutants on the exact line.None.
Live SOQL validationEvery column checked as you type.Basic; relies on org metadata sync.
Test flakiness inlineReads .nimbus/history/; warns on unstable tests.None.
Editor-neutralWorks in VSCode, Neovim, Zed, Helix, JetBrains, Emacs.VSCode and Code Builder only.

Also shipped: full refactoring surface

Beyond completion and coverage hints, Nimbus LSP covers the workspace-wide refactoring capabilities developers rely on daily. All of it offline, all of it powered by the same parser + schema the test runner uses.

Find all references

Shift+F12 on any symbol — every caller across the repo. Powered by a workspace symbol index that rebuilds incrementally on every keystroke.

Rename symbol

F2 on a class, method, or field — workspace-wide edit applied atomically. 22 edits across 2 files when renamingCalculator on the testdata project.

Signature help

Floating parameter tooltip while you type a method call, with the active parameter bolded. Covers stdlib, user classes, and stays accurate even mid-keystroke.

Workspace symbol search

Cmd+T finds any class, method, or field across the repo. Ranks exact matches over prefix matches over substrings.

Code actions

Yellow bulb with SOQL auto-fix (NameeName), "Generate test stub" with schema-aware record construction, "Extract to @TestSetup", "Add @IsTest annotation".

Snippets

Type test → full @IsTest scaffold with Arrange/Act/Assert Tab-through placeholders. 12 hand-curated Apex idioms: debug, soql, trycatch, runAs, trigger, classTest…

Call hierarchy

Ctrl+Shift+H on any method — incoming and outgoing calls, resolved through the workspace symbol index.

Semantic tokens

Theme-aware syntax highlighting: annotations, keywords, stdlib namespaces, types, numbers, comments, strings. Customrequired modifier reserved for custom fields marked <required>true</required>.

Opinionated formatter

Conservative by design: normalises indentation, trims trailing whitespace, collapses excess blank lines. Preserves strings, SOQL, and comments verbatim. Idempotent on clean input.

Dead code detector

Faded Hint diagnostics on methods that have zero workspace references AND zero coverage hits. Skipsglobal/virtual to avoid false positives on public APIs.

Folding + selection + highlight

Brace-based code folding, Ctrl+W expand-selection, same-symbol highlighting in the active file.

Type / impl / declaration jumps

Separate protocol verbs for "type of this variable", "implementations of this interface", and "declaration (opposed to definition)".

Still on the list

Pragmatic follow-ups. Most valuable on long-lived codebases. All optional for the daily loop.

Inline debug values

Ghost-text variable values beside running lines during a debug session. Blocked on LSP ↔ DAP co-hosting — they're separate processes today.

Coverage-filtered call hierarchy

"Which tests actually exercise this method?" filter on incoming calls, using the coverage overlay.

Refactor moves

Extract variable, inline variable, inline method, convert field to property with getter/setter. Each independent, ~1 day's work.

See LSP_ROADMAP.md in the source for the full plan.

Try it

If you have Nimbus installed, the LSP is already available. The VSCode extension auto-starts it; other editors need the 5-line config above. The server is the same process for everyone — you get identical capability, regardless of which editor you write your Apex in.

Completion and diagnostics work on a fresh clone before the first nimbus sync; stock SObjects and the stdlib are built in. Custom object fields appear after your first sync. Coverage and mutation inlays appear after your first nimbus test --coverage run.

Full configuration and troubleshooting docs live in the reference.

bash
# Already installed Nimbus? LSP is ready.
nimbus lsp             # What editors launch
nimbus lsp --log /tmp/nimbus-lsp.log  # Debug mode

# Not yet installed:
brew install nimbus-solution/nimbus/nimbus
# or download from testnimbus.dev