Skip to content

Fix F16 Step Rename: parameter mismatch, closed-.cs staleness, keyword duplication - #37

Merged
clrudolphi merged 4 commits into
masterfrom
fix/feature-rename-parameter-mismatch
Jul 3, 2026
Merged

Fix F16 Step Rename: parameter mismatch, closed-.cs staleness, keyword duplication#37
clrudolphi merged 4 commits into
masterfrom
fix/feature-rename-parameter-mismatch

Conversation

@clrudolphi

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes the F16 Step Rename feature silently no-op'ing when a parameterized step is renamed from a .feature file (VS Code's native F2 seeds the dialog with concrete step text, not the abstract binding expression, which previously always failed the parameter-count validation).
  • Handles both call shapes through the same textDocument/rename handler: VS Code's concrete-text submission and VS's custom "Rename Step" command's already-abstract submission.
  • Fixes a closed .cs file's renamed binding attribute leaving the server's registry stale (feature step showed "unbound") until the file was manually reopened.
  • Fixes prepareRename returning a synthetic whole-line range for .feature steps, which duplicated the step keyword/indentation when the resulting edit was applied.

Test plan

  • Reqnroll.IdeSupport.LSP.Core.Tests (429 passed)
  • Reqnroll.IdeSupport.LSP.Server.Tests (519 passed, including new regression tests for each fix)
  • Reqnroll.IdeSupport.LSP.Server.Specs (152 passed, including new end-to-end rename scenarios)
  • Reqnroll.IdeSupport.VisualStudio.Wizards.Tests (63 passed)
  • Reqnroll.VisualStudio.Tests (79 passed, including new WorkspaceEditApplier tests)
  • Manually verified in both VS Code (native F2 from .feature) and Visual Studio (custom Rename Step command from both .feature and .cs) by Chris

Follow-up enhancements filed separately: #33, #34, #35, #36

🤖 Generated with Claude Code

clrudolphi and others added 4 commits July 3, 2026 13:30
…files

VS Code seeds the F2 rename dialog for a .feature step with the step's
concrete text (real parameter values), since prepareRename's returned
range covers the whole line rather than just the abstract expression.
HandleRenameAsync validated that concrete text directly against the
binding's abstract expression, so the parameter-count check always
failed for any parameterized step renamed from the feature side -
returning null, which the LSP wrapper silently converts to an empty
WorkspaceEdit with no visible error.

Add FeatureStepTextBuilder.DeriveExpressionFromEditedText to reconcile
the edited concrete text back into an abstract expression (locating the
original parameter values in both the old and new text) before
validating and propagating the rename to the .cs attribute and other
feature usages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dy abstract

VS's custom "Rename Step" command (RenameStepCommand.cs) seeds its own
dialog with the binding's abstract expression regardless of which file
the cursor was in, and submits that abstract text verbatim as newName.
The previous fix assumed every .feature-triggered rename arrives as
concrete text (true for VS Code's native F2, seeded via prepareRename's
whole-line range) and unconditionally tried to locate old parameter
values inside newName - which fails and wrongly rejects renames that
were already in the correct abstract form.

Only attempt the concrete-text reconciliation when newName's parameter
slot count doesn't already match the live source expression's, so both
call patterns work through the same textDocument/rename handler.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
WorkspaceEditApplier.NotifyDidChange only sent textDocument/didChange
for .feature files. When a rename rewrote a closed .cs file's binding
attribute via File.WriteAllText (ApplyToDisk), the server was never
told - its Roslyn binding registry (CSharpBindingDiscoveryService) is
only refreshed by didOpen/didChange, and WatchedFilesHandler has no
file-system watcher for .cs content changes (only reqnroll.json,
.editorconfig, output assemblies, and .cs *deletions*). The renamed
feature step showed as unbound until the .cs file was manually opened,
triggering didOpen and a live re-parse.

Extend the notification to .cs files too - TextDocumentSyncHandler
already handles didChange for .cs unconditionally via
UpdateFromSourceAsync, with no dependency on a prior didOpen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
HandlePrepareRenameAsync returned a synthetic whole-line range (column
0-200) for .feature steps, so VS Code seeded the rename dialog with the
keyword and indentation included (e.g. "\tThen the result should be
120"). Submitting an edited copy of that back as newName, the resulting
edit was still applied only at usage.Range - the step-text-only span,
excluding the keyword - so the untouched keyword prefix and the
keyword baked into the submitted text both ended up in the file:
"\tThen \tThen the result should be 120".

FindBindingsAtFeatureStep now also reports the matched step's own
GherkinRange (excluding keyword/indentation, same span HandleRenameAsync
edits at), and prepareRename returns that instead of the synthetic
whole-line range.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@clrudolphi
clrudolphi merged commit aa7ea75 into master Jul 3, 2026
7 checks passed
@clrudolphi
clrudolphi deleted the fix/feature-rename-parameter-mismatch branch July 3, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant