pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
134 lines (119 loc) • 13 kB
Markdown
# Language Coverage
pi-lens supports **36+ languages** through dispatch runners and LSP integration.
Formatting uses a single selected formatter per file: explicit project config wins, otherwise pi-lens uses a smart default where supported, and config-first ecosystems do not autoformat without config.
Dispatch is diagnostics-oriented: automatic formatting and safe autofix happen in the post-write pipeline rather than through dispatch format-check runners.
| Language | LSP | Dispatch Runners | Formatter |
| --------------------- | --- | -------------------------------------------------------------------------------------------------------------- | ----------------------- |
| JavaScript/TypeScript | ✓ | lsp, ts-lsp, biome-check-json, tree-sitter, ast-grep-napi, type-safety, similarity, fact-rules, eslint, oxlint | biome, prettier |
| Python | ✓ | lsp, pyright, mypy (config-first), ruff-lint, tree-sitter | ruff, black |
| Go | ✓ | lsp, go-vet, golangci-lint, tree-sitter | gofmt |
| Rust | ✓ | lsp, rust-clippy, tree-sitter | rustfmt |
| Ruby | ✓ | lsp, rubocop, tree-sitter | rubocop, standardrb |
| C/C++ | ✓ | lsp, cpp-check, tree-sitter | clang-format |
| Shell | ✓ | lsp, shellcheck | shfmt |
| Fish | ✓ (fish-lsp) | lsp, fish-indent | fish_indent |
| CSS/SCSS/Less | ✓ | lsp, stylelint | biome, prettier |
| HTML | ✓ | lsp, htmlhint | prettier (with project config) |
| YAML | ✓ | lsp, yamllint, actionlint (GitHub workflows), trivy-config (opt-in; Kubernetes manifests, CloudFormation) | prettier (with project config) |
| JSON | ✓ | lsp, trivy-config (opt-in; CloudFormation templates only) | biome, prettier |
| Svelte | ✓ | lsp | oxfmt (needs `svelte` pkg installed + config `svelte: true`) |
| Vue | ✓ | lsp | prettier, oxfmt |
| SQL | — | sqlfluff | sqlfluff |
| Markdown | — | spellcheck, markdownlint, vale | prettier |
| Docker | ✓ | lsp, hadolint, trivy-config (opt-in) | — |
| PHP | ✓ | lsp, php-lint, phpstan | php-cs-fixer |
| PowerShell | ✓ | lsp, psscriptanalyzer | psscriptanalyzer-format |
| Prisma | ✓ | lsp, prisma-validate | — |
| C# | ✓ | lsp, dotnet-build | csharpier |
| F# | ✓ | lsp | fantomas |
| Java | ✓ | lsp, javac | google-java-format |
| Java + Lombok | ✓ | JDT LS launched with `-javaagent:<lombok.jar>` when Lombok is detected and a jar is found (`PI_LENS_LOMBOK_JAR` / `LOMBOK_JAR`, project `.lombok/lombok.jar`, or Maven/Gradle cache) | google-java-format |
| Kotlin | ✓ | lsp, ktlint, detekt | ktlint |
| Swift | ✓ | lsp, swiftlint | swiftformat |
| Dart | ✓ | lsp, dart-analyze | dart format |
| Lua | ✓ | lsp | stylua |
| Zig | ✓ | lsp, zig-check | zig fmt |
| Haskell | ✓ | lsp | ormolu |
| Elixir | ✓ (ElixirLS default, Expert alternate) | lsp, elixir-check, credo | mix format |
| Gleam | ✓ | lsp, gleam-check | gleam format |
| Typst | ✓ (tinymist) | — | typstyle (`.typ`, `.typc`) |
| OCaml | ✓ | lsp | ocamlformat |
| Clojure | ✓ | lsp | cljfmt |
| Terraform | ✓ | lsp, tflint, trivy-config (opt-in) | terraform fmt |
| Terragrunt | — | terragrunt | terragrunt hcl fmt |
| Nix | ✓ | lsp | nixfmt |
| TOML | ✓ | lsp, taplo | taplo |
| CMake | ✓ (cmake-language-server) | lsp | cmake-format |
| CUE | ✓ (syntax via cue lsp, evaluation via cue vet) | lsp, cue-vet | cue fmt |
### CUE tree-sitter grammar
`cue lsp` reports load and parse errors as you type but leaves conflicting
values and failed constraints to `cue vet` — the `cue-vet` auxiliary runner
(#1522) covers that gap, so together they give full coverage: syntax/parse
diagnostics, hover, definition, completion, code actions, and formatting from
the language server, plus evaluation-error validation from `cue vet` on every
edit (vetted at the PACKAGE level — the touched file's directory — with the
result filtered back to that file, since CUE packages are directory-scoped).
`.cue` files parse under tree-sitter with symbol (`#Definition`s, fields,
`let` bindings) and import queries (#1522), giving CUE the same structural
symbol search and import extraction as any other language, with two known
rough edges inherited from the young `tree-sitter-cue` grammar itself (not
this repo's queries):
- **Multi-hash raw strings** (`` ##"..."## ``, two or more `#` delimiters)
mis-parse regardless of content — the field's value becomes a bare
`identifier` node instead of a `string`, and the parser emits stray
top-level nodes outside the field entirely. Because the broken value node
can carry `#`-prefixed text, this can surface as a spurious symbol
reference in the extracted refs. Single-hash raw strings (`` #"..."# ``)
are unaffected.
- **Aliased field labels** (`X=name: value`) emit the alias identifier (`X`)
as its own spurious `property` symbol alongside the real field's correct
symbol, because the grammar exposes both identifiers as untagged siblings
under the same `label` node with no way to tell them apart structurally.
Both are upstream grammar limitations (tracked among
[eonpatapon/tree-sitter-cue](https://github.com/eonpatapon/tree-sitter-cue)'s
open issues), not something a query change here can fix.
## Non-core grammars
The `tree-sitter` runner and `symbol_search`/`module_report` structural
extraction ship a fixed set of core grammars; grammars for the other
languages in the table above (CUE included) are fetched lazily on first use
into web-tree-sitter's resolved package directory, verified against
`scripts/grammars.lock.json`. This resolution works on a runtime that cannot
resolve a bare package specifier — pi shipped as a `bun build --compile`
binary — via one shared subpath-based ladder
(`scripts/lib/web-tree-sitter-dir.mjs`, #3409); before that fix, non-core
languages could never be analysed on those hosts. A grammar that never loads
(fetch failure, unwritable target, an unsupported runtime) is recorded as a
`grammar_unavailable` **skip**, never a clean pass
(`clients/dispatch/runners/tree-sitter.ts`).
## Considered and skipped (2026-08-20 survey, closed out by #1757)
Recorded so these are not re-litigated. The 2026-08-20 survey's first pass
rejected `bandit` and `checkov` as duplicates of existing lanes; #1752's
review disproved both "already covered" claims against the actual code, and
#1757 closed the resulting gaps:
- **bandit** (Python SAST) — RESOLVED. ruff's `S` ruleset implements Bandit's
checks; `config/ruff/core.toml` now selects `S`, with `S101` (assert),
`S311` (non-cryptographic random), `S603`/`S607` (subprocess without
`shell=True` / partial executable path) excluded as over-firing on real
code (measured against the `requests` and `pip` packages — see the config
file's comments for the exclusion reasoning and hit counts). `C90`
(mccabe complexity) was evaluated and NOT enabled: it is a maintainability
metric, not a Bandit-equivalent security check, and its hit volume on a
large pre-existing codebase (379 in `pandas`) would drown the security
signal `S` adds. As always, a project-local ruff config overrides the
bundled one outright.
- **checkov** (IaC security) — the `trivy config` lane pi-lens's own dispatch
registry already carries (`clients/dispatch/runners/trivy-config.ts`,
opt-in via `trivy.enabled`) makes checkov a duplicate, not a gap: no
second full-scan tool is needed. That lane covers Kubernetes manifests,
Dockerfiles, and Terraform; #1757 added CloudFormation (yaml and json
templates, detected via `AWSTemplateFormatVersion` / SAM `Transform` /
`Type: AWS::*` heuristics) and fixed a real bug found while verifying
against the installed binary: the runner passed `--no-progress` to `trivy
config`, a flag that subcommand rejects (only `trivy fs` accepts it) —
every real invocation exited 1 and, because trivy prints its usage text to
stdout on a rejected flag, was misreported as a clean `succeeded` scan
rather than an errored one. Dropping the flag was the fix.
- **radon / lizard** (complexity) — still not enabled; see the C90 note
above. Unchanged conclusion, now backed by measurement rather than
assumption.
Known coverage holes with no tool currently clearing the adoption bar: Rust and Java dead-code detection, Ruby type checking (sorbet judged too heavy and idiosyncratic for a default lane).