Perl has lacked a proper modern LSP implementation. Other languages — Rust, TypeScript, Go, Python — have mature language servers with fast completions, reliable navigation, and full debugger integration. Perl's existing options were slow, incomplete, or required a working Perl runtime just to get basic editor features. perl-lsp fills that gap: a native Rust implementation of the Language Server Protocol and Debug Adapter Protocol for Perl 5, with its own parser and lexer, no Perl runtime required for IDE features.
perl-lsp is a workspace of Rust crates delivering a complete Perl 5 tooling stack: an LSP server (perllsp) implementing all 119 capabilities catalogued in features.toml (88 LSP + 24 DAP + 7 extension features), a DAP debug adapter, a recursive-descent parser, a context-aware lexer, and a semantic analyzer — packaged as a single native binary you can drop into any editor. It runs on Windows, macOS, and Linux.
VS Code — install the extension and you are done:
code --install-extension effortlessmetrics.perl-lsp-rsThe extension auto-downloads the matching perllsp binary for your platform.
Other editors — download a prebuilt binary from GitHub Releases, add it to your PATH, then point your LSP client at it:
-- Neovim (nvim-lspconfig)
require('lspconfig').perl_ls.setup { cmd = { "perllsp", "--stdio" } };; Emacs (eglot)
(add-to-list 'eglot-server-programs
'((perl-mode cperl-mode) . ("perllsp" "--stdio")))# Any generic LSP client
perllsp --stdio
Verify the install:
perllsp --healthFor a full walkthrough, see docs/tutorials/GETTING_STARTED.md.
Note: Do not use
cargo install perl-lsp— that name is owned by an unrelated project on crates.io. Usecargo install --path crates/perllspto build from source.
- Full LSP surface — completions, diagnostics, hover, go-to-definition, find references, rename, formatting, semantic tokens, inlay hints, code actions, code lens, workspace symbols; every capability in
features.tomlhas an implementation wired up (see what the numbers mean) - Native debug adapter — DAP breakpoints, stepping, stack frames, variable inspection, and evaluate; no wrapper script required
- Fast native parser — recursive-descent v3 parser with a context-aware lexer; validated against a curated CPAN corpus
- Semantic analysis — symbol resolution, scope tracking, Moose/Moo method modifiers and role composition
- Refactoring — extract variable, extract subroutine, workspace-scoped rename, subroutine inlining
- Diagnostics — dead code highlighting, strict/warnings diagnostics, perlcritic integration with walk-up discovery and explicit tooling warnings
- Zero-Perl dependency for IDE features — the server is a single native binary
- Windows first-class — install, path handling, and shell interactions are part of the release surface
The native Rust parser stack is the architectural center of the workspace — the LSP server, diagnostics, hover, completion, and every other IDE feature read from it directly. Tree-sitter integration is an interop surface layered over that core, not a dependency of it.
Different users walk in through different doors. Pick the one that matches your use case:
| You want to… | Use |
|---|---|
| Get Perl IDE features in an editor | VS Code extension (perl-lsp-rs) or the perllsp binary from Releases |
| Perl syntax support for tree-sitter consumers (Neovim, Helix, GitHub) | tree-sitter-perl-c — conventional C grammar binding |
| Query a Perl AST from Rust with tree-sitter-style ergonomics | tree-sitter-perl-rs — Rust-native facade over the v3 parser (in development) |
| Parse Perl from Rust directly with full fidelity | perl-parser (+ perl-lexer) — the recursive-descent v3 parser |
| Tokenize Perl only | perl-lexer — context-aware tokenizer, no parse tree |
| Resolve symbols and track scopes over a parsed Perl AST (including Moose/Moo method modifiers) | perl-semantic-analyzer — scope tracking, symbol extraction, role composition |
| Index and search Perl symbols across a whole project | perl-workspace-index — cross-file symbol index and refactoring orchestration |
| Validate Perl regex patterns for ReDoS, catastrophic backtracking, or embedded code execution | perl-regex — safety/complexity checks (not a regex parser) |
| Generate Perl fixtures or test against a curated corpus | perl-corpus — library plus perl-corpus CLI for proptest strategies, edge cases, and deterministic codegen |
| Debug Perl from a DAP-speaking editor | perl-dap via the perllsp binary's DAP server mode |
| Layer | Crates | Role |
|---|---|---|
| LSP server binary | crates/perllsp, crates/perl-lsp |
Protocol loop, request dispatch |
| Debug adapter | crates/perl-dap |
DAP server for stepping, breakpoints, evaluate |
| Parser stack (center) | crates/perl-parser, crates/perl-lexer, crates/perl-parser-core |
Recursive-descent v3 parser and context-aware lexer — all IDE features read from this |
| Semantic analysis | crates/perl-semantic-analyzer |
Scope tracking, symbol resolution, Moose/Moo handling |
| Workspace indexing | crates/perl-workspace-index |
Cross-file symbol index |
| LSP feature providers | crates/perl-lsp-* |
Per-feature crates (hover, definition, rename, …) |
| Tree-sitter interop | crates/tree-sitter-perl-c, crates/tree-sitter-perl-rs |
See split below |
Tree-sitter split. Two crates share the family name but play different roles:
tree-sitter-perl-c— the conventional C grammar binding, maintained for compatibility with tree-sitter consumers and as a reference point for comparison. Not on the LSP's critical path.tree-sitter-perl-rs— a Rust-native facade over the v3 parser that exposes tree-sitter-compatible ergonomics without the C grammar. In development.
See docs/README.md for the full crate map and design notes.
| What you need | Where to go |
|---|---|
| First-time setup | docs/tutorials/GETTING_STARTED.md |
| Editor-specific config | docs/how-to/EDITOR_SETUP.md |
| All configuration options | docs/reference/CONFIG.md |
| Commands reference | docs/reference/COMMANDS_REFERENCE.md |
| Upgrade guide | docs/how-to/UPGRADING.md |
| Troubleshooting | docs/how-to/TROUBLESHOOTING.md |
| Current status and metrics | docs/project/status/index.md |
| Release roadmap | docs/project/ROADMAP.md |
| Release history | RELEASE_HISTORY.md |
| Full docs index | docs/INDEX.md |
New contributor? Start with docs/contributing/FIRST_PR.md — clone to PR in five minutes.
cargo test --workspace --lib
cargo xtask fmt
cargo clippy --workspace
nix develop -c just ci-gate # required before mergeFind beginner-friendly issues:
gh issue list --label "good-first-issue" --state openSee CONTRIBUTING.md for the full contributor workflow.
Current release: v0.12.4 — public alpha. The 0.12.x line is building parser corpus confidence, diagnostic hardening, and distribution coverage toward the v0.13.0 public alpha announcement. See docs/project/ROADMAP.md for the milestone ladder and docs/project/status/index.md for live metrics.
The project tracks a few distinct metrics that are easy to conflate. Each one scopes a different question:
| Metric | Current | What it measures | What it does not measure |
|---|---|---|---|
| LSP/DAP capability coverage | 119 / 119 | Every capability catalogued in features.toml has an implementation wired up |
Per-capability correctness, completeness on edge cases, or subjective UX quality |
| Parser corpus — CPAN top 1000 | 95.3% (8931 / 9372) | File-level clean parse rate: share of files the parser processes without recording errors | Semantic fidelity of the AST, cross-file analysis, or any LSP-level correctness |
| Parser corpus — Ubuntu system Perl | 97.1% (6890 / 7095) | Same, against the Ubuntu system-installed Perl compatibility baseline | Same |
| Parser corpus — project corpus | 100.0% (91 / 91) | Deterministic regression baseline that must stay clean | Same |
| End-to-end UX confidence | qualitative | Currently covered by manual editor smoke workflows, the perl-lsp-ux-tests first-5-minutes harness, and open-issue burn-down — not a published number |
Anything about parser breadth, protocol catalog size, or capability count |
The last row is the important one: none of the automated metrics above measure whether a real editing session feels good. That's validated through workflow smoke tests, the perl-lsp-ux-tests harness, and the list of known gaps below, not by a dashboard.
Live numbers live in docs/project/status/parser.md; this table may lag a merge cycle.
The table below is the honest state of v0.13.0 rough edges. None block basic use; all affect realistic workflows.
- Workspace-wide rename slice — multi-root workspace support shipped in 0.12.x (PR #3984: per-folder config loading,
WorkspaceFolderState, cross-folder integration); workspace-wide rename and module-move are roughly 30% complete, conditionally in scope pending verification (#3522)
- Dynamic require / literal import —
require Module; Module->import('sym')with static string names: goto-def on the bareword should resolve to the definition site;@ISA/use parent/use basechains anduse Module qw(...)list imports already work; this is the remaining slice of the import visibility lane (#3476, tracked by umbrella #4246)
- Dynamic workspace configuration — per-folder
.perl-lsp.tomlis the supported v0.13 mechanism; fully dynamic per-folder scoping via theworkspace/configurationreverse-request flow is deferred (#3515)
These items were rough edges in the previous list and have since landed:
- Parser error recovery: unclosed block recovery (PR #4079), symbol extractor descends into partial
Errornodes (PR #4071) — #3499 closed - Import list bareword resolution for
use Module qw(...)and tag imports — #3472 closed use constantsymbols tracked in visible symbol table — #3475 closed- Pragma tracker:
use if, feature bundles, eval/sub-scoped pragma leakage (PRs #4050, #4038, #4052), conservativeeval STRINGhandling (PR #4052) — #3489 closed
Release artifacts include SBOM generation and provenance attestations. See docs/reference/SUPPLY_CHAIN_SECURITY.md.
Dual licensed under MIT or Apache-2.0: LICENSE-MIT / LICENSE-APACHE