@prisma-next/language-server
Internal package. This package is an implementation detail of prisma-next and is published only to support its runtime. Its API is unstable and may change without notice. Do not depend on this package directly; install prisma-next instead.
The Prisma Next language server speaks the Language Server Protocol over stdio for PSL schema inputs declared in a project's prisma-next.config.ts. It is launched by the prisma-next lsp subcommand, so editor features come from the project's own @prisma-next version and stay version-matched by construction.
Scope
Supported capabilities are intentionally narrow: parse diagnostics, whole-document formatting, folding ranges, full/range semantic tokens, model field type completion, descriptor-backed generic block parameter completion, and declaration keyword completion for configured PSL inputs. Formatting is only available for documents listed in contract.source.inputs, uses @prisma-next/psl-parser/format, and applies formatter options from the project's Prisma config formatter block. Semantic tokens use the standard LSP token taxonomy advertised by the server; they do not introduce Prisma-specific token names or a second parser. Completion is only available for open configured PSL inputs. At model field type positions, it suggests configured scalar types plus visible model, composite type, scalar, type-alias, and namespace qualifier candidates from the current project symbol table; bare positions offer namespace segments such as auth., and after a namespace qualifier the provider suggests visible model and composite type members inside that namespace. The classifier also accepts contract-space-qualified type-position syntax such as supabase:auth.User when the namespace data is visible in the current cached artifacts. Inside descriptor-backed generic block bodies, it suggests declared parameter keys and excludes keys already present in sibling entries. At document top-level declaration positions, it suggests native PSL block keywords model, type, types, and namespace, plus descriptor-backed generic block keywords from pslBlockDescriptors. Inside namespace bodies, declaration keyword completion suggests only namespace-valid native keywords model and type, plus descriptor-backed generic block keywords; it does not suggest nested namespace or types. Declaration keyword items are snippets only when the client advertises textDocument.completion.completionItem.snippetSupport === true; otherwise the server returns plain-text edits for the same labels. Ordinary PSL @ / @@ attribute completions, attribute argument completions, generic block parameter value completions, generic block value completions, relation-aware completions, and new external contract-space candidate discovery are not part of this slice. Hover, navigation, range formatting, on-type formatting, semantic-token delta requests, and editor-extension work are out of scope. A server process can manage multiple projects under the workspace root, keyed by the config file each open document belongs to.
Responsibilities
- Resolve workspace/project configuration for open PSL documents and keep managed projects aligned with config-file changes.
- Serve parse diagnostics (LSP 3.17 pull with push fallback) plus whole-document formatting, folding ranges, full/range semantic tokens, and model field type completion for configured PSL inputs.
- Preserve parser artifacts per project so editor features share the same AST, source-file, and symbol-table lifecycle instead of reparsing through feature-specific paths.
- Fail safely for unsupported documents, missing or closed buffers, config-load failures, malformed inputs, and oversized semantic-token requests.
Dependencies
@prisma-next/config-loader — discovers nearest config files and loads project configuration.
@prisma-next/psl-parser — parses PSL, builds symbol tables, exposes syntax artifacts, and formats PSL text.
@prisma-next/framework-components — supplies control-stack input types used when resolving project configuration.
@prisma-next/errors and @prisma-next/utils — shared framework utilities used by parsing/config plumbing.
vscode-languageserver and vscode-languageserver-textdocument — LSP connection, request/notification types, semantic-token/folding/formatting types, and incremental document management.
How it works
initialize — resolves the workspace root from the client's rootUri and registers config-file watching when the client supports it. The server advertises incremental text sync, whole-document formatting, folding ranges, and semanticTokensProvider with a stable standard-only legend, full: true, and range: true. When the client advertises textDocument.diagnostic, the server also advertises diagnosticProvider with { interFileDependencies: false, workspaceDiagnostics: false } — flags that describe the current single-input implementation scope, not PSL itself. Configs are loaded when matching documents open, on the first read that needs them, or when watched config files change. If a config cannot be loaded, the server does not manage that project.
- Document sync — text-document sync is incremental (
TextDocumentSyncKind.Incremental); the TextDocuments manager applies incremental edits, and each open or change is forwarded to the project's artifact store as a document-changed event. The server parses the full current buffer at most once per change, when a read (diagnostic pull, completion, semantic tokens, folding, or a push publish) next needs the document's artifacts.
- Diagnostics — served over exactly one transport per client, decided at
initialize. For clients that advertise textDocument.diagnostic (pull), didOpen / didChange only invalidate — the artifact store is only told the document changed and no eager work runs; a textDocument/diagnostic request reads the document artifacts from the store, which parses the current buffer internally when needed (running @prisma-next/psl-parser's parse() and buildSymbolTable() once per change) and returns a full report with the merged, mapped diagnostics. Documents that are not configured inputs, or whose project cannot load, return an empty full report. On a config-file change the server reloads the project and asks the client to re-pull via workspace/diagnostic/refresh when the client advertises workspace.diagnostics.refreshSupport, instead of republishing. For clients without pull support, the previous push behavior is preserved: the server computes on didOpen / didChange and publishes via textDocument/publishDiagnostics (a clean document publishes an empty array, clearing markers; unconfigured documents publish nothing), and config changes republish affected open documents. The report builder is project-scoped so a future multi-input symbol table can attach relatedDocuments; today reports carry only the requested document's items.
- Formatting — on
textDocument/formatting, the server formats the current in-memory document text with @prisma-next/psl-parser/format when the document is a configured PSL input. It returns one whole-document edit when the formatted text differs, and returns no edits for missing or closed documents, unconfigured documents, already canonical text, malformed PSL, or invalid formatter options.
- Folding ranges — on
textDocument/foldingRange, the server reads the current document artifacts from the project store for the configured input and returns foldable declaration/block ranges. Missing or unconfigured documents return an empty result.
- Semantic tokens — on
textDocument/semanticTokens/full and textDocument/semanticTokens/range, the server reads the current preserved DocumentAst, SourceFile, project SymbolTable, and control-stack scalar types from the same ProjectArtifacts lifecycle used by diagnostics. It classifies PSL keywords, declaration names, field/property names, type references, attributes, strings, numbers, booleans, and comments into standard token types/modifiers, then encodes them as LSP five-integer relative semantic-token data. The range request filters to intersecting tokens before encoding. Unconfigured, missing, closed, config-resolution-failed, or oversized documents return { data: [] } instead of throwing; the store parses the current buffer internally before returning artifacts, so tokens never derive from an out-of-date parse. Malformed PSL returns best-effort tokens from parser recovery when artifacts are available.
- Completion — on
textDocument/completion, the server serves configured PSL model field type positions, descriptor-backed generic block parameter-key positions, and declaration keyword positions from cached parse artifacts. It classifies the cursor using the cached AST/source file, reads the current project symbol table plus project control-stack block descriptors, threads the client's snippet capability into declaration keyword item construction, and returns [] for missing or closed documents, unconfigured documents, unavailable artifacts, unsupported contexts, ordinary attributes or attribute arguments, generic block parameter values, generic block value positions, relation-aware scenarios, and external contract-space discovery gaps.
- Project artifact store — each project load constructs one artifact store with the config's inputs, control stack, and a text provider over the open-document mirror. The store returns the AST,
SourceFile, and diagnostics per open configured input plus one project symbol table, parsing internally when a read needs them; document-changed/closed events (edits, closes) and store replacement on config reload are the only things that change what reads return. The artifacts are exposed through getDocumentAst / getProjectSymbolTable for future features. Filling the project table from several inputs — and reading unopened inputs from disk — is deferred cross-file work.
Module layout
diagnostic-mapping.ts — pure ParseDiagnostic[] → LspDiagnostic[] mapping. Free of any vscode-languageserver import; it returns plain shape objects (ranges pass through unchanged) so it stays reusable. The connection layer adapts the numeric severity to the LSP enum.
schema-inputs.ts — resolves the schema-input set (SchemaInputSet) from a config and answers URI membership.
config-resolution.ts — wraps loadConfig and resolves schema inputs, formatter options, and control-stack inputs for a config. A standalone async function so it can be re-run on a config change without rewiring the server.
document-diagnostics.ts — computeDocumentDiagnostics(uri, text, inputs, controlStack), the pure seam that parses, builds the symbol table, and returns the diagnostics plus the parse artifacts.
project-artifacts.ts — createProjectArtifacts({ inputs, controlStack, getText }), the per-project-load store that owns document artifacts and the project symbol table, parsing on demand and responding to document-changed/closed events.
folding-ranges.ts — pure AST-to-LSP folding-range computation for declaration/block bodies.
semantic-tokens.ts — pure PSL semantic-token collection, range filtering, multiline normalization, duplicate resolution, modifier bitset encoding, and LSP semantic-token data encoding.
completion-context.ts — pure cursor classifier for PSL completion contexts, currently routing model field type positions, descriptor-backed generic block parameter-key positions, and declaration keyword positions while marking everything outside slice scope unsupported.
completion-provider.ts — pure completion item provider for supported model field type, generic block parameter, and declaration keyword contexts.
server.ts — createServer(connection) wires diagnostics, whole-document formatting, folding ranges, semantic-token handlers, completion, config watching, and project-artifact access onto an injected connection.
start-server.ts — startServer() creates a stdio connection and starts the server. This is what the CLI delegates to.
Package Location
- Domain: framework (target-agnostic)
- Layer: tooling
- Plane: migration
- Path:
packages/1-framework/3-tooling/language-server