Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/Config.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ gui:
# When mouse events are captured, it's a little harder to select text: e.g. requiring you to hold the option key when on macOS.
mouseEvents: true

# Automatically adjust the width of the side panel to fit the longest visible filename.
# The width is capped at a half of the total screen width to prevent the panel from becoming excessively large.
# This is useful for projects with deeply nested file structures.
sidePanelAutoWidth: false


# If true, do not show a warning when amending a commit.
skipAmendWarning: false

Expand Down
1,040 changes: 1,040 additions & 0 deletions en.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ type GuiConfig struct {
CommitAuthorShortLength int `yaml:"commitAuthorShortLength"`
// Length of author name in expanded commits view. 2 means show initials only.
CommitAuthorLongLength int `yaml:"commitAuthorLongLength"`
// Automatically adjust side panel width to fit the longest filename (capped at a screen percentage). Defaults to false.
SidePanelAutoWidth bool `yaml:"sidePanelAutoWidth"`
// Length of commit hash in commits view. 0 shows '*' if NF icons aren't on.
CommitHashLength int `yaml:"commitHashLength" jsonschema:"minimum=0"`
// If true, show commit hashes alongside branch names in the branches view.
Expand Down
12 changes: 10 additions & 2 deletions pkg/gui/controllers/helpers/refresh_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"
"sync"
"time"
"unicode/utf8"

"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/gocui"
Expand Down Expand Up @@ -531,7 +532,6 @@ func (self *RefreshHelper) refreshFilesAndSubmodules() error {

func (self *RefreshHelper) refreshStateFiles() error {
fileTreeViewModel := self.c.Contexts().Files.FileTreeViewModel

prevConflictFileCount := 0
if self.c.UserConfig().Git.AutoStageResolvedConflicts {
// If git thinks any of our files have inline merge conflicts, but they actually don't,
Expand Down Expand Up @@ -570,10 +570,17 @@ func (self *RefreshHelper) refreshStateFiles() error {
})

conflictFileCount := 0
requiredContentWidth := 0
for _, file := range files {
if file.HasMergeConflicts {
conflictFileCount++
}
if len(file.Names()) > 0 {
fileNameLen := utf8.RuneCountInString(file.Names()[0])
if fileNameLen > requiredContentWidth {
requiredContentWidth = fileNameLen
}
}
}

if self.c.Git().Status.WorkingTreeState().Any() && conflictFileCount == 0 && prevConflictFileCount > 0 {
Expand All @@ -582,7 +589,6 @@ func (self *RefreshHelper) refreshStateFiles() error {

fileTreeViewModel.RWMutex.Lock()

// only taking over the filter if it hasn't already been set by the user.
if conflictFileCount > 0 && prevConflictFileCount == 0 {
if fileTreeViewModel.GetFilter() == filetree.DisplayAll {
fileTreeViewModel.SetStatusFilter(filetree.DisplayConflicted)
Expand All @@ -596,6 +602,8 @@ func (self *RefreshHelper) refreshStateFiles() error {
self.c.Model().Files = files
fileTreeViewModel.SetTree()
fileTreeViewModel.RWMutex.Unlock()
self.c.Model().SidePanelAutoWidth = requiredContentWidth
self.refreshView(self.c.Contexts().Files)

return nil
}
Expand Down
20 changes: 15 additions & 5 deletions pkg/gui/controllers/helpers/window_arrangement_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ type WindowArrangementArgs struct {
InSearchPrompt bool
// One of '' (not searching), 'Search: ', and 'Filter: '
SearchPrefix string
// Proportional width of the side panel, as a float between 0.1 and 0.9. Defaults to 0.33.
SidePanelAutoWidth int
}

func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string, appStatus string) map[string]boxlayout.Dimensions {
Expand Down Expand Up @@ -101,6 +103,7 @@ func (self *WindowArrangementHelper) GetWindowDimensions(informationStr string,
IsAnyModeActive: self.modeHelper.IsAnyModeActive(),
InSearchPrompt: repoState.InSearchPrompt(),
SearchPrefix: searchPrefix,
SidePanelAutoWidth: self.c.Model().SidePanelAutoWidth,
}

return GetWindowDimensions(args)
Expand Down Expand Up @@ -138,18 +141,25 @@ func GetWindowDimensions(args WindowArrangementArgs) map[string]boxlayout.Dimens
infoSectionSize = 1
}

sidePanelBox := &boxlayout.Box{
Direction: boxlayout.ROW,
ConditionalChildren: sidePanelChildren(args),
}

if args.UserConfig.Gui.SidePanelAutoWidth && args.SidePanelAutoWidth > 0 {
sidePanelBox.Size = min(args.Width/2, args.SidePanelAutoWidth)
} else {
sidePanelBox.Weight = sideSectionWeight
}

root := &boxlayout.Box{
Direction: boxlayout.ROW,
Children: []*boxlayout.Box{
{
Direction: sidePanelsDirection,
Weight: 1,
Children: []*boxlayout.Box{
{
Direction: boxlayout.ROW,
Weight: sideSectionWeight,
ConditionalChildren: sidePanelChildren(args),
},
sidePanelBox,
{
Direction: boxlayout.ROW,
Weight: mainSectionWeight,
Expand Down
3 changes: 2 additions & 1 deletion pkg/gui/filetree/file_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ func (self *FileTree) SetTree() {
filesForDisplay := self.getFilesForDisplay()
showRootItem := self.common.UserConfig().Gui.ShowRootItemInFileTree
if self.showTree {
self.tree = BuildTreeFromFiles(filesForDisplay, showRootItem)
tree := BuildTreeFromFiles(filesForDisplay, showRootItem)
self.tree = tree
} else {
self.tree = BuildFlatTreeFromFiles(filesForDisplay, showRootItem)
}
Expand Down
26 changes: 26 additions & 0 deletions pkg/gui/filetree/file_tree_view_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,29 @@ func (self *FileTreeViewModel) ExpandAll() {
self.SetSelectedLineIdx(index)
}
}

// func (self *FileTreeViewModel) GetRequiredWidth() int {
// maxWidth := 0
// // We need a recursive function to walk the tree
// var traverse func(node *Node[models.File], depth int)
// traverse = func(node *Node[models.File], depth int) {
// if node != nil {
// // Calculate the width of the indentation string (e.g., " ├─ ")
// indentationWidth := getIndentationWidth(depth) // You'll need to figure out how lazygit calculates this
//
// // Calculate the total width for this line
// lineLength := utf8.RuneCountInString(node.GetFile().Name) + indentationWidth
// if lineLength > maxWidth {
// maxWidth = lineLength
// }
//
// // Recurse for all children
// for _, child := range node.Children {
// traverse(child, depth+1)
// }
// }
// }
//
// traverse(self.GetRoot(), 0)
// return maxWidth
// }
6 changes: 6 additions & 0 deletions pkg/gui/types/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,12 @@ type Model struct {
Authors map[string]*models.Author

HashPool *utils.StringPool

// SidePanelAutoWidth stores the required width to display the longest file name
// in the files panel without truncation. This value is calculated whenever the
// file state is refreshed and is used by the layout manager to conditionally
// set a fixed size for the side panel.
SidePanelAutoWidth int
}

type Mutexes struct {
Expand Down