Expand to the new half of a rename after rebuilding the tree

When the deletion of the selected file is staged and git then reports
it as the old half of a rename whose new half sits in a collapsed
directory, the selection is meant to move to the rename. With
gui.showRootItemInFileTree turned on, the directory stays collapsed and
the selection lands on it. With the option turned off, the directory
expands, but if another file in it sorts before the rename, that file
gets selected.

The loop that expands the directory runs before the tree is rebuilt and
works on the file list instead of on tree nodes. It compares the
rename's previous path, a user-facing path, against the selected node's
internal path, so the two never match while the root item is shown. The
path it hands to ExpandToPath is user-facing as well, so the directory
would stay collapsed either way. And because the loop runs before the
old node list is captured, expanding a directory above the selection
shifts that list, and the search for the new selection starts from
whatever node moved into the selected line.

Rebuild the tree first, capture the old node list before anything
expands, and then look for the rename among the leaves of the new tree.
ExpandToPath gets the leaf's own internal path, so the two kinds of
paths never need converting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-09-20 16:03:49 +02:00
co-authored by Claude Fable 5.1
parent b4b6a993ea
commit 7d576c3d30
2 changed files with 9 additions and 15 deletions
+9 -9
View File
@@ -98,22 +98,22 @@ func (self *FileTreeViewModel) GetSelectedPath() string {
}
func (self *FileTreeViewModel) SetTree() {
newFiles := self.GetAllFiles()
selectedNode := self.GetSelected()
// for when you stage the old file of a rename and the new file is in a collapsed dir
for _, file := range newFiles {
if selectedNode != nil && selectedNode.path != "" && file.PreviousPath == selectedNode.path {
self.ExpandToPath(file.Path)
}
}
prevNodes := self.GetAllItems()
prevSelectedLineIdx := self.GetSelectedLineIdx()
self.IFileTree.SetTree()
if selectedNode != nil {
// If the selected file has become the old half of a rename, e.g. because
// its deletion was staged, make sure the rename is visible so that the
// selection can move to it.
for _, node := range self.GetRoot().GetLeaves() {
if node.File.PreviousPath == selectedNode.GetPath() {
self.ExpandToPath(node.GetInternalPath())
}
}
newNodes := self.GetAllItems()
newIdx := self.findNewSelectedIdx(prevNodes[prevSelectedLineIdx:], newNodes)
if newIdx != -1 && newIdx != prevSelectedLineIdx {
@@ -88,18 +88,12 @@ func TestSetTreeFollowsRenameIntoCollapsedDir(t *testing.T) {
{
name: "with root item",
showRootItem: true,
/* EXPECTED:
expectedPath: "a/new.go",
ACTUAL: */
expectedPath: "a",
},
{
name: "without root item",
showRootItem: false,
/* EXPECTED:
expectedPath: "a/new.go",
ACTUAL: */
expectedPath: "a/b.go",
},
}