Skip to content

v8.0.0-rc.17-dev.5

@StevenMcClankerton StevenMcClankerton tagged this 08 Oct 15:59
The PSL language server answers `textDocument/rename` and
`textDocument/prepareRename` for models, composite types, named types,
blocks (including enum blocks), fields and namespaces. A rename edits
the declaration and every reference across the schema files of the
project, from a cursor on the declaration name or on any reference. The
renamed positions are exactly the ones find references (#30621) returns.
When the rename would change a database name, the edit also adds
`@map("<old name>")` or `@@map("<old name>")` to the declaration, so the
database keeps its names.

## Changes

- **Rename provider**
(`packages/1-framework/3-tooling/language-server/src/rename.ts`):
`provideRename` calls `provideReferences` with `includeDeclaration` set
and returns one `WorkspaceEdit` with a `TextEdit` per returned location.
It has no symbol lookup of its own. `providePrepareRename` returns the
range and text of the identifier under the cursor when that identifier
has references to rename, and `null` otherwise.
- **Map attribute** (`rename.ts`): after the name edits, at most one
insertion is added, on the renamed declaration, in the declaration's
file:
  - a model gets `@@map` when it has neither `@@map` nor `@@base`;
- a field of a model gets `@map` when it has none and its type is not a
model or a type of another contract space (those are relation fields);
- a block gets `@@map` when its descriptor sets the new
`nameIsStorageName` flag and it has none.
  
Models and fields are covered only when the attribute specs of the
project define `map` at that level. A rename to the current name adds
nothing. The field attribute goes after the last attribute, before a
trailing comment. In a model or block the same text is always inserted
before the closing brace: a line break, the indent, the attribute, a
line break. The indent unit and the line break come from the project's
formatter options, and blank lines around the attribute are left to the
formatter.
- **Block descriptor flag** (`framework-components`,
`AuthoringPslBlockDescriptor.nameIsStorageName`): set by whoever
contributes a block whose name is the name of the storage object it
declares and whose `@@map` replaces that name. The Postgres target sets
it on `native_enum`.
- **New name check**: a name that `isPslIdentifier` rejects is refused
with an error response (`RequestFailed`, message `"1st" is not a valid
PSL identifier`) before any edit is computed.
- **Wiring**: `Project.prepareRename` and `Project.rename` follow
`Project.references`. `server.ts` declares `renameProvider`, as `{
prepareProvider: true }` when the client declares `prepareSupport`, and
registers the two handlers.
- **Playground** (`apps/lsp-playground/src/client/main.ts`): the editor
refuses a multi-file edit when one of the edited files has no model, and
a scratch file that was never selected has none, so rename failed for
every file. The client now opens each not-yet-opened scratch file named
in a rename edit before the editor applies it. Selecting a file takes
its text from the editor's model instead of a copy saved when the file
was last switched away from; otherwise selecting a file that a rename
had edited would restore its old text.
- **Tests**: `test/rename.test.ts` covers each symbol kind from the
declaration and from a reference, `prepareRename` on renameable and
non-renameable positions, and refused names. `test/server.test.ts`
covers the capability in both client modes, the handlers, the error
response and an edit to a file that is not open. The fixtures of
`test/references.test.ts` moved to `test/helpers/reference-fixtures.ts`
so both files share them; no find-references test or expectation
changed.
- **Docs**: both READMEs list rename among the supported features.

## Why

- **Edits equal references.** A rename that used its own matching could
edit a token that go-to-definition would not take to the renamed
declaration.
- **No collision check.** Renaming to a name that is already declared is
applied, and the existing `Duplicate declaration` diagnostic reports it.
TypeScript and Prisma 7 behave the same way; gopls and clangd reject
such a rename.
- **Map attribute on rename.** Without it, renaming a model or field
renames its table or column in the next migration plan. Prisma 7's
language server adds the attribute too.
- **The rule for models and fields is written in the language server**,
with the attribute names `map` and `base`, not contributed by each
family. A model with `@@base` and no `@@map` is stored with its base;
adding `@@map` would give it its own table in SQL and is an error in
Mongo, so it gets none.
- **Blocks opt in with a flag** because a block's descriptor shows that
it accepts `@@map` but not what its name means in the database: Postgres
`policy_*` blocks accept `@@map` and their unmapped name is the block
name plus a hash.

## Not in this PR

- A `native_enum` usage inside `pg.enum(OrderStatus)` is not renamed:
the binder records no resolution for an entity name inside a
type-constructor argument, so find references does not return it.
Renaming a `native_enum` edits the block name, adds `@@map`, and leaves
the usage, which then reports `PSL_UNKNOWN_ENTITY_REF`. A follow-up PR
makes the binder record that resolution; rename needs no change for it.
- Names that reach the database and have no map attribute are renamed by
name only: a namespace (the schema name on Postgres), a composite type
member, Postgres `role` and `policy_*` blocks.
- Enum members: the binder records no resolution for them, so find
references does not list them.
- Symbols with no declaration in the schema sources (attributes,
functions, contributed types, cross-space references): `prepareRename`
returns `null`.
- Application code, the emitted contract and migrations are not edited.

## Known behaviour after a collision

Found in the QA run, and accepted under the no-collision-check decision:

- After renaming `Member` to an existing `Post`, references to the older
`Post` and the renamed model resolve to one symbol. Renaming it back to
`Member` then also rewrites the references that belonged to the older
`Post`. Editor undo restores the text; a second rename does not.
- The `Duplicate declaration` diagnostic is reported in the file of one
of the two declarations, which may not be the file the rename was
started in.

## Verification

- Language-server tests (1052) and psl-parser tests (1357), typecheck
and lint pass; typecheck and lint pass for `framework-components`, the
Postgres target, and the SQL and Mongo `contract-psl` packages, with
their changed test files; `pnpm lint:deps` passes.
- Tests in the SQL and Mongo interpreters show that a schema and its
renamed version with the added attributes have the same storage.
- A QA run drove the built CLI over stdio with the Postgres target
(`projects/lsp-rename/qa/run-2026-10-07-map.md`): for a model and a
scalar field, the attribute is in the edit, diagnostics stay empty after
applying, and `contract emit` gives the same storage hash before and
after; eight cases that must get no attribute get none. The
`native_enum` scenario fails as described above.
- The playground was driven with a headless browser before the map
attribute was added: renaming a model used in two scratch files, one
never selected, changed both. It was not rerun afterwards.
- Not yet done: the manual check in VS Code and a manual pass in the
playground.
- `pnpm test:packages`: three tarball tests fail with
`ERR_PNPM_TRUST_DOWNGRADE` in their own `pnpm install`, before reaching
code from this branch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Rename support is now available in the language server, updating
declarations and references across project files.
* Rename edits can include files that haven’t been opened yet; selecting
an unopened file also opens it in the playground.
* Renaming models and fields can preserve their existing database
storage names, avoiding unintended changes to stored data.
* Rename is now listed among the language server’s documented features.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Steven McClankerton <tatarintsev@prisma.io>
Co-authored-by: Steven McClankerton <tatarintsev@prisma.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Assets 2
Loading