Files
lazygit/pkg/gui/filetree/commit_file_tree_view_model_test.go
Stefan HallerandClaude Opus 4.8 7c4d8045f9 Clamp the commit-file tree selection when the tree is rebuilt
CommitFileTreeViewModel embedded the low-level tree's SetTree, which
rebuilds the node list without touching the cursor. So after a shrinking
rebuild (e.g. moving a patch out into the index removes a file), the
selection index could be left past the end of the tree. GetSelectedItems
then indexes out of range and returns a nil node, which segfaults callers
such as canEditFiles when the options map is rendered during layout.

Override SetTree to ClampSelection after the rebuild. Unlike
FileTreeViewModel we deliberately don't also re-find the selected node by
path: that walk lands on the containing directory when a file is removed
from a dir that then collapses, whereas keeping the clamped index lands
on the sibling file (see discard_old_file_changes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:09:33 +02:00

41 lines
1.2 KiB
Go

package filetree
import (
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/stretchr/testify/assert"
)
// When the tree shrinks under the selection - e.g. moving a patch out into the
// index removes a file - SetTree must keep the selection in range. Otherwise
// GetSelectedItems returns a nil node, which crashes callers such as
// canEditFiles when the options map is rendered during layout.
func TestCommitFileTreeViewModelSetTreeClampsSelectionOnShrink(t *testing.T) {
files := []*models.CommitFile{
{Path: "file1"},
{Path: "file2"},
{Path: "file3"},
}
viewModel := NewCommitFileTreeViewModel(
func() []*models.CommitFile { return files },
common.NewDummyCommon(),
false, // flat list
)
viewModel.SetTree()
viewModel.SetSelectedLineIdx(viewModel.Len() - 1)
// The file under the cursor goes away and the tree shrinks.
files = []*models.CommitFile{{Path: "file1"}}
viewModel.SetTree()
assert.Less(t, viewModel.GetSelectedLineIdx(), viewModel.Len())
assert.NotNil(t, viewModel.GetSelected())
items, _, _ := viewModel.GetSelectedItems()
assert.NotEmpty(t, items)
for _, item := range items {
assert.NotNil(t, item)
}
}