# @prisma-next/language-server

> **Internal package.** This package is an implementation detail of [`prisma-next`](https://www.npmjs.com/package/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

1. **`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.
2. **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.
3. **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.
4. **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.
5. **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.
6. **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.
7. **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.
8. **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`
