prometeu-studio/discussion/lessons/DSC-0043-pbs-lsp-rename-symbol/LSN-0072-pbs-rename-is-a-validated-physical-workspace-edit.md
bQUARKz 65d2d40310
Some checks are pending
Intrepid/Prometeu/Studio/pipeline/pr-master Build started...
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 63.56% (19159/30145) * Branch Coverage: 54.14% (7612/14060) * Lines of Code: 30145 * Cyclomatic Complexity: 12214 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 678
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
implements PLN-0135
Add PBS rename as a validated WorkspaceEdit of physical identifier
sites on FrontendLanguageService. Identity is definition; locals and
params rename from their name span. Refuse aliases, virtual stdlib,
builtins, error labels, keywords, and collisions. Advertise
renameProvider with prepareProvider; refused cursors are LSP errors,
not empty success.

Housekeep DSC-0043 with LSN-0072.
2026-09-22 06:40:18 +01:00

7.7 KiB
Raw Blame History

id ticket title created tags
LSN-0072 pbs-lsp-rename-symbol PBS rename is a validated physical workspace edit 2026-09-22
studio
lsp
vscode
compiler-pbs
editor
rename

PBS rename is a validated physical workspace edit

Original Problem

PBS already had compiler-backed completion, hover, signature help, go-to-definition (LSN-0068), find-references (LSN-0069), outline (LSN-0070), and workspace symbols (LSN-0071). Users still could not rename a symbol through the editor. Manual text edits miss uses or rewrite unrelated overloads, comments, strings, and same-name declarations.

The risk was a file-local rename, a lexeme replace, a new SymbolId / RefIndex, snapshot cache (AGD-0051) as a prerequisite, virtual stdlib documents, or a successful empty WorkspaceEdit that pretends the symbol changed.

Consolidated Decision

PBS Rename Symbol is the LSP projection of a validated WorkspaceEdit over physical identifier spans of the same identity definition already returns.

Durable locks from DEC-0056 (AGD-0046 Q1Q6 = A):

  1. Identity is the physical declaration-location set from definition. Do not text-search. Do not add SymbolId or a persistent RefIndex.
  2. Sites are identifier tokens whose definition intersects that identity, plus identifier-sized declaration name spans. Overloads of the same name in the same owner rewrite together. Members rewrite only that member. Comments, strings, and homonyms of another identity stay.
  3. Walk regular fileTable files of the current analyze() surface. Omit /virtual/stdlib and other non-regular handles. Every edit location must be a compiler-known physical source file.
  4. Refuse when physical identity is empty: builtins, virtual stdlib/SDK, unresolved names, keywords, comments, strings, this, Self. Refuse mod.barrel. Refuse declare error case labels that have no per-label span.
  5. Exception: a local or parameter name span is renameable even when definition at that offset is empty. Identity becomes that name span; uses are identifiers whose definition intersects it.
  6. Refuse when the cursor lexeme is an import alias (item.alias() distinct from item.name()), including uses of that alias. Do not follow alias definition and rewrite the export. Export rename happens only when the cursor writes the original name. Export rewrite must not change as Alias tokens or uses that spell a differing alias.
  7. prepareRename validates the cursor and returns the identifier range, or null. rename also validates the new name: PBS identifier, not a keyword, no collision in the same binding space (top-level fn/service/global/const, local/param in the same scope, field in the same struct, method in the same owner, case in the same enum), ignoring rewritten sites and the overload set. Failure is an LSP error. Never return a successful empty WorkspaceEdit. The server does not write files and does not rename paths.
  8. Keep the capability on aggregated FrontendLanguageService. prepareRename defaults empty; rename defaults unsupported. PBS maps in prometeu-frontend-pbs. Common LSP must not import p.studio.compiler.pbs.*. No Rename SPI. Advertise renameProvider with prepareProvider. Compile-only stays unsupported. VS Code stays a thin client.

Spec 23 §8.3 now also states: rename is a validated workspace edit of identifier spans in compiler-known physical source files; identity is definition; local/param name spans remain renameable; alias, virtual, and error-label cursors refuse; invalid names and collisions are protocol errors, not empty success; the server does not write files; prepareProvider is advertised when rename is exposed.

