fix(lsp): apply each rename edit to its own document
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -11,6 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
-
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Rename edits land in their own documents.** A rename collected the
|
||||
ranges of every reference across the open documents but applied them all
|
||||
to the document that started it, so renaming a symbol used in a second
|
||||
file moved that file's text into the first. Each edit now applies to the
|
||||
document it was collected in.
|
||||
|
||||
## [0.35.0] - 2026-09-22
|
||||
|
||||
### Added
|
||||
|
||||
+6
-6
@@ -229,7 +229,9 @@ func (s *Server) references(p referenceParams) []Location {
|
||||
return out
|
||||
}
|
||||
|
||||
// rename renames a label across the document.
|
||||
// rename renames a label across every document the references reach. Each
|
||||
// edit is grouped under the URI it belongs to: a range collected in another
|
||||
// file must never be applied to the document that started the rename.
|
||||
func (s *Server) rename(p renameParams) *WorkspaceEdit {
|
||||
text := s.docs[p.TextDocument.URI]
|
||||
word, _ := wordAt(text, p.Position)
|
||||
@@ -244,13 +246,11 @@ func (s *Server) rename(p renameParams) *WorkspaceEdit {
|
||||
if len(refs) == 0 {
|
||||
return nil
|
||||
}
|
||||
edits := make([]TextEdit, 0, len(refs))
|
||||
edits := make(map[string][]TextEdit, len(refs))
|
||||
for _, loc := range refs {
|
||||
edits = append(edits, TextEdit{Range: loc.Range, NewText: p.NewName})
|
||||
}
|
||||
return &WorkspaceEdit{
|
||||
Changes: map[string][]TextEdit{p.TextDocument.URI: edits},
|
||||
edits[loc.URI] = append(edits[loc.URI], TextEdit{Range: loc.Range, NewText: p.NewName})
|
||||
}
|
||||
return &WorkspaceEdit{Changes: edits}
|
||||
}
|
||||
|
||||
// documentFormatting returns a single edit that replaces the whole document
|
||||
|
||||
@@ -1074,3 +1074,49 @@ func TestCrossFileDefinitionAndReferences(t *testing.T) {
|
||||
t.Errorf("references by uri = %v, want one in each file", byURI)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenameGroupsEditsByDocument pins the grouping contract: every edit is
|
||||
// applied to the document it was collected in, so a cross-file rename never
|
||||
// corrupts the origin buffer.
|
||||
func TestRenameGroupsEditsByDocument(t *testing.T) {
|
||||
docA := "TEXT \u00b7caller(SB), NOSPLIT, $0\n" +
|
||||
"\tCALL \u00b7helper(SB)\n" +
|
||||
"\tRET\n"
|
||||
docB := "TEXT \u00b7helper(SB), NOSPLIT, $0\n" +
|
||||
"\tRET\n"
|
||||
uriA := "file:///rename_a_amd64.s"
|
||||
uriB := "file:///rename_b_amd64.s"
|
||||
in := frame(1, "initialize", map[string]any{}) +
|
||||
frame(nil, "initialized", map[string]any{}) +
|
||||
frame(nil, "textDocument/didOpen", map[string]any{
|
||||
"textDocument": map[string]any{"uri": uriA, "languageId": "gasm", "version": 1, "text": docA},
|
||||
}) +
|
||||
frame(nil, "textDocument/didOpen", map[string]any{
|
||||
"textDocument": map[string]any{"uri": uriB, "languageId": "gasm", "version": 1, "text": docB},
|
||||
}) +
|
||||
frame(2, "textDocument/rename", map[string]any{
|
||||
"textDocument": map[string]any{"uri": uriA},
|
||||
"position": map[string]any{"line": 1, "character": 8}, // on helper in CALL ·helper(SB)
|
||||
"newName": "renamed",
|
||||
}) + frame(nil, "exit", nil)
|
||||
msgs := run(t, in)
|
||||
resp := findByID(msgs, 2)
|
||||
if resp == nil {
|
||||
t.Fatal("no rename response")
|
||||
}
|
||||
var edit WorkspaceEdit
|
||||
if err := json.Unmarshal(mustResult(t, resp), &edit); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(edit.Changes[uriA]) != 1 || len(edit.Changes[uriB]) != 1 {
|
||||
t.Fatalf("edits per uri = %d and %d, want one each", len(edit.Changes[uriA]), len(edit.Changes[uriB]))
|
||||
}
|
||||
gotA := applyEdits(docA, edit.Changes[uriA])
|
||||
gotB := applyEdits(docB, edit.Changes[uriB])
|
||||
if !strings.Contains(gotA, "CALL renamed(SB)") || strings.Contains(gotA, "renamed(SB), NOSPLIT") {
|
||||
t.Errorf("caller document rename wrong: %q", gotA)
|
||||
}
|
||||
if !strings.Contains(gotB, "TEXT renamed(SB)") {
|
||||
t.Errorf("helper document rename wrong: %q", gotB)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user