summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2020-09-30 08:27:23 +1000
committerJesse Duffield <jessedduffield@gmail.com>2020-10-02 08:09:42 +1000
commit7b4a0f20b26d884cb756f09bc7514e57941709d4 (patch)
tree89fe1962fd3bc4b2b8a58b41eef15191839df909
parent3b93b5dde410fbed657f04c16d6b89c80c5be861 (diff)
add submodules context
-rw-r--r--pkg/commands/models/submodule_config.go12
-rw-r--r--pkg/gui/context.go129
-rw-r--r--pkg/gui/diffing.go3
-rw-r--r--pkg/gui/discard_changes_menu_panel.go4
-rw-r--r--pkg/gui/files_panel.go34
-rw-r--r--pkg/gui/gui.go20
-rw-r--r--pkg/gui/keybindings.go44
-rw-r--r--pkg/gui/layout.go2
-rw-r--r--pkg/gui/list_context.go24
-rw-r--r--pkg/gui/presentation/submodules.go21
-rw-r--r--pkg/gui/submodules_panel.go135
-rw-r--r--pkg/gui/view_helpers.go24
-rw-r--r--pkg/gui/workspace_reset_options_panel.go2
-rw-r--r--pkg/i18n/english.go6
14 files changed, 367 insertions, 93 deletions
diff --git a/pkg/commands/models/submodule_config.go b/pkg/commands/models/submodule_config.go
index c6481bbd1..f52576921 100644
--- a/pkg/commands/models/submodule_config.go
+++ b/pkg/commands/models/submodule_config.go
@@ -5,3 +5,15 @@ type SubmoduleConfig struct {
Path string
Url string
}
+
+func (r *SubmoduleConfig) RefName() string {
+ return r.Name
+}
+
+func (r *SubmoduleConfig) ID() string {
+ return r.RefName()
+}
+
+func (r *SubmoduleConfig) Description() string {
+ return r.RefName()
+}
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
index 585838505..3119065a5 100644
--- a/pkg/gui/context.go
+++ b/pkg/gui/context.go
@@ -34,6 +34,7 @@ const (
CONFIRMATION_CONTEXT_KEY = "confirmation"
SEARCH_CONTEXT_KEY = "search"
COMMIT_MESSAGE_CONTEXT_KEY = "commitMessage"
+ SUBMODULES_CONTEXT_KEY = "submodules"
)
var allContextKeys = []string{
@@ -57,6 +58,64 @@ var allContextKeys = []string{
CONFIRMATION_CONTEXT_KEY,
SEARCH_CONTEXT_KEY,
COMMIT_MESSAGE_CONTEXT_KEY,
+ SUBMODULES_CONTEXT_KEY,
+}
+
+type SimpleContextNode struct {
+ Context Context
+}
+
+type RemotesContextNode struct {
+ Context Context
+ Branches SimpleContextNode
+}
+
+type ContextTree struct {
+ Status SimpleContextNode
+ Files SimpleContextNode
+ Submodules SimpleContextNode
+ Menu SimpleContextNode
+ Branches SimpleContextNode
+ Remotes RemotesContextNode
+ Tags SimpleContextNode
+ BranchCommits SimpleContextNode
+ CommitFiles SimpleContextNode
+ ReflogCommits SimpleContextNode
+ SubCommits SimpleContextNode
+ Stash SimpleContextNode
+ Normal SimpleContextNode
+ Staging SimpleContextNode
+ PatchBuilding SimpleContextNode
+ Merging SimpleContextNode
+ Credentials SimpleContextNode
+ Confirmation SimpleContextNode
+ CommitMessage SimpleContextNode
+ Search SimpleContextNode
+}
+
+func (gui *Gui) allContexts() []Context {
+ return []Context{
+ gui.Contexts.Status.Context,
+ gui.Contexts.Files.Context,
+ gui.Contexts.Submodules.Context,
+ gui.Contexts.Branches.Context,
+ gui.Contexts.Remotes.Context,
+ gui.Contexts.Remotes.Branches.Context,
+ gui.Contexts.Tags.Context,
+ gui.Contexts.BranchCommits.Context,
+ gui.Contexts.CommitFiles.Context,
+ gui.Contexts.ReflogCommits.Context,
+ gui.Contexts.Stash.Context,
+ gui.Contexts.Menu.Context,
+ gui.Contexts.Confirmation.Context,
+ gui.Contexts.Credentials.Context,
+ gui.Contexts.CommitMessage.Context,
+ gui.Contexts.Normal.Context,
+ gui.Contexts.Staging.Context,
+ gui.Contexts.Merging.Context,
+ gui.Contexts.PatchBuilding.Context,
+ gui.Contexts.SubCommits.Context,
+ }
}
type Context interface {
@@ -139,61 +198,6 @@ func (c BasicContext) GetKey() string {
return c.Key
}
-type SimpleContextNode struct {
- Context Context
-}
-
-type RemotesContextNode struct {
- Context Context
- Branches SimpleContextNode
-}
-
-type ContextTree struct {
- Status SimpleContextNode
- Files SimpleContextNode
- Menu SimpleContextNode
- Branches SimpleContextNode
- Remotes RemotesContextNode
- Tags SimpleContextNode
- BranchCommits SimpleContextNode
- CommitFiles SimpleContextNode
- ReflogCommits SimpleContextNode
- SubCommits SimpleContextNode
- Stash SimpleContextNode
- Normal SimpleContextNode
- Staging SimpleContextNode
- PatchBuilding SimpleContextNode
- Merging SimpleContextNode
- Credentials SimpleContextNode
- Confirmation SimpleContextNode
- CommitMessage SimpleContextNode
- Search SimpleContextNode
-}
-
-func (gui *Gui) allContexts() []Context {
- return []Context{
- gui.Contexts.Status.Context,
- gui.Contexts.Files.Context,
- gui.Contexts.Branches.Context,
- gui.Contexts.Remotes.Context,
- gui.Contexts.Remotes.Branches.Context,
- gui.Contexts.Tags.Context,
- gui.Contexts.BranchCommits.Context,
- gui.Contexts.CommitFiles.Context,
- gui.Contexts.ReflogCommits.Context,
- gui.Contexts.Stash.Context,
- gui.Contexts.Menu.Context,
- gui.Contexts.Confirmation.Context,
- gui.Contexts.Credentials.Context,
- gui.Contexts.CommitMessage.Context,
- gui.Contexts.Normal.Context,
- gui.Contexts.Staging.Context,
- gui.Contexts.Merging.Context,
- gui.Contexts.PatchBuilding.Context,
- gui.Contexts.SubCommits.Context,
- }
-}
-
func (gui *Gui) contextTree() ContextTree {
return ContextTree{
Status: SimpleContextNode{
@@ -207,6 +211,9 @@ func (gui *Gui) contextTree() ContextTree {
Files: SimpleContextNode{
Context: gui.filesListContext(),
},
+ Submodules: SimpleContextNode{
+ Context: gui.submodulesListContext(),
+ },
Menu: SimpleContextNode{
Context: gui.menuListContext(),
},
@@ -365,6 +372,18 @@ func (gui *Gui) viewTabContextMap() map[string][]tabContext {
},
},
},
+ "files": {
+ {
+ tab: "Files",
+ contexts: []Context{gui.Contexts.Files.Context},
+ },
+ {
+ tab: "Submodules",
+ contexts: []Context{
+ gui.Contexts.Submodules.Context,
+ },
+ },
+ },
}
}
diff --git a/pkg/gui/diffing.go b/pkg/gui/diffing.go
index 7da33dee0..58a549d62 100644
--- a/pkg/gui/diffing.go
+++ b/pkg/gui/diffing.go
@@ -34,7 +34,8 @@ func (gui *Gui) currentDiffTerminals() []string {
switch gui.currentContext().GetKey() {
case "":
return nil
- case FILES_CONTEXT_KEY:
+ case FILES_CONTEXT_KEY, SUBMODULES_CONTEXT_KEY:
+ // TODO: should we just return nil here?
return []string{""}
case COMMIT_FILES_CONTEXT_KEY:
return []string{gui.State.Panels.CommitFiles.refName}
diff --git a/pkg/gui/discard_changes_menu_panel.go b/pkg/gui/discard_changes_menu_panel.go
index 8c761a3e9..6805187c4 100644
--- a/pkg/gui/discard_changes_menu_panel.go
+++ b/pkg/gui/discard_changes_menu_panel.go
@@ -6,7 +6,7 @@ import (
)
func (gui *Gui) submoduleFromFile(file *models.File) *models.SubmoduleConfig {
- for _, config := range gui.State.SubmoduleConfigs {
+ for _, config := range gui.State.Submodules {
if config.Name == file.Name {
return config
}
@@ -23,7 +23,7 @@ func (gui *Gui) handleCreateDiscardMenu(g *gocui.Gui, v *gocui.View) error {
var menuItems []*menuItem
- submoduleConfigs := gui.State.SubmoduleConfigs
+ submoduleConfigs := gui.State.Submodules
if file.IsSubmodule(submoduleConfigs) {
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index 157b75599..d70309a6a 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -8,7 +8,6 @@ import (
// "strings"
"fmt"
- "os"
"regexp"
"strings"
@@ -80,7 +79,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
return gui.refreshMainViews(refreshOpts)
}
-func (gui *Gui) refreshFiles() error {
+func (gui *Gui) refreshFilesAndSubmodules() error {
gui.State.RefreshingFilesMutex.Lock()
gui.State.IsRefreshingFiles = true
defer func() {
@@ -103,15 +102,25 @@ func (gui *Gui) refreshFiles() error {
}
gui.g.Update(func(g *gocui.Gui) error {
- if err := gui.Contexts.Files.Context.HandleRender(); err != nil {
- return err
+ if err := gui.postRefreshUpdate(gui.Contexts.Submodules.Context); err != nil {
+ gui.Log.Error(err)
}
- if g.CurrentView() == filesView || (g.CurrentView() == gui.getMainView() && g.CurrentView().Context == MAIN_MERGING_CONTEXT_KEY) {
+ if gui.getFilesView().Context == FILES_CONTEXT_KEY {
+ // doing this a little custom (as opposed to using gui.postRefreshUpdate) because we handle selecting the file explicitly below
+ if err := gui.Contexts.Files.Context.HandleRender(); err != nil {
+ return err
+ }
+ }
+
+ if gui.currentContext().GetKey() == FILES_CONTEXT_KEY || (g.CurrentView() == gui.getMainView() && g.CurrentView().Context == MAIN_MERGING_CONTEXT_KEY) {
newSelectedFile := gui.getSelectedFile()
alreadySelected := selectedFile != nil && newSelectedFile != nil && newSelectedFile.Name == selectedFile.Name
- return gui.selectFile(alreadySelected)
+ if err := gui.selectFile(alreadySelected); err != nil {
+ return err
+ }
}
+
return nil
})
@@ -161,15 +170,10 @@ func (gui *Gui) enterFile(forceSecondaryFocused bool, selectedLineIdx int) error
return nil
}
- submoduleConfigs := gui.State.SubmoduleConfigs
+ submoduleConfigs := gui.State.Submodules
if file.IsSubmodule(submoduleConfigs) {
- wd, err := os.Getwd()
- if err != nil {
- return err
- }
- gui.State.RepoPathStack = append(gui.State.RepoPathStack, wd)
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
- return gui.dispatchSwitchToRepo(submoduleConfig.Path)
+ return gui.enterSubmodule(submoduleConfig)
}
if file.HasInlineMergeConflicts {
@@ -325,7 +329,7 @@ func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
if err := gui.GitCommand.StageAll(); err != nil {
return gui.surfaceError(err)
}
- if err := gui.refreshFiles(); err != nil {
+ if err := gui.refreshFilesAndSubmodules(); err != nil {
return gui.surfaceError(err)
}
@@ -448,7 +452,7 @@ func (gui *Gui) refreshStateSubmoduleConfigs() error {
return err
}
- gui.State.SubmoduleConfigs = configs
+ gui.State.Submodules = configs
return nil
}
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 57d450f8a..06d79e12d 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -209,6 +209,10 @@ type commitFilesPanelState struct {
canRebase bool
}
+type submodulePanelState struct {
+ listPanelState
+}
+
type panelStates struct {
Files *filePanelState
Branches *branchPanelState
@@ -223,6 +227,7 @@ type panelStates struct {
LineByLine *lineByLinePanelState
Merging *mergingPanelState
CommitFiles *commitFilesPanelState
+ Submodules *submodulePanelState
}
type searchingState struct {
@@ -273,12 +278,12 @@ type Modes struct {
}
type guiState struct {
- Files []*models.File
- SubmoduleConfigs []*models.SubmoduleConfig
- Branches []*models.Branch
- Commits []*models.Commit
- StashEntries []*models.StashEntry
- CommitFiles []*models.CommitFile
+ Files []*models.File
+ Submodules []*models.SubmoduleConfig
+ Branches []*models.Branch
+ Commits []*models.Commit
+ StashEntries []*models.StashEntry
+ CommitFiles []*models.CommitFile
// FilteredReflogCommits are the ones that appear in the reflog panel.
// when in filtering mode we only include the ones that match the given path
FilteredReflogCommits []*models.Commit
@@ -358,6 +363,7 @@ func (gui *Gui) resetState() {
Panels: &panelStates{
// TODO: work out why some of these are -1 and some are 0. Last time I checked there was a good reason but I'm less certain now
Files: &filePanelState{listPanelState{SelectedLineIdx: -1}},
+ Submodules: &submodulePanelState{listPanelState{SelectedLineIdx: -1}},
Branches: &branchPanelState{listPanelState{SelectedLineIdx: 0}},
Remotes: &remotePanelState{listPanelState{SelectedLineIdx: 0}},
RemoteBranches: &remoteBranchesState{listPanelState{SelectedLineIdx: -1}},
@@ -456,7 +462,7 @@ func (gui *Gui) Run() error {
go gui.startBackgroundFetch()
}
- gui.goEvery(time.Second*10, gui.stopChan, gui.refreshFiles)
+ gui.goEvery(time.Second*10, gui.stopChan, gui.refreshFilesAndSubmodules)
g.SetManager(gocui.ManagerFunc(gui.layout), gocui.ManagerFunc(gui.getFocusLayout()))
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 5abe3d989..b08f3df92 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -363,102 +363,119 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.commitChanges"),
Handler: gui.wrappedHandler(gui.handleCommitPress),
Description: gui.Tr.SLocalize("CommitChanges"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.commitChangesWithoutHook"),
Handler: gui.handleWIPCommitPress,
Description: gui.Tr.SLocalize("commitChangesWithoutHook"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.amendLastCommit"),
Handler: gui.wrappedHandler(gui.handleAmendCommitPress),
Description: gui.Tr.SLocalize("AmendLastCommit"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.commitChangesWithEditor"),
Handler: gui.wrappedHandler(gui.handleCommitEditorPress),
Description: gui.Tr.SLocalize("CommitChangesWithEditor"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.select"),
Handler: gui.wrappedHandler(gui.handleFilePress),
Description: gui.Tr.SLocalize("toggleStaged"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.remove"),
Handler: gui.handleCreateDiscardMenu,
Description: gui.Tr.SLocalize("viewDiscardOptions"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.edit"),
Handler: gui.handleFileEdit,
Description: gui.Tr.SLocalize("editFile"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.openFile"),
Handler: gui.handleFileOpen,
Description: gui.Tr.SLocalize("openFile"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.ignoreFile"),
Handler: gui.handleIgnoreFile,
Description: gui.Tr.SLocalize("ignoreFile"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.refreshFiles"),
Handler: gui.handleRefreshFiles,
Description: gui.Tr.SLocalize("refreshFiles"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.stashAllChanges"),
Handler: gui.handleStashChanges,
Description: gui.Tr.SLocalize("stashAllChanges"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.viewStashOptions"),
Handler: gui.handleCreateStashMenu,
Description: gui.Tr.SLocalize("viewStashOptions"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.toggleStagedAll"),
Handler: gui.handleStageAll,
Description: gui.Tr.SLocalize("toggleStagedAll"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.viewResetOptions"),
Handler: gui.handleCreateResetMenu,
Description: gui.Tr.SLocalize("viewResetOptions"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.goInto"),
Handler: gui.handleEnterFile,
Description: gui.Tr.SLocalize("StageLines"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("files.fetch"),
Handler: gui.handleGitFetch,
Description: gui.Tr.SLocalize("fetch"),
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("universal.copyToClipboard"),
Handler: gui.wrappedHandler(gui.handleCopySelectedSideContextItemToClipboard),
Description: gui.Tr.SLocalize("copyFileNameToClipboard"),
@@ -471,6 +488,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
},
{
ViewName: "files",
+ Contexts: []string{FILES_CONTEXT_KEY},
Key: gui.getKey("commits.viewResetOptions"),
Handler: gui.handleCreateResetToUpstreamMenu,
Description: gui.Tr.SLocalize("viewResetToUpstreamOptions"),
@@ -1542,6 +1560,32 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Modifier: gocui.ModNone,
Handler: gui.wrappedHandler(gui.onMenuPress),
},
+ {
+ ViewName: "files",
+ Contexts: []string{SUBMODULES_CONTEXT_KEY},
+ Key: gui.getKey("universal.goInto"),
+ Handler: gui.wrappedHandler(gui.handleSubmoduleEnter),
+ Description: gui.Tr.SLocalize("enterSubmodule"),
+ },
+ {
+ ViewName: "files",
+ Key: gui.getKey("universal.nextTab"),
+ Handler: gui.handleNextTab,
+ Description: gui.Tr.SLocalize("nextTab"),
+ },
+ {
+ ViewName: "files",
+ Key: gui.getKey("universal.prevTab"),
+ Handler: gui.handlePrevTab,
+ Description: gui.Tr.SLocalize("prevTab"),
+ },
+ {
+ ViewName: "files",
+ Contexts: []string{SUBMODULES_CONTEXT_KEY},
+ Key: gui.getKey("universal.copyToClipboard"),
+ Handler: gui.wrappedHandler(gui.handleCopySelectedSideContextItemToClipboard),
+ Description: gui.Tr.SLocalize("copySubmoduleNameToClipboard"),
+ },
}
for _, viewName := range []string{"status", "branches", "files", "commits", "commitFiles", "stash", "menu"} {
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index 2aa01c6a7..e27a46457 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -285,8 +285,10 @@ func (gui *Gui) layout(g *gocui.Gui) error {
listContext *ListContext
}
+ // TODO: don't we already have the view included in the context object itself? Or might that change in a way we don't want reflected here?
listContextStates := []listContextState{
{view: filesView, listContext: gui.filesListContext()},
+ {view: filesView, listContext: gui.submodulesListContext()},
{view: branchesView, listContext: gui.branchesListContext()},
{view: branchesView, listContext: gui.remotesListContext()},
{view: branchesView, listContext: gui.remoteBranchesListContext()},
diff --git a/pkg/gui/list_context.go b/pkg/gui/list_context.go
index e2dec2a96..c360b2f2a 100644
--- a/pkg/gui/list_context.go
+++ b/pkg/gui/list_context.go
@@ -270,7 +270,7 @@ func (gui *Gui) filesListContext() *ListContext {
ResetMainViewOriginOnFocus: false,
Kind: SIDE_CONTEXT,
GetDisplayStrings: func() [][]string {
- return presentation.GetFileListDisplayStrings(gui.State.Files, gui.State.Modes.Diffing.Ref, gui.State.SubmoduleConfigs)
+ return presentation.GetFileListDisplayStrings(gui.State.Files, gui.State.Modes.Diffing.Ref, gui.State.Submodules)
},
SelectedItem: func() (ListItem, bool) {
item := gui.getSelectedFile()
@@ -462,6 +462,27 @@ func (gui *Gui) commitFilesListContext() *ListContext {
}
}
+func (gui *Gui) submodulesListContext() *ListContext {
+ return &ListContext{
+ ViewName: "files",
+ WindowName: "files",
+ ContextKey: SUBMODULES_CONTEXT_KEY,
+ GetItemsLength: func() int { return len(gui.State.Submodules) },
+ GetPanelState: func() IListPanelState { return gui.State.Panels.Submodules },
+ OnFocus: gui.handleSubmoduleSelect,
+ Gui: gui,
+ ResetMainViewOriginOnFocus: true,
+ Kind: SIDE_CONTEXT,
+ GetDisplayStrings: func() [][]string {
+ return presentation.GetSubmoduleListDisplayStrings(gui.State.Submodules)
+ },
+ SelectedItem: func() (ListItem, bool) {
+ item := gui.getSelectedSubmodule()
+ return item, item != nil
+ },
+ }
+}
+
func (gui *Gui) getListContexts() []*ListContext {
return []*ListContext{
gui.menuListContext(),
@@ -475,6 +496,7 @@ func (gui *Gui) getListContexts() []*ListContext {
gui.subCommitsListContext(),
gui.stashListContext(),
gui.commitFilesListContext(),
+ gui.submodulesListContext(),
}
}
diff --git a/pkg/gui/presentation/submodules.go b/pkg/gui/presentation/submodules.go
new file mode 100644
index 000000000..6b42d76f0
--- /dev/null
+++ b/pkg/gui/presentation/submodules.go
@@ -0,0 +1,21 @@
+package presentation
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+ "github.com/jesseduffield/lazygit/pkg/theme"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]string {
+ lines := make([][]string, len(submodules))
+
+ for i := range submodules {
+ lines[i] = getSubmoduleDisplayStrings(submodules[i])
+ }
+
+ return lines
+}
+
+func getSubmoduleDisplayStrings(s *models.SubmoduleConfig) []string {
+ return []string{utils.ColoredString(s.Name, theme.DefaultTextColor)}
+}
diff --git a/pkg/gui/submodules_panel.go b/pkg/gui/submodules_panel.go
new file mode 100644
index 000000000..e956a30f4
--- /dev/null
+++ b/pkg/gui/submodules_panel.go
@@ -0,0 +1,135 @@
+package gui
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/fatih/color"
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+ "github.com/jesseduffield/lazygit/pkg/utils"
+)
+
+func (gui *Gui) getSelectedSubmodule() *models.SubmoduleConfig {
+ selectedLine := gui.State.Panels.Submodules.SelectedLineIdx
+ if selectedLine == -1 || len(gui.State.Submodules) == 0 {
+ return nil
+ }
+
+ return gui.State.Submodules[selectedLine]
+}
+
+func (gui *Gui) handleSubmoduleSelect() error {
+ var task updateTask
+ submodule := gui.getSelectedSubmodule()
+ if submodule == nil {
+ task = gui.createRenderStringTask("No submodules")
+ } else {
+ // TODO: we want to display the path, name, url, and a diff. We really need to be able to pipe commands together. We can always pipe commands together and just not do it asynchronously, but what if it's an expensive diff to obtain? I think that makes the most sense now though.
+ task = gui.createRenderStringTask(
+ fmt.Sprintf(
+ "Name: %s\nPath: %s\nUrl: %s\n",
+ utils.ColoredString(submodule.Name, color.FgGreen),
+ utils.ColoredString(submodule.Path, color.FgYellow),
+ utils.ColoredString(submodule.Url, color.FgCyan),
+ ),
+ )
+ }
+
+ return gui.refreshMainViews(refreshMainOpts{
+ main: &viewUpdateOpts{
+ title: "Submodule",
+ task: task,
+ },
+ })
+}
+
+func (gui *Gui) handleSubmoduleEnter() error {
+ submodule := gui.getSelectedSubmodule()
+ if submodule == nil {
+ return nil
+ }
+
+ return gui.enterSubmodule(submodule)
+}
+
+func (gui *Gui) enterSubmodule(submodule *models.SubmoduleConfig) error {
+ wd, err := os.Getwd()
+ if err != nil {
+ return err
+ }
+ gui.State.RepoPathStack = append(gui.State.RepoPathStack, wd)
+
+ return gui.dispatchSwitchToRepo(submodule.Path)
+}
+
+// func (gui *Gui) handleAddRemote(g *gocui.Gui, v *gocui.View) error {
+// return gui.prompt(gui.Tr.SLocalize("newRemoteName"), "", func(remoteName string) error {
+// return gui.prompt(gui.Tr.SLocalize("newRemoteUrl"), "", func(remoteUrl string) error {
+// if err := gui.GitCommand.AddRemote(remoteName, remoteUrl); err != nil {
+// return err
+// }
+// return gui.refreshSidePanels(refreshOptions{scope: []int{REMOTES}})
+// })
+// })
+// }
+
+// func (gui *Gui) handleRemoveRemote(g *gocui.Gui, v *gocui.View) error {
+// remote := gui.getSelectedSubmodule()
+// if remote == nil {
+// return nil
+// }
+
+// return gui.ask(askOpts{
+// title: gui.Tr.SLocalize("removeRemote"),
+// prompt: gui.Tr.SLocalize("removeRemotePrompt") + " '" + remote.Name + "'?",
+// handleConfirm: func() error {
+// if err := gui.GitCommand.RemoveRemote(remote.Name); err != nil {
+// return err
+// }
+
+// return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
+// },
+// })
+// }
+
+// func (gui *Gui) handleEditRemote(g *gocui.Gui, v *gocui.View) error {
+// remote := gui.getSelectedSubmodule()
+// if remote == nil {
+// return nil
+// }
+
+// editNameMessage := gui.Tr.TemplateLocalize(
+// "editRemoteName",
+// Teml{
+// "remoteName": remote.Name,
+// },
+// )
+
+// return gui.prompt(editNameMessage, remote.Name, func(updatedRemoteName string) error {
+// if updatedRemoteName != remote.Name {
+// if err := gui.GitCommand.RenameRemote(remote.Name, updatedRemoteName); err != nil {
+// return gui.surfaceError(err)
+// }
+// }
+
+// editUrlMessage := gui.Tr.TemplateLocalize(
+// "editRemoteUrl",
+// Teml{
+// "remoteName": updatedRemoteName,
+// },
+// )
+
+// urls := remote.Urls
+// url := ""
+// if len(urls) > 0 {
+// url = urls[0]
+// }
+
+// return gui.prompt(editUrlMessage, url, func(updatedRemoteUrl string) error {
+// if err := gui.GitCommand.UpdateRemoteUrl(updatedRemoteName, updatedRemoteUrl); err != nil {
+// return gui.surfaceError(err)
+// }
+// return gui.refreshSidePanels(refreshOptions{scope: []int{BRANCHES, REMOTES}})
+// })
+// })
+// }
diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go
index d7eab73ba..f2b5b6026 100644
--- a/pkg/gui/view_helpers.go
+++ b/pkg/gui/view_helpers.go
@@ -25,18 +25,20 @@ const (
TAGS
REMOTES
STATUS
+ SUBMODULES
)
func getScopeNames(scopes []int) []string {
scopeNameMap := map[int]string{
- COMMITS: "commits",
- BRANCHES: "branches",
- FILES: "files",
- STASH: "stash",
- REFLOG: "reflog",
- TAGS: "tags",
- REMOTES: "remotes",
- STATUS: "status",
+ COMMITS: "commits",
+ BRANCHES: "branches",
+ FILES: "files",
+ SUBMODULES: "submodules",
+ STASH: "stash",
+ REFLOG: "reflog",
+ TAGS: "tags",
+ REMOTES: "remotes",
+ STATUS: "status",
}
scopeNames := make([]string, len(scopes))
@@ -116,13 +118,13 @@ func (gui *Gui) refreshSidePanels(options refreshOptions) error {
}()
}
- if scopeMap[FILES] {
+ if scopeMap[FILES] || scopeMap[SUBMODULES] {
wg.Add(1)
func() {
if options.mode == ASYNC {
- go gui.refreshFiles()
+ go gui.refreshFilesAndSubmodules()
} else {
- gui.refreshFiles()
+ gui.refreshFilesAndSubmodules()
}
wg.Done()
}()
diff --git a/pkg/gui/workspace_reset_options_panel.go b/pkg/gui/workspace_reset_options_panel.go
index 93b33f56e..6695c6cb5 100644
--- a/pkg/gui/workspace_reset_options_panel.go
+++ b/pkg/gui/workspace_reset_options_panel.go
@@ -11,7 +11,7 @@ func (gui *Gui) handleCreateResetMenu(g *gocui.Gui, v *gocui.View) error {
red := color.New(color.FgRed)
nukeStr := "reset --hard HEAD && git clean -fd"
- if len(gui.State.SubmoduleConfigs) > 0 {
+ if len(gui.State.Submodules) > 0 {
nukeStr = fmt.Sprintf("%s (%s)", nukeStr, gui.Tr.SLocalize("andResetSubmodules"))
}
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index afce1cffd..45a13647f 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -1197,6 +1197,12 @@ func addEnglish(i18nObject *i18n.Bundle) error {
}, &i18n.Message{
ID: "andResetSubmodules",
Other: "and reset submodules",
+ }, &i18n.Message{
+ ID: "enterSubmodule",
+ Other: "enter submodule",
+ }, &i18n.Message{
+ ID: "copySubmoduleNameToClipboard",
+ Other: "copy submodule name to clipboard",
},
)
}