Final Implementation

Layer What landed
PBS editorial PbsEditorialSupportService.planRename builds identity from definition or local/param name span, refuses aliases and error labels, walks IDENTIFIER tokens whose definition intersects identity and whose lexeme is the original name, then validates identifier/keyword/collision.
Generic contract FrontendLanguageService.prepareRename defaults empty. rename defaults FrontendRenameResult.unsupported(). FrontendRenameEdit is path + offsets + newText. Applied results reject an empty edit list.
PBS mapping PBSFrontendLanguageService reuses projectDocuments and toFrontendLocation. Empty physical identity is refuse, even if editorial sites exist on virtual handles.
LSP renameSupported, prepareRename, rename. Mapper advertises RenameOptions.prepareProvider. Refused rename is ResponseErrorException, not an empty WorkspaceEdit. Compile-only does not advertise.
VS Code Unchanged thin client.

Stdlib and SDK modules remain virtual (/virtual/stdlib/...). F2 on Gfx.clear is refused, matching empty F12 / Shift+F12. Hover is unchanged.

let total at the binding itself still has empty definition. Rename uses the name span as identity so F2 on the binding works.

Examples

Same-file function, including overloads and the declaring identifier. Comment and string stay:

fn helper() -> int { return 1; }
fn helper(value: int) -> int { return value; }

fn frame() -> void {
  helper();
  helper(2);
  // helper
  let text = "helper";
}

F2 on helperaid rewrites both declarations and both calls.

Local name span:

fn frame() -> void {
  let total = 1;
  return total;
}

F2 on let total or on the use rewrites both identifier sites.

Alias is refused. Export rename keeps the local alias:

import { Log as Logger } from @sdk:log;
Logger.info("a");

F2 on Logger or Logger.info is refused. F2 on a physical Log declaration rewrites the original name and import { Log as Logger }, and leaves Logger / Logger.info.

Builtins and virtual stdlib stay hover-only:

let n: int = 1;
Gfx.clear(5);

Invalid new names (fn, 1ab) and collisions (helper → existing frame) fail with an LSP error.

Pitfalls

Do not implement rename as workspace text search in lsp-v1 or in the VS Code extension. It will rewrite comments, strings, overloads of another identity, and import aliases.

Do not follow definition through an import alias. Logger currently resolves to Log's span; a blind inverse-of-definition rename would rewrite the export.

Do not return a successful empty WorkspaceEdit for a refused cursor. The editor will treat that as “renamed”.

Do not skip the local/param name-span exception. F2 on let total is the common case, and definition there is empty.

Do not rewrite declare error case labels by using the parent ErrorDecl span. Those labels have no per-label span.

Do not wait for snapshot cache. Rename reuses the request-local walk already used by references.

Do not treat empty/refused stdlib rename as a missing feature. Virtual paths are not regular files.

Do not split a Rename SPI “for future languages”. PBS is a real consumer of FrontendLanguageService (LSN-0067).

Do not treat this lesson as closing snapshots, folding, document links, hierarchy, code actions, formatting, file rename, or local-alias rename.

References

  • Agenda: AGD-0046 (Q1Q6 = A)
  • Decision: DEC-0056
  • Plan: PLN-0135
  • Spec 23 §8.3 — optional rename; physical-file-or-empty; alias refuse; error instead of empty success; prepareProvider
  • Spec: docs/specs/compiler-languages/pbs/3. Core Syntax Specification.md §4.34.4
  • LSN-0068 — definition identity this feature writes
  • LSN-0069 — physical inverse that supplies usage sites
  • LSN-0051 — wave 1 deferred rename
  • LSN-0058 — generic FrontendLanguageService for LSP
  • LSN-0047 — protocol stays in lsp-v1; compiler owns semantics
  • LSN-0067 — no SPI without a real in-repo consumer
  • Still open: AGD-0051 snapshots, AGD-0053 folding, AGD-0055 document links, AGD-0056 hierarchy