mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-28 02:24:49 -05:00
The transient contexts (remoteBranches, subCommits, commitFiles) take
over the window of the context they are drilled into from, but until
then they carry a hardcoded initial window ("branches" or
"commits"). Under a gui.sidePanels config where those tabs aren't
their panel's first, no window of that name exists, leaving the
window-to-view map with entries for windows the layout never
produces. The previous commit made such entries harmless, but there's
no reason to have contexts point at nonexistent windows in the first
place; assign them the window hosting branches or commits instead,
which the config validation guarantees to exist.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
138 lines
5.2 KiB
Go
138 lines
5.2 KiB
Go
package gui
|
|
|
|
import (
|
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
// sidePanelViewNames maps each gui.sidePanels name to the gocui view it controls.
|
|
// A panel's window name is the name of its first tab, so for a panel's first tab
|
|
// this also gives the default view of its window. The keys must match
|
|
// config.ValidSidePanelTabs (enforced by a test).
|
|
var sidePanelViewNames = map[string]string{
|
|
"status": "status",
|
|
"files": "files",
|
|
"worktrees": "worktrees",
|
|
"submodules": "submodules",
|
|
"branches": "localBranches",
|
|
"remotes": "remotes",
|
|
"tags": "tags",
|
|
"commits": "commits",
|
|
"reflog": "reflogCommits",
|
|
"stash": "stash",
|
|
}
|
|
|
|
// sidePanelTabTitles maps each gui.sidePanels name to the title shown on its tab.
|
|
func (gui *Gui) sidePanelTabTitles() map[string]string {
|
|
tr := gui.c.Tr
|
|
return map[string]string{
|
|
"status": tr.StatusTitle,
|
|
"files": tr.FilesTitle,
|
|
"worktrees": tr.WorktreesTitle,
|
|
"submodules": tr.SubmodulesTitle,
|
|
"branches": tr.LocalBranchesTitle,
|
|
"remotes": tr.RemotesTitle,
|
|
"tags": tr.TagsTitle,
|
|
"commits": tr.CommitsTitle,
|
|
"reflog": tr.ReflogCommitsTitle,
|
|
"stash": tr.StashTitle,
|
|
}
|
|
}
|
|
|
|
// sidePanelContexts maps each gui.sidePanels name to the context it controls.
|
|
func sidePanelContexts(contextTree *context.ContextTree) map[string]types.Context {
|
|
return map[string]types.Context{
|
|
"status": contextTree.Status,
|
|
"files": contextTree.Files,
|
|
"worktrees": contextTree.Worktrees,
|
|
"submodules": contextTree.Submodules,
|
|
"branches": contextTree.Branches,
|
|
"remotes": contextTree.Remotes,
|
|
"tags": contextTree.Tags,
|
|
"commits": contextTree.LocalCommits,
|
|
"reflog": contextTree.ReflogCommits,
|
|
"stash": contextTree.Stash,
|
|
}
|
|
}
|
|
|
|
// applySidePanelConfig (re)assigns each side context's window and resets each
|
|
// window's default view from the current gui.sidePanels config. It runs against
|
|
// the current repo's contexts, so gui.State must already be set. We call it on
|
|
// every repo entry (a repo's per-repo config can differ from the previous one's)
|
|
// and on a live config reload.
|
|
func (gui *Gui) applySidePanelConfig() {
|
|
contextTree := gui.State.Contexts
|
|
gui.assignSidePanelWindows(contextTree)
|
|
gui.State.WindowViewNameMap = gui.initialWindowViewNameMap(contextTree)
|
|
}
|
|
|
|
// moveDefaultTabsToTop brings each panel's first configured tab to the top of
|
|
// its window, so the configured default tab is the one shown when a panel hasn't
|
|
// been focused yet (the view z-order is otherwise set from a fixed list that
|
|
// need not match the configured tab order).
|
|
func (gui *Gui) moveDefaultTabsToTop() {
|
|
contexts := sidePanelContexts(gui.State.Contexts)
|
|
for _, panel := range gui.c.UserConfig().Gui.SidePanels {
|
|
gui.helpers.Window.MoveToTopOfWindow(contexts[panel[0]])
|
|
}
|
|
}
|
|
|
|
// reloadSidePanels re-applies the side panel config to the current repo after a
|
|
// live config reload: it reassigns windows and default views, restores each
|
|
// panel's default tab, and keeps the focused panel in a consistent state.
|
|
func (gui *Gui) reloadSidePanels() {
|
|
gui.applySidePanelConfig()
|
|
gui.moveDefaultTabsToTop()
|
|
|
|
// applySidePanelConfig reset every window to show its first configured tab,
|
|
// which would leave the focused tab hidden behind its panel's default tab
|
|
// (the panel would look unfocused even though its tab is selected). Re-focus
|
|
// the current context so its tab stays shown and highlighted. If the new
|
|
// config has hidden the focused panel entirely, move focus to the default
|
|
// side panel instead.
|
|
current := gui.c.Context().Current()
|
|
if current.GetKind() != types.SIDE_CONTEXT {
|
|
return
|
|
}
|
|
|
|
if lo.Contains(gui.helpers.Window.SideWindows(), current.GetWindowName()) {
|
|
gui.c.Context().Activate(current, types.OnFocusOpts{})
|
|
} else {
|
|
gui.c.Context().Push(gui.defaultSideContext(), types.OnFocusOpts{})
|
|
}
|
|
}
|
|
|
|
// assignSidePanelWindows sets each side context's window name from the config so
|
|
// that contexts grouped into one panel share a window (the window name being the
|
|
// panel's first tab). Side panels the user hasn't listed get their own window
|
|
// name; since the layout produces no dimensions for those windows, their views
|
|
// stay hidden rather than overlapping a visible panel.
|
|
func (gui *Gui) assignSidePanelWindows(contextTree *context.ContextTree) {
|
|
contexts := sidePanelContexts(contextTree)
|
|
assigned := make(map[string]bool, len(contexts))
|
|
|
|
for _, panel := range gui.c.UserConfig().Gui.SidePanels {
|
|
windowName := panel[0]
|
|
for _, name := range panel {
|
|
contexts[name].SetWindowName(windowName)
|
|
assigned[name] = true
|
|
}
|
|
}
|
|
|
|
for name, ctx := range contexts {
|
|
if !assigned[name] {
|
|
ctx.SetWindowName(name)
|
|
}
|
|
}
|
|
|
|
// The transient contexts take over the window of the context they are
|
|
// drilled into from, but they need a valid initial window before their
|
|
// first use. Assign the window hosting branches or commits, respectively;
|
|
// unlike e.g. remotes, those tabs can't be hidden, so their windows are
|
|
// always part of the layout.
|
|
contextTree.RemoteBranches.SetWindowName(contextTree.Branches.GetWindowName())
|
|
contextTree.SubCommits.SetWindowName(contextTree.Branches.GetWindowName())
|
|
contextTree.CommitFiles.SetWindowName(contextTree.LocalCommits.GetWindowName())
|
|
}
|