TML-2946: Add PSL type and generic block completions#871
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds PSL completion support in the language server, updates parser syntax helpers used by completion, adjusts semantic-token decorator ranges, and changes the playground to serve and load runtime config over HTTP. ChangesPSL completion stack
LSP playground runtime config
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@prisma-next/extension-author-tools
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/middleware-cache
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/extension-postgis
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/extension-supabase
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/cli-telemetry
@prisma-next/config-loader
@prisma-next/emitter
@prisma-next/language-server
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
size-limit report 📦
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/1-framework/3-tooling/language-server/README.md`:
- Around line 37-39: The server module description is incomplete because it only
mentions model field type completions and omits the generic-block parameter-key
completions that `createServer(connection)` also wires up. Update the
`server.ts` bullet in the README so it reflects both supported completion
contexts, matching the behavior described in `completion-context.ts` and
`completion-provider.ts`.
In `@packages/1-framework/3-tooling/language-server/src/server.ts`:
- Around line 253-295: Refresh the completion path in completeDocument so it
does not rely on stale cached parse artifacts from
project.artifacts.getDocument(uri) when the buffer has just changed. Before
calling classifyPslCompletionContext(), rebuild or refresh the document
artifacts from the current document.getText() and use those updated artifacts
for the completion context and providePslCompletionItems call, while keeping the
existing early returns and try/catch behavior intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: b388a8f7-7b3e-4afb-a96d-db96126c5a9d
⛔ Files ignored due to path filters (9)
projects/lsp-autocomplete/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/dispatches/01-cursor-classifier-substrate.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/dispatches/02-model-type-provider.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/dispatches/03-lsp-completion-route.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/dispatches/04-scope-docs-and-final-guardrails.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/model-type-completions/spec.mdis excluded by!projects/**projects/lsp-autocomplete/spec.mdis excluded by!projects/**projects/lsp-autocomplete/trace.jsonlis excluded by!projects/**
📒 Files selected for processing (7)
packages/1-framework/3-tooling/language-server/README.mdpackages/1-framework/3-tooling/language-server/src/completion-context.tspackages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.tspackages/1-framework/3-tooling/language-server/test/completion-provider.test.tspackages/1-framework/3-tooling/language-server/test/server.test.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/1-framework/3-tooling/language-server/src/completion-provider.ts (1)
178-186: 🎯 Functional Correctness | 🔵 TrivialConditionally generate name placeholders based on descriptor
name.requiredmetadata.While all current descriptors in the codebase define
name: { required: true }, theAuthoringPslBlockDescriptortype permitsrequired: false. The current implementation unconditionally inserts a name snippet placeholder, which would generate invalid boilerplate for any future optional-name blocks.Update
genericBlockDeclarationKeywordCandidatesto inspect the descriptor'sname.requiredproperty and omit the name placeholder ininsertTextandsnippetTextwhen optional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/3-tooling/language-server/src/completion-provider.ts` around lines 178 - 186, Update genericBlockDeclarationKeywordCandidates to respect each descriptor’s name.required metadata instead of always inserting a name placeholder. Inspect the descriptor returned by descriptorBlockKeywords/its source descriptor data and, when name.required is false, omit the nameSnippetPlaceholder from both insertText and snippetText so optional-name blocks generate valid boilerplate. Keep the current placeholder behavior only for descriptors whose name is required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/1-framework/3-tooling/language-server/src/completion-provider.ts`:
- Around line 178-186: Update genericBlockDeclarationKeywordCandidates to
respect each descriptor’s name.required metadata instead of always inserting a
name placeholder. Inspect the descriptor returned by descriptorBlockKeywords/its
source descriptor data and, when name.required is false, omit the
nameSnippetPlaceholder from both insertText and snippetText so optional-name
blocks generate valid boilerplate. Keep the current placeholder behavior only
for descriptors whose name is required.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: b182fb52-90c5-497a-a328-6f1cc92b9db2
⛔ Files ignored due to path filters (6)
projects/lsp-autocomplete/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/top-level-keyword-completions/dispatches/01-declaration-keyword-completions.mdis excluded by!projects/**projects/lsp-autocomplete/slices/top-level-keyword-completions/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/top-level-keyword-completions/spec.mdis excluded by!projects/**projects/lsp-autocomplete/spec.mdis excluded by!projects/**projects/lsp-autocomplete/trace.jsonlis excluded by!projects/**
📒 Files selected for processing (7)
packages/1-framework/3-tooling/language-server/README.mdpackages/1-framework/3-tooling/language-server/src/completion-context.tspackages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.tspackages/1-framework/3-tooling/language-server/test/completion-provider.test.tspackages/1-framework/3-tooling/language-server/test/server.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/1-framework/3-tooling/language-server/src/server.ts
- packages/1-framework/3-tooling/language-server/test/completion-context.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/lsp-playground/src/client/main.ts (1)
19-27: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the runtime-config contract into a shared module.
RUNTIME_CONFIG_PATHandRuntimeConfigare re-declared here and inapps/lsp-playground/src/cli.ts, so a future rename can still typecheck and fail only once the browser boots. Pull the path constant and TypeScript type into one shared file, and keep only the runtime validator local.Also applies to: 33-55
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/lsp-playground/src/client/main.ts` around lines 19 - 27, The runtime-config contract is duplicated between the client and CLI, so extract `RUNTIME_CONFIG_PATH` and `RuntimeConfig` into a shared module and import them from both `main.ts` and `cli.ts`. Keep the runtime-specific validation logic local in each entrypoint, but remove the redeclared path constant and interface so there is a single source of truth for the shared shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/lsp-playground/src/cli.ts`:
- Around line 197-199: The request path parsing in the playground handler should
not depend on request.headers.host, since using the incoming Host header as the
base for new URL can throw before a response is sent. Update the logic around
the request.url parsing in the cli.ts handler to use a fixed local base URL, or
wrap the URL construction in error handling and return a 400 when parsing fails.
Keep the change focused on the requestPath/baseUrl logic so the playground
process cannot be taken down by a malformed Host header.
---
Nitpick comments:
In `@apps/lsp-playground/src/client/main.ts`:
- Around line 19-27: The runtime-config contract is duplicated between the
client and CLI, so extract `RUNTIME_CONFIG_PATH` and `RuntimeConfig` into a
shared module and import them from both `main.ts` and `cli.ts`. Keep the
runtime-specific validation logic local in each entrypoint, but remove the
redeclared path constant and interface so there is a single source of truth for
the shared shape.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 853eb3b7-31d2-4bc1-83b5-8c84988b9357
📒 Files selected for processing (4)
apps/lsp-playground/README.mdapps/lsp-playground/src/cli.tsapps/lsp-playground/src/client/main.tsapps/lsp-playground/src/client/runtime.ts
💤 Files with no reviewable changes (1)
- apps/lsp-playground/src/client/runtime.ts
| readonly position: Position; | ||
| } | ||
|
|
||
| export type UnsupportedPslCompletionReason = |
There was a problem hiding this comment.
Why do we need this?
| let previousSignificant: SyntaxToken | undefined; | ||
| let touching: SyntaxToken | undefined; | ||
|
|
||
| for (const token of root.tokens()) { |
There was a problem hiding this comment.
We can use binary search here if SyntaxNode allow to access chidren by index, can't we?
| }; | ||
| } | ||
|
|
||
| function isTrivia(token: SyntaxToken): boolean { |
There was a problem hiding this comment.
Don't we already have a helper for this in parser module?
| return tokenContext({ current, previous, previousSignificant, touching }); | ||
| } | ||
|
|
||
| function tokenContext(input: { |
There was a problem hiding this comment.
WTF is this function doing? Just construct goddamn object from a list of properties, it's fine if some property value is explicitly undefined
|
|
||
| interface TokenContext { | ||
| readonly current?: SyntaxToken; | ||
| readonly previous?: SyntaxToken; |
There was a problem hiding this comment.
This property is not used anywhere
| if (offset >= token.offset && offset < tokenEnd) { | ||
| current = token; | ||
| } | ||
| if (offset > token.offset && offset <= tokenEnd) { |
There was a problem hiding this comment.
This condition does not make sense to me. It looks like you only want to know if offset is directly at end of current token?
| } | ||
|
|
||
| const node = findDeepestNodeAtOffset(input.document.syntax, offset); | ||
| const previousNode = |
There was a problem hiding this comment.
SyntaxNode has methods for navigating siblings specifically for that, use those rather than reinvent the wheel
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/1-framework/2-authoring/psl-parser/src/syntax/navigation.ts`:
- Around line 57-63: previousNonTriviaToken currently returns undefined for
zero-width SyntaxNode inputs because it relies on firstToken, so it never walks
to the preceding significant token. Update previousNonTriviaToken to detect
empty nodes and resolve a suitable starting token from the node’s
position/surrounding siblings or ancestor chain before iterating prevToken,
while still skipping trivia. Keep the behavior for SyntaxToken inputs unchanged
and preserve the helper’s contract for incomplete trees.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 08fa15a3-ccc2-4f84-b769-6a338e751a22
⛔ Files ignored due to path filters (7)
projects/lsp-autocomplete/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/completion-context-simplification/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/completion-context-simplification/spec.mdis excluded by!projects/**projects/lsp-autocomplete/slices/parser-red-tree-navigation/plan.mdis excluded by!projects/**projects/lsp-autocomplete/slices/parser-red-tree-navigation/spec.mdis excluded by!projects/**projects/lsp-autocomplete/slices/top-level-keyword-completions/dispatches/02-review-feedback-and-classifier-navigation.mdis excluded by!projects/**projects/lsp-autocomplete/slices/top-level-keyword-completions/plan.mdis excluded by!projects/**
📒 Files selected for processing (11)
apps/lsp-playground/src/cli.tspackages/1-framework/2-authoring/psl-parser/src/exports/syntax.tspackages/1-framework/2-authoring/psl-parser/src/syntax/navigation.tspackages/1-framework/2-authoring/psl-parser/src/syntax/red.tspackages/1-framework/2-authoring/psl-parser/test/syntax/navigation.test.tspackages/1-framework/2-authoring/psl-parser/test/syntax/red.test.tspackages/1-framework/3-tooling/language-server/src/completion-context.tspackages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.tspackages/1-framework/3-tooling/language-server/test/server.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- packages/1-framework/3-tooling/language-server/test/completion-context.test.ts
- packages/1-framework/3-tooling/language-server/src/server.ts
- apps/lsp-playground/src/cli.ts
- packages/1-framework/3-tooling/language-server/test/server.test.ts
- packages/1-framework/3-tooling/language-server/src/completion-provider.ts
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Make red tokens navigable (parent, prev/next token) and add SyntaxNode.tokenAtOffset (none/single/between with left/right bias), coveringElement, first/lastToken, plus trivia-skip helpers (skipTriviaToken, nonTriviaSibling, previousNonTriviaToken). Mirrors rust-analyzer syntax idioms; no fake-identifier marker. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…navigation Anchor classification on tokenAtOffset(offset).leftBiased() and navigate from token.parent, with one source_range()-style replacement helper. Removes findCursorContext, findTokenContext, findDeepestNodeAtOffset, tokensBetween, lineStartOffsetFromTokens, containsOnlyWhitespaceTokens, the duplicated prefix builders, and the unused UnsupportedPslCompletionReason. Behavior unchanged; matches rust-analyzer classifier shape. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…layground URL parsing Address PR review: rebuild completion artifacts from the current buffer before classifying so completions after an edit are not stale; parse playground request URLs against a fixed local base and return 400 on malformed/absent URLs instead of trusting the incoming Host header. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…d review dispatch Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…code comments Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… AST Replace the splitQualifiedPrefix source scan (which re-tokenized the qualified name by counting : and . in raw text) with QualifiedNameAst navigation: contract-space/namespace/name roles are resolved from colon()/dot() offsets and segment IdentifierAst offsets relative to the cursor. The only source touch left is slicing the cursor segments own identifier-token text. Deletes splitQualifiedPrefix, pathFromSegments, and segmentAt; behavior preserved, two new tests pin the colon-without-dot and cursor-mid-name cases. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Order the Position/Range/SemanticTokens type imports merged during the rebase against main (semantic tokens + completion landed in parallel). Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
With client-side filtering, the classifier no longer needs to carry the typed prefix. Removes the prefix string from the declaration-keyword and generic-block contexts and shrinks the model-field-type prefix to just the namespace it selects (deleting TypeNamePrefix and the cursor-truncation helper). Completability and namespace resolution are unchanged. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… position Replaces the single modelFieldType context with position-specific modelType / spaceMember / namespaceMember, and splits genericBlockParameter into genericBlockKey / genericBlockValue. Each position now maps to a focused provider. spaceMember and genericBlockValue are seams for behaviour not yet backed by data (foreign contract-space members need the multi-input symbol table; parameter-value completion is future work) and return no items. The only changed outcome: a :-qualified type with no . now classifies as spaceMember rather than offering bare model-type completions. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Mirrors SyntaxNode.endOffset (offset + textLength) so consumers stop hand-rolling offset + text.length. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
- simplify classifyTypePosition to read the separator-positional accessors directly (malformed leading-separator types now resolve to modelType) - inline the position factories and sourceRangeStart; use token.endOffset - relocate the attribute guard from the top-level classifier into the generic-block classifier (the only place it is load-bearing: an @@ attribute inside a generic block would otherwise misclassify as genericBlockKey) - offer type completion for composite-type fields, not just model fields - treat the next line after a field name as a valid blank-type position, since the parser skips newlines as trivia and accepts type-on-newline Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
nodeForContext only redirected when the anchor was a Document or ModelDeclaration, and in both cases the classification is identical with or without the redirect (a bare model body has nothing to complete; top-level resolves to a document-scope keyword either way). Field positions already anchor on the FieldDeclaration since the parser attaches a field trailing trivia to the field. Drop the dead nodeForContext and the anchor/context distinction. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Model/composite/namespace/types/generic block AST nodes share lbrace()/rbrace(); a BracedBlock interface lets consumers write one brace-body helper instead of duplicating it per node kind. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
- closestAst takes variadic casts and walks the ancestor path once (model-or-
composite check no longer traverses twice)
- classifyTypePosition builds and returns the context directly, dropping the
intermediate TypePosition type and the re-mapping switch
- declaration keywords may be completed after a closing brace regardless of
newlines (model A {} model B {} is valid one-line PSL); keyed on RBrace, not
newlineBetween
- merge declarationBodyContainsOffset/namespaceBodyContainsOffset into a single
blockBodyContainsOffset over BracedBlock, used by the namespace-scope, non-
declaration-body, and generic-block checks alike
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… combinator Reverts closestAst to its single-cast form and adds a small any() cast-combinator, so the model-or-composite lookup reads closestAst(node, any(a, b)) instead of overloading closestAst with variadic casts. Single ancestor walk, identical behaviour. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/1-framework/3-tooling/language-server/src/completion-context.ts`:
- Around line 45-50: The namespace-member completion payload is dropping the
contract-space qualifier, so `classifyTypePosition()` and
`NamespaceMemberCompletionContext` currently treat foreign-space and local
namespace lookups the same. Update the context shape to retain the `space`
information (or split this into a distinct completion kind) and thread that
through `provideNamespaceMemberCompletionItems()` so it can detect external
contract-space prefixes and short-circuit instead of resolving them against
`symbolTable.topLevel.namespaces[namespace]`.
In `@packages/1-framework/3-tooling/language-server/src/completion-provider.ts`:
- Around line 265-274: The namespace completion path in
provideNamespaceMemberCompletionItems is incorrectly resolving contract-space
namespaces from source.symbolTable, so foreign references like supabase:auth.|
get treated as local auth members. Update this branch to check the namespace
context for a preserved space/contract-space marker and, when it is foreign,
return an empty completion list instead of calling namespaceCandidates on the
local symbol table; only local namespaces should continue using
modelTypeCompletionItems.
- Around line 381-389: The namespace qualifier completion is inserting only the
bare namespace name, so accepting it does not produce a qualifier flow. Update
namespaceQualifierCandidate in completion-provider.ts to insert the dotted
namespace form (for example, the namespace name plus a trailing dot) and keep
the displayed label consistent with that qualifier behavior so users can
continue completing the qualified member after insertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 35f1ffc5-9d1f-4960-b41c-b193f8cbaa75
📒 Files selected for processing (20)
apps/lsp-playground/README.mdapps/lsp-playground/src/cli.tsapps/lsp-playground/src/client/main.tsapps/lsp-playground/src/client/runtime.tspackages/1-framework/2-authoring/psl-parser/src/exports/syntax.tspackages/1-framework/2-authoring/psl-parser/src/syntax/ast-helpers.tspackages/1-framework/2-authoring/psl-parser/src/syntax/ast/declarations.tspackages/1-framework/2-authoring/psl-parser/src/syntax/ast/qualified-name.tspackages/1-framework/2-authoring/psl-parser/src/syntax/navigation.tspackages/1-framework/2-authoring/psl-parser/src/syntax/red.tspackages/1-framework/2-authoring/psl-parser/test/syntax/ast.test.tspackages/1-framework/2-authoring/psl-parser/test/syntax/navigation.test.tspackages/1-framework/2-authoring/psl-parser/test/syntax/red.test.tspackages/1-framework/3-tooling/language-server/README.mdpackages/1-framework/3-tooling/language-server/src/completion-context.tspackages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.tspackages/1-framework/3-tooling/language-server/test/completion-provider.test.tspackages/1-framework/3-tooling/language-server/test/server.test.ts
💤 Files with no reviewable changes (1)
- apps/lsp-playground/src/client/runtime.ts
✅ Files skipped from review due to trivial changes (2)
- apps/lsp-playground/README.md
- packages/1-framework/3-tooling/language-server/README.md
🚧 Files skipped from review as they are similar to previous changes (10)
- packages/1-framework/2-authoring/psl-parser/src/exports/syntax.ts
- packages/1-framework/2-authoring/psl-parser/test/syntax/red.test.ts
- packages/1-framework/2-authoring/psl-parser/test/syntax/navigation.test.ts
- packages/1-framework/2-authoring/psl-parser/src/syntax/navigation.ts
- apps/lsp-playground/src/cli.ts
- packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts
- packages/1-framework/3-tooling/language-server/src/server.ts
- apps/lsp-playground/src/client/main.ts
- packages/1-framework/3-tooling/language-server/test/server.test.ts
- packages/1-framework/2-authoring/psl-parser/src/syntax/red.ts
| function provideNamespaceMemberCompletionItems( | ||
| context: NamespaceMemberCompletionContext, | ||
| sourceFile: SourceFile, | ||
| source: PslCompletionCandidateSource, | ||
| ): readonly CompletionItem[] { | ||
| return modelTypeCompletionItems( | ||
| context, | ||
| sourceFile, | ||
| namespaceCandidates(source.symbolTable?.topLevel.namespaces[context.namespace]), | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Don't resolve foreign contract-space namespaces from the local symbol table.
Because NamespaceMemberCompletionContext has no space, this branch treats supabase:auth.| exactly like auth.| and returns members from the local auth namespace. That contradicts the PR objective that external contract-space discovery remains unsupported. Once the context preserves space, this path should return [] for foreign spaces until external symbols exist.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/1-framework/3-tooling/language-server/src/completion-provider.ts`
around lines 265 - 274, The namespace completion path in
provideNamespaceMemberCompletionItems is incorrectly resolving contract-space
namespaces from source.symbolTable, so foreign references like supabase:auth.|
get treated as local auth members. Update this branch to check the namespace
context for a preserved space/contract-space marker and, when it is foreign,
return an empty completion list instead of calling namespaceCandidates on the
local symbol table; only local namespaces should continue using
modelTypeCompletionItems.
| function namespaceQualifierCandidate(namespace: NamespaceSymbol): ModelTypeCompletionCandidate { | ||
| return { | ||
| category: 'namespace', | ||
| label: namespace.name, | ||
| insertText: namespace.name, | ||
| filterText: namespace.name, | ||
| detail: 'Namespace', | ||
| kind: CompletionItemKind.Module, | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Insert namespace qualifiers with the trailing dot.
These candidates currently insert auth, not auth.. Accepting the completion at a bare type position therefore produces a plain identifier instead of the advertised namespace qualifier flow. Make the qualifier candidate insert the dotted form (and ideally label it the same way).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/1-framework/3-tooling/language-server/src/completion-provider.ts`
around lines 381 - 389, The namespace qualifier completion is inserting only the
bare namespace name, so accepting it does not produce a qualifier flow. Update
namespaceQualifierCandidate in completion-provider.ts to insert the dotted
namespace form (for example, the namespace name plus a trailing dot) and keep
the displayed label consistent with that qualifier behavior so users can
continue completing the qualified member after insertion.
hasUnsupportedAncestor (3 walks) and isInsideNonDeclarationKeywordBody (4 walks) each collapse to a single closestAst over an any() of their casts. The block kinds never nest within one another, so the nearest ancestor of any kind equals the OR of each kind. Explicit type args (BracedBlock / AstNode) pin inference to the shared interfaces. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Replace the single-T any() signature with an overload pair: a precise generic public signature that distributes CastTarget over the cast tuple, plus a loose impl signature whose body type-checks without a cast. Call sites drop their explicit any<BracedBlock> / any<AstNode> type arguments and infer the union automatically. A single-signature const-generic form does not work: const only affects call-site tuple inference, not the body, where cast(node) stays opaque and is unassignable to the generic return type. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Relocate two completion-context helpers into @prisma-next/psl-parser so they sit with the AST navigation surface they belong to: - closestAst becomes SyntaxNode.findClosestParent, a self-inclusive generic method (tests the node itself before walking ancestors), mirroring rust-analyzer ancestors().find_map. - the any cast-combinator moves to ast-helpers next to findFirstChild / filterChildren and is exported from the syntax barrel. completion-context drops both local definitions, imports any, and rewires its eight call sites to node?.findClosestParent(...). Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…efore them
The comment guard checked both leftBiased and rightBiased tokens. The
rightBiased clause fires when the cursor sits in a real type position
that merely precedes a trailing comment (model Post { author |// note }),
wrongly returning unsupported instead of a model-type completion.
Drop the rightBiased clause: a cursor inside a comment is detected by the
leftBiased token alone. Add a regression test pinning that completion is
still offered in a type position before a trailing comment.
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
The offset field on UnsupportedPslCompletionContext was never read: the provider short-circuits the unsupported case to an empty list, and no test asserts it. Remove the field, delete the unsupported(offset) helper that threaded it, and return a single UNSUPPORTED sentinel from every non-completable position. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Red nodes now carry their index within the parent, set once at construction. Sibling navigation becomes childAt(parent, index +/- 1), matching the rust-analyzer red-tree idiom, and the bespoke siblingAfter / siblingBefore green-layer walkers are deleted. Navigation no longer touches the green layer or guesses node identity from (green, offset); the index also disambiguates a zero-width child from an offset-colliding neighbour, which an offset-only scheme cannot. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/1-framework/2-authoring/psl-parser/src/syntax/red.ts (1)
49-57: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid routing sibling traversal through
childAt.
nextSibling*,prevSibling*, and climbing traversal still callchildAt, which rescans from child0to recompute offsets. Iterating siblings is therefore O(n²) on wide nodes despite the newindexfield. Use the current element’soffsetplus adjacent green child lengths for true O(1) sibling wrapping.♻️ Proposed localized refactor
get nextSiblingOrToken(): SyntaxElement | undefined { - return childAt(this.parent, this.index + 1); + return siblingAfter(this); } /** The sibling element immediately before this token within its parent. */ get prevSiblingOrToken(): SyntaxElement | undefined { - return childAt(this.parent, this.index - 1); + return siblingBefore(this); }get nextSibling(): SyntaxElement | undefined { - return this.parent === undefined ? undefined : childAt(this.parent, this.index + 1); + return siblingAfter(this); } get prevSibling(): SyntaxElement | undefined { - return this.parent === undefined ? undefined : childAt(this.parent, this.index - 1); + return siblingBefore(this); }function climbingNext(el: SyntaxElement): SyntaxElement | undefined { let current: SyntaxElement = el; for (;;) { const parent = current.parent; if (parent === undefined) return undefined; - const sibling = childAt(parent, current.index + 1); + const sibling = siblingAfter(current); if (sibling !== undefined) return sibling; current = parent; } } function climbingPrev(el: SyntaxElement): SyntaxElement | undefined { let current: SyntaxElement = el; for (;;) { const parent = current.parent; if (parent === undefined) return undefined; - const sibling = childAt(parent, current.index - 1); + const sibling = siblingBefore(current); if (sibling !== undefined) return sibling; current = parent; } } + +function siblingAfter(el: SyntaxElement): SyntaxElement | undefined { + const parent = el.parent; + if (parent === undefined) return undefined; + const index = el.index + 1; + const target = parent.green.children[index]; + if (target === undefined) return undefined; + return wrapElement(target, el.offset + elementTextLength(el.green), parent, index); +} + +function siblingBefore(el: SyntaxElement): SyntaxElement | undefined { + const parent = el.parent; + if (parent === undefined) return undefined; + const index = el.index - 1; + const target = parent.green.children[index]; + if (target === undefined) return undefined; + return wrapElement(target, el.offset - elementTextLength(target), parent, index); +}Also applies to: 188-193, 388-405, 422-433
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/1-framework/2-authoring/psl-parser/src/syntax/red.ts` around lines 49 - 57, The sibling/climbing traversal still routes through childAt, which defeats the new index-based optimization and keeps sibling iteration O(n²) on wide nodes. Update the traversal logic in red.ts for nextSiblingOrToken, prevSiblingOrToken, and the related climbing paths mentioned in the diff to compute adjacent elements directly from the current element’s offset and neighboring green child lengths instead of rescanning from child 0. Keep the fix localized around the existing SyntaxElement accessors and any helper used by the climbing traversal so sibling wrapping becomes O(1).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/1-framework/2-authoring/psl-parser/src/syntax/red.ts`:
- Around line 49-57: The sibling/climbing traversal still routes through
childAt, which defeats the new index-based optimization and keeps sibling
iteration O(n²) on wide nodes. Update the traversal logic in red.ts for
nextSiblingOrToken, prevSiblingOrToken, and the related climbing paths mentioned
in the diff to compute adjacent elements directly from the current element’s
offset and neighboring green child lengths instead of rescanning from child 0.
Keep the fix localized around the existing SyntaxElement accessors and any
helper used by the climbing traversal so sibling wrapping becomes O(1).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: bcccb435-c86b-416f-8267-9bdf735e071a
📒 Files selected for processing (7)
packages/1-framework/2-authoring/psl-parser/src/exports/syntax.tspackages/1-framework/2-authoring/psl-parser/src/syntax/ast-helpers.tspackages/1-framework/2-authoring/psl-parser/src/syntax/red.tspackages/1-framework/2-authoring/psl-parser/test/syntax/ast.test.tspackages/1-framework/2-authoring/psl-parser/test/syntax/red.test.tspackages/1-framework/3-tooling/language-server/src/completion-context.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.ts
✅ Files skipped from review due to trivial changes (1)
- packages/1-framework/2-authoring/psl-parser/test/syntax/ast.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/1-framework/2-authoring/psl-parser/src/exports/syntax.ts
- packages/1-framework/3-tooling/language-server/test/completion-context.test.ts
…t source text The decorator highlighter walked the raw source string backwards over @ characters to extend the first segment range. The @ / @@ prefix is a real token (At / DoubleAt), so read its offset from the attribute node via findChildToken instead. Drops the character scan and the sourceText threading through collectDecoratorName / rangeForDecoratorIdentifier. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…oken previousNonTriviaToken hand-rolled the trivia-skipping loop that skipTriviaToken already encapsulates. Step strictly backwards once, then delegate to skipTriviaToken, matching the rust-analyzer composition (prev_token then skip_trivia_token) instead of duplicating it. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…cturally
Replace the token-scan declaration-keyword logic with a structural rule:
a keyword is offered unless the cursor sits inside an established
declaration. The nearest declaration ancestor permits a keyword only
when it is still nascent (its sole significant child is the keyword),
when the cursor is past its closing brace, or when it is a namespace
body offering a fresh nested slot.
This fixes the cursor wedged right after a complete declaration
(model A {}|, previously misread as inside the model) while keeping a
mid-header position (model A|) correctly rejected. Deletes the
isInsideNonDeclarationKeywordBody and declarationKeywordAllowed helpers;
previousSignificantToken stays for the generic-block value position.
Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…sors canBeginDeclaration counted non-trivia children to decide whether a declaration was still nascent. Read it off the AST instead: a nascent declaration has no lbrace and no name. TypesBlockAst is anonymous (no name accessor), so discriminate it with instanceof, which also narrows the union so name() resolves on the rest. The namespace case is now an explicit branch (a namespace permits a keyword only in its body) rather than an opaque combined flag. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
A field with no type emitted a zero-width TypeAnnotation node — a deviation from the rust-analyzer rule that missing constructs are absent, not materialized empty. parseTypeAnnotation now emits a node only when type content is present. Field-type completion relied on that placeholder to anchor the empty type slot. Without it, a typeless field has no TypeAnnotation and its trailing trivia attaches to the enclosing block, so the slot is found positionally: fieldForTypeSlot resolves the field via the nearest significant token to the left, and emptyTypeSlotEnd bounds the slot at the first attribute or next sibling. The textLength === 0 branch is deleted (no zero-width node can exist for it to match). Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…dth navigation handling parseAttributeArg, like parseTypeAnnotation before it, emitted a zero-width AttributeArg node when no named-arg or value-expression was present (e.g. @default(,)). Guard on the expression FIRST-set so a node is emitted only when the arg has content. With both placeholder emits gone, no non-root node can be zero-width, so the red-tree navigation no longer needs to special-case it: isInside / containsOffset / containsRange collapse to the plain inclusive span, and the zero-width child skip in tokenAtOffset is removed. A parametrized test pins the precondition across malformed inputs; the obsolete synthetic zero-width-seam test is removed. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…ken primitive The completion classifier recovered "cursor in a gap with no node to anchor on" three different ways: a raw leftBiased().parent anchor, a trivia-branching fallback inside fieldForTypeSlot, and a bespoke previousSignificantToken for the generic-block value check. Collapse all three onto one cursor-relative look-left primitive, contextToken, which skips the in-progress edit identifier the way tsserver adjusts previousToken to contextToken. The generic-block structural checks (which block, which pair, in-field) stay anchored on the cursor own node, since whether the cursor sits in a key/value/ attribute slot is a node-structural question, not a look-left one; only the value-gap check moves to contextToken. canBeginDeclaration generalizes its past-rbrace clause to namespaces so a cursor past a closed namespace closing brace can still begin a document-level declaration, consistent with contextToken anchoring on the closing brace. previousSignificantToken is deleted; previousNonTriviaToken is removed from this file imports (its parser-side removal follows). Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…on helper previousNonTriviaToken existed solely to back the language server previousSignificantToken, which the contextToken unification deleted. Its only production consumer gone, the wrapper is dead: contextToken reaches the same token via skipTriviaToken directly. Remove the function, its re-export, and its test block. skipTriviaToken, nonTriviaSibling, and isTrivia keep their other live consumers and are untouched. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…once per request contextToken folded the look-left over cursorIdentifier, so the classifier recomputed the edit identifier three times: once for replacementStartOffset and once inside each contextToken call. Compute the edit and the context token once at the top of classifyPslCompletionContext and thread the results down - contextToken now takes the precomputed edit, and the generic-block value check reads the passed-in token instead of recomputing. Pure de-duplication, no behaviour change. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
…eclaration Rename the declaration-keyword gate and its declaration argument to name what they check: whether a declaration keyword can be completed given the preceding declaration the cursor sits against. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… token The empty-type-slot check was the last completion site recovering a gap with numeric offset bounds (emptyTypeSlotEnd + offset > fieldNameEnd && offset <= slotEnd) instead of the unified look-left mechanism. Since a typeless field has no node spanning its type slot, the slot is valid exactly when the context token belongs to the field own name - nothing significant sits between the name and the cursor; an attribute token (or anything else) means the cursor has left the slot. Thread the context token into classifyModelFieldType and test it with fieldName.syntax.isInside(context.offset); identity comparison is unreliable because the red layer recreates wrapper objects on traversal. emptyTypeSlotEnd is deleted along with its lone nonTriviaSibling consumer in this file. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
"context" said nothing about which token the helper returns. It is the significant token preceding the cursor (skipping the in-progress edit identifier and trivia) - the token the classifier anchors on. Rename the helper to precedingToken and the derived locals/fields to match (preceding, precedingNode, the precedingToken fields threaded into the generic-block and model-field classifiers), harmonising with the precedingDeclaration naming. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
… param names in the provider existingParameterNames was candidate-filtering data precomputed on the completion context, but the context should describe the cursor position, not gather provider data. Replace GenericBlockKeyCompletionContext.existingParameterNames with the enclosing block AST node and compute the existing-parameter set in the provider from block.entries(), excluding the in-progress pair the cursor sits inside. Behaviour is preserved: the old sameSpan-against-active-pair exclusion and the new isOutside(cursorOffset) check select the same entry. Signed-off-by: Serhii Tatarintsev <tatarintsev@prisma.io>
Linked issue
Refs TML-2946
At a glance
At the
author |model field type position,textDocument/completionreturns configured scalar types plus visible model/composite/type-alias candidates such asBoolean,DateTime,Int,String,Post,User, andAddress, plus namespace qualifiers such asauth.. After the user types or accepts a namespace qualifier, positions likeauth.|complete model/composite members inside that namespace. At descriptor-backed generic block parameter-key positions likewh|, it returns declared block parameters such aswhereand filters out sibling keys that already exist in the block.Decision
This PR ships the first PSL autocomplete surfaces in
@prisma-next/language-server: configured PSL model field type completions and descriptor-backed generic block parameter-key completions. It adds a typed cursor classifier over cached parser artifacts, completion providers backed by configured scalars, the current project symbol table, and the project control-stack PSL block descriptors, an LSP completion route, and README/guardrail coverage for the intentionally narrow scope.Summary
The language server now answers completion requests for open configured PSL documents at model field type positions and descriptor-backed generic block parameter-key positions. Unsupported contexts stay empty, including ordinary PSL
@/@@attributes, attribute arguments, generic block parameter values, unconfigured documents, relation-aware scenarios, and external contract-space discovery gaps.Reviewer notes
completion-context.tsowns the cursor-context boundary: token lookup, ancestor rejection, blank model type positions, partial qualified names, generic block body/key detection, and explicit unsupported reasons.ref,option,value, orlistdescriptors remain out of scope.projects/lsp-autocomplete/**is included for Drive traceability: it contains the project/slice specs, dispatch briefs, and trace for this project. Close-out removes/migrates project artifacts after the overall project is done.How it fits together
DocumentAst/SourceFiledata into either a model-field-type context, a generic-block-parameter context, or an explicit unsupported reason. This keeps completion on the existing recovered CST/AST path instead of reparsing with a marker.CompletionItems for configured scalars, top-level model/composite/scalar/type-alias symbols, bare namespace qualifier candidates such asauth., and namespace-local model/composite members only after a qualifier is present. For generic blocks, it resolves the descriptor by block keyword and returns declared parameter keys that match the typed prefix.completionProviderand routestextDocument/completionthrough the same configured-input and cached-artifact ownership checks used by diagnostics/formatting, passing both the project symbol table andpslBlockDescriptorsinto the provider.Behavior changes & evidence
Configured PSL inputs now complete model field type positions. The server advertises completion and routes requests only after it finds an open configured document, cached parse artifacts, and a project symbol table.
packages/1-framework/3-tooling/language-server/src/server.ts,packages/1-framework/3-tooling/language-server/src/completion-context.ts,packages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/test/server.test.ts,packages/1-framework/3-tooling/language-server/test/completion-context.test.ts,packages/1-framework/3-tooling/language-server/test/completion-provider.test.tsDescriptor-backed generic block parameter keys now complete. The classifier identifies blank and partial key positions inside
GenericBlockDeclarationAstbodies, the provider resolves the matchingAuthoringPslBlockDescriptor, and completions exclude already-present sibling keys.packages/1-framework/3-tooling/language-server/src/completion-context.ts,packages/1-framework/3-tooling/language-server/src/completion-provider.ts,packages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.ts,packages/1-framework/3-tooling/language-server/test/completion-provider.test.ts,packages/1-framework/3-tooling/language-server/test/server.test.tsModel type candidates are stable and scoped to visible type-like symbols. The provider orders configured scalars before top-level candidates and namespace qualifiers, filters prefixes, completes namespace qualifiers such as
auth.before namespace-local model names, and excludes generic block symbols from model-field-type completions.packages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/test/completion-provider.test.tsQualified type positions preserve the typed shape. Bare namespace prefixes like
a|replace the typed segment withauth., namespace-qualified positions likeauth.U|complete only the typed member segment, and contract-space-qualified positions likesupabase:auth.P|replace only the typed name segment and use currently visible candidates.packages/1-framework/3-tooling/language-server/src/completion-context.ts,packages/1-framework/3-tooling/language-server/src/completion-provider.tspackages/1-framework/3-tooling/language-server/test/completion-context.test.ts,packages/1-framework/3-tooling/language-server/test/completion-provider.test.tsUnsupported contexts return
[]instead of misleading suggestions. Unconfigured documents, ordinary field/model attributes, attribute arguments, generic block parameter values, comments, constructor arguments, and other non-type/non-key positions are rejected before provider output reaches the client.packages/1-framework/3-tooling/language-server/src/completion-context.ts,packages/1-framework/3-tooling/language-server/src/server.tspackages/1-framework/3-tooling/language-server/test/server.test.ts,packages/1-framework/3-tooling/language-server/test/completion-context.test.tsThe supported editor surface is documented. The README now says completion is supported for configured PSL model field type positions and descriptor-backed generic block parameter-key positions, and explicitly excludes ordinary attributes, attribute arguments, generic block parameter values, relation-aware suggestions, and external contract-space discovery.
packages/1-framework/3-tooling/language-server/README.mdCompatibility / migration / risk
No contract emission, runtime, migration, adapter, or public TypeScript API behavior changes are expected. The main risk is editor-surface false positives; this PR mitigates it by returning
[]outside configured model field type and descriptor-backed generic block parameter-key positions.Testing performed
pnpm --filter @prisma-next/framework-components build— passed; rebuilt missing localdist/needed by language-server tests in this worktreepnpm --filter @prisma-next/language-server test— 9 files / 103 tests passedpnpm --filter @prisma-next/language-server typecheck— passedpnpm --filter @prisma-next/language-server lint— passed, 26 files checkedSkill update
n/a — no reusable agent skill update is required. The language-server README documents the new editor-facing behavior and exclusions.
Follow-ups
ref,option,value,list) at the value cursor position.Alternatives considered
@/@@attributes are a different surface from descriptor-backed generic block parameters.Checklist
git commit -s) per the DCO. The DCO status check will block merge if any commit is missing aSigned-off-by:trailer.n/aif the change is doc-only / refactor with no behavioural delta).TML-NNNN: <sentence-case title>form (Linear ticket prefix + concise title naming the concrete deliverable). See.claude/skills/create-pr/SKILL.mdfor the full convention.n/a — internal only).Summary by CodeRabbit
.trigger and snippet insertion when supported.@markers.