From c488e22abc9bd782e360bc16b309cc831bcdd528 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sun, 6 Apr 2025 22:08:51 -0700 Subject: [PATCH 01/18] Bump generics dependency --- go.mod | 2 +- go.sum | 4 ++-- vendor/github.com/jesseduffield/generics/set/set.go | 4 ++++ vendor/modules.txt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 1cc02b1d7..b520fc259 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/gookit/color v1.4.2 github.com/imdario/mergo v0.3.11 github.com/integrii/flaggy v1.4.0 - github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 + github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a diff --git a/go.sum b/go.sum index 3750f2e9f..81b13f6ab 100644 --- a/go.sum +++ b/go.sum @@ -182,8 +182,8 @@ github.com/invopop/jsonschema v0.10.0 h1:c1ktzNLBun3LyQQhyty5WE3lulbOdIIyOVlkmDL github.com/invopop/jsonschema v0.10.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8TIcC6Y4RI+1ZbJDOHfGJ570tPeYVCqo7/tws= -github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk= +github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 h1:meoUDZGF6jZAbhW5IBwj92mTqGmrOn+Cuu0jM7/aUcs= +github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac h1:vUNTiVEB9Bz16pTJ5kNgb/1HhnWdSA1P0GfFLUJeITI= diff --git a/vendor/github.com/jesseduffield/generics/set/set.go b/vendor/github.com/jesseduffield/generics/set/set.go index 3e1bb69a3..0f0f4beb1 100644 --- a/vendor/github.com/jesseduffield/generics/set/set.go +++ b/vendor/github.com/jesseduffield/generics/set/set.go @@ -39,6 +39,10 @@ func (s *Set[T]) Includes(value T) bool { return s.hashMap[value] } +func (s *Set[T]) Len() int { + return len(s.hashMap) +} + // output slice is not necessarily in the same order that items were added func (s *Set[T]) ToSlice() []T { return maps.Keys(s.hashMap) diff --git a/vendor/modules.txt b/vendor/modules.txt index c46999dd3..3e68ca437 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -120,7 +120,7 @@ github.com/integrii/flaggy # github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 ## explicit github.com/jbenet/go-context/io -# github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 +# github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 ## explicit; go 1.18 github.com/jesseduffield/generics/maps github.com/jesseduffield/generics/set From 106b7018ddef104169b5022b887dd844f6338ccc Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 4 Apr 2025 23:06:06 -0700 Subject: [PATCH 02/18] Cleanup: rename predicate params (test -> predicate) --- pkg/gui/filetree/node.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/gui/filetree/node.go b/pkg/gui/filetree/node.go index d46025a0a..ce3bac566 100644 --- a/pkg/gui/filetree/node.go +++ b/pkg/gui/filetree/node.go @@ -109,13 +109,13 @@ func (self *Node[T]) SortChildren() { self.Children = children } -func (self *Node[T]) Some(test func(*Node[T]) bool) bool { - if test(self) { +func (self *Node[T]) Some(predicate func(*Node[T]) bool) bool { + if predicate(self) { return true } for _, child := range self.Children { - if child.Some(test) { + if child.Some(predicate) { return true } } @@ -123,14 +123,14 @@ func (self *Node[T]) Some(test func(*Node[T]) bool) bool { return false } -func (self *Node[T]) SomeFile(test func(*T) bool) bool { +func (self *Node[T]) SomeFile(predicate func(*T) bool) bool { if self.IsFile() { - if test(self.File) { + if predicate(self.File) { return true } } else { for _, child := range self.Children { - if child.SomeFile(test) { + if child.SomeFile(predicate) { return true } } @@ -139,13 +139,13 @@ func (self *Node[T]) SomeFile(test func(*T) bool) bool { return false } -func (self *Node[T]) Every(test func(*Node[T]) bool) bool { - if !test(self) { +func (self *Node[T]) Every(predicate func(*Node[T]) bool) bool { + if !predicate(self) { return false } for _, child := range self.Children { - if !child.Every(test) { + if !child.Every(predicate) { return false } } @@ -153,14 +153,14 @@ func (self *Node[T]) Every(test func(*Node[T]) bool) bool { return true } -func (self *Node[T]) EveryFile(test func(*T) bool) bool { +func (self *Node[T]) EveryFile(predicate func(*T) bool) bool { if self.IsFile() { - if !test(self.File) { + if !predicate(self.File) { return false } } else { for _, child := range self.Children { - if !child.EveryFile(test) { + if !child.EveryFile(predicate) { return false } } @@ -279,23 +279,23 @@ func (self *Node[T]) compressAux() *Node[T] { return self } -func (self *Node[T]) GetPathsMatching(test func(*Node[T]) bool) []string { +func (self *Node[T]) GetPathsMatching(predicate func(*Node[T]) bool) []string { paths := []string{} - if test(self) { + if predicate(self) { paths = append(paths, self.GetPath()) } for _, child := range self.Children { - paths = append(paths, child.GetPathsMatching(test)...) + paths = append(paths, child.GetPathsMatching(predicate)...) } return paths } -func (self *Node[T]) GetFilePathsMatching(test func(*T) bool) []string { +func (self *Node[T]) GetFilePathsMatching(predicate func(*T) bool) []string { matchingFileNodes := lo.Filter(self.GetLeaves(), func(node *Node[T], _ int) bool { - return test(node.File) + return predicate(node.File) }) return lo.Map(matchingFileNodes, func(node *Node[T], _ int) string { From 1fa9ea7f0401b518a1b3661ede82bc903d203323 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sat, 22 Feb 2025 14:09:01 -0800 Subject: [PATCH 03/18] Fix discarding submodule changes in nested folders The current rules for discarding submodule changes is that no other changed item must be also selected. There are some bugs with the current implementation when submodules are in folders. For example, selecting and discarding a folder with only a nested submodule change will currently do nothing. The submodule changes should be discarded. The folder only contains submodule changes so it should be no different than pressing discard on the submodule entry itself. Also, I noticed range selecting both the folder and the submodule and then pressing discard would be incorrectly disallowed. --- pkg/gui/controllers/files_controller.go | 46 +++++++++++++++++++------ pkg/gui/filetree/node.go | 16 +++++++++ 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 5f4a3bae0..e37127181 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/jesseduffield/generics/set" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands/git_commands" "github.com/jesseduffield/lazygit/pkg/commands/models" @@ -1194,13 +1195,36 @@ func filterNodesHaveUnstagedChanges(nodes []*filetree.FileNode) []*filetree.File }) } +func findSubmoduleNode(nodes []*filetree.FileNode, submodules []*models.SubmoduleConfig) *models.File { + for _, node := range nodes { + submoduleNode := node.FindFirstFileBy(func(f *models.File) bool { + return f.IsSubmodule(submodules) + }) + if submoduleNode != nil { + return submoduleNode + } + } + return nil +} + func (self *FilesController) canRemove(selectedNodes []*filetree.FileNode) *types.DisabledReason { + // Return disabled if the selection contains multiple changed items and includes a submodule change. submodules := self.c.Model().Submodules - submoduleCount := lo.CountBy(selectedNodes, func(node *filetree.FileNode) bool { - return node.File != nil && node.File.IsSubmodule(submodules) - }) - if submoduleCount > 0 && len(selectedNodes) > 1 { - return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupportedForSubmodules} + hasFiles := false + uniqueSelectedSubmodules := set.New[*models.SubmoduleConfig]() + + for _, node := range selectedNodes { + _ = node.ForEachFile(func(f *models.File) error { + if submodule := f.SubmoduleConfig(submodules); submodule != nil { + uniqueSelectedSubmodules.Add(submodule) + } else { + hasFiles = true + } + return nil + }) + if uniqueSelectedSubmodules.Len() > 0 && (hasFiles || uniqueSelectedSubmodules.Len() > 1) { + return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupportedForSubmodules} + } } return nil @@ -1209,11 +1233,13 @@ func (self *FilesController) canRemove(selectedNodes []*filetree.FileNode) *type func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { submodules := self.c.Model().Submodules + selectedNodes = normalisedSelectedNodes(selectedNodes) + // If we have one submodule then we must only have one submodule or `canRemove` would have // returned an error - firstNode := selectedNodes[0] - if firstNode.File != nil && firstNode.File.IsSubmodule(submodules) { - submodule := firstNode.File.SubmoduleConfig(submodules) + submoduleNode := findSubmoduleNode(selectedNodes, submodules) + if submoduleNode != nil { + submodule := submoduleNode.SubmoduleConfig(submodules) menuItems := []*types.MenuItem{ { @@ -1224,11 +1250,9 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { }, } - return self.c.Menu(types.CreateMenuOptions{Title: firstNode.GetPath(), Items: menuItems}) + return self.c.Menu(types.CreateMenuOptions{Title: submoduleNode.GetPath(), Items: menuItems}) } - selectedNodes = normalisedSelectedNodes(selectedNodes) - discardAllChangesItem := types.MenuItem{ Label: self.c.Tr.DiscardAllChanges, OnPress: func() error { diff --git a/pkg/gui/filetree/node.go b/pkg/gui/filetree/node.go index ce3bac566..97d5232b5 100644 --- a/pkg/gui/filetree/node.go +++ b/pkg/gui/filetree/node.go @@ -169,6 +169,22 @@ func (self *Node[T]) EveryFile(predicate func(*T) bool) bool { return true } +func (self *Node[T]) FindFirstFileBy(predicate func(*T) bool) *T { + if self.IsFile() { + if predicate(self.File) { + return self.File + } + } else { + for _, child := range self.Children { + if file := child.FindFirstFileBy(predicate); file != nil { + return file + } + } + } + + return nil +} + func (self *Node[T]) Flatten(collapsedPaths *CollapsedPaths) []*Node[T] { result := []*Node[T]{self} From 2db0a024bc7e4220677a92194fcac114cf2a9a98 Mon Sep 17 00:00:00 2001 From: Brandon Date: Sun, 23 Feb 2025 16:33:12 -0800 Subject: [PATCH 04/18] Add integration test --- pkg/integration/components/shell.go | 4 +- pkg/integration/tests/submodule/remove.go | 2 +- .../tests/submodule/reset_folder.go | 126 ++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 4 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 pkg/integration/tests/submodule/reset_folder.go diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index cdafb5756..4a66fc9e3 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -360,8 +360,8 @@ func (self *Shell) CloneIntoRemote(name string) *Shell { } func (self *Shell) CloneIntoSubmodule(submoduleName string, submodulePath string) *Shell { - self.Clone("other_repo") - self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../other_repo", submodulePath}) + self.Clone(submoduleName) + self.RunCommand([]string{"git", "submodule", "add", "--name", submoduleName, "../" + submoduleName, submodulePath}) return self } diff --git a/pkg/integration/tests/submodule/remove.go b/pkg/integration/tests/submodule/remove.go index 9290726ff..ca9a90b33 100644 --- a/pkg/integration/tests/submodule/remove.go +++ b/pkg/integration/tests/submodule/remove.go @@ -44,7 +44,7 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{ t.Views().Main().Content( Contains("-[submodule \"my_submodule_name\"]"). Contains("- path = my_submodule_path"). - Contains("- url = ../other_repo"), + Contains("- url = ../my_submodule_name"), ) t.FileSystem().PathNotPresent(gitDirSubmodulePath) diff --git a/pkg/integration/tests/submodule/reset_folder.go b/pkg/integration/tests/submodule/reset_folder.go new file mode 100644 index 000000000..47d226b20 --- /dev/null +++ b/pkg/integration/tests/submodule/reset_folder.go @@ -0,0 +1,126 @@ +package submodule + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Reset submodule changes located in a nested folder.", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(cfg *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("first commit") + shell.CreateDir("dir") + shell.CloneIntoSubmodule("submodule1", "dir/submodule1") + shell.CloneIntoSubmodule("submodule2", "dir/submodule2") + shell.GitAddAll() + shell.Commit("add submodules") + + shell.CreateFile("dir/submodule1/file", "") + shell.CreateFile("dir/submodule2/file", "") + shell.CreateFile("dir/file", "") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files().Focus(). + Lines( + Equals("▼ dir").IsSelected(), + Equals(" ?? file"), + Equals(" M submodule1 (submodule)"), + Equals(" M submodule2 (submodule)"), + ). + // Verify we cannot reset the entire folder (has nested file and submodule changes). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + }). + // Verify we cannot reset submodule + file or submodule + submodule via range select. + SelectNextItem(). + Press(keys.Universal.ToggleRangeSelect). + SelectNextItem(). + Lines( + Equals("▼ dir"), + Equals(" ?? file").IsSelected(), + Equals(" M submodule1 (submodule)").IsSelected(), + Equals(" M submodule2 (submodule)"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + }). + Press(keys.Universal.ToggleRangeSelect). + Press(keys.Universal.ToggleRangeSelect). + SelectNextItem(). + Lines( + Equals("▼ dir"), + Equals(" ?? file"), + Equals(" M submodule1 (submodule)").IsSelected(), + Equals(" M submodule2 (submodule)").IsSelected(), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + }). + // Reset the file change. + Press(keys.Universal.ToggleRangeSelect). + NavigateToLine(Contains("file")). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Discard changes")). + Select(Contains("Discard all changes")). + Confirm() + }). + NavigateToLine(Contains("▼ dir")). + Lines( + Equals("▼ dir").IsSelected(), + Equals(" M submodule1 (submodule)"), + Equals(" M submodule2 (submodule)"), + ). + // Verify we still cannot reset the entire folder (has two submodule changes). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + }). + // Reset one of the submodule changes. + SelectNextItem(). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir/submodule1")). + Select(Contains("Stash uncommitted submodule changes and update")). + Confirm() + }). + NavigateToLine(Contains("▼ dir")). + Lines( + Equals("▼ dir").IsSelected(), + Equals(" M submodule2 (submodule)"), + ). + // Now we can reset the folder (equivalent to resetting just the nested submodule change). + // Range selecting both the folder and submodule change is allowed. + Press(keys.Universal.ToggleRangeSelect). + SelectNextItem(). + Lines( + Equals("▼ dir").IsSelected(), + Equals(" M submodule2 (submodule)").IsSelected(), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir/submodule2")). + Select(Contains("Stash uncommitted submodule changes and update")). + Cancel() + }). + // Or just selecting the folder itself. + NavigateToLine(Contains("▼ dir")). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("dir/submodule2")). + Select(Contains("Stash uncommitted submodule changes and update")). + Confirm() + }). + IsEmpty() + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 210ae2852..4752e9bde 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -345,6 +345,7 @@ var tests = []*components.IntegrationTest{ submodule.Remove, submodule.RemoveNested, submodule.Reset, + submodule.ResetFolder, sync.FetchPrune, sync.FetchWhenSortedByDate, sync.ForcePush, From f75c0af6e0173c4991b630736b969e1cf088f443 Mon Sep 17 00:00:00 2001 From: Brandon Date: Fri, 4 Apr 2025 23:08:25 -0700 Subject: [PATCH 05/18] Update disabled message --- pkg/gui/controllers/files_controller.go | 2 +- pkg/i18n/english.go | 4 ++-- pkg/integration/tests/submodule/reset.go | 4 ++-- pkg/integration/tests/submodule/reset_folder.go | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index e37127181..f8e6061b3 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -1223,7 +1223,7 @@ func (self *FilesController) canRemove(selectedNodes []*filetree.FileNode) *type return nil }) if uniqueSelectedSubmodules.Len() > 0 && (hasFiles || uniqueSelectedSubmodules.Len() > 1) { - return &types.DisabledReason{Text: self.c.Tr.RangeSelectNotSupportedForSubmodules} + return &types.DisabledReason{Text: self.c.Tr.MultiSelectNotSupportedForSubmodules} } } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 2ca489d7e..8e63b60c9 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -845,7 +845,7 @@ type TranslationSet struct { NoItemSelected string SelectedItemIsNotABranch string SelectedItemDoesNotHaveFiles string - RangeSelectNotSupportedForSubmodules string + MultiSelectNotSupportedForSubmodules string OldCherryPickKeyWarning string CommandDoesNotSupportOpeningInEditor string CustomCommands string @@ -1889,7 +1889,7 @@ func EnglishTranslationSet() *TranslationSet { NoItemSelected: "No item selected", SelectedItemIsNotABranch: "Selected item is not a branch", SelectedItemDoesNotHaveFiles: "Selected item does not have files to view", - RangeSelectNotSupportedForSubmodules: "Range select not supported for submodules", + MultiSelectNotSupportedForSubmodules: "Multiselection not supported for submodules", OldCherryPickKeyWarning: "The 'c' key is no longer the default key for copying commits to cherry pick. Please use `{{.copy}}` instead (and `{{.paste}}` to paste). The reason for this change is that the 'v' key for selecting a range of lines when staging is now also used for selecting a range of lines in any list view, meaning that we needed to find a new key for pasting commits, and if we're going to now use `{{.paste}}` for pasting commits, we may as well use `{{.copy}}` for copying them. If you want to configure the keybindings to get the old behaviour, set the following in your config:\n\nkeybinding:\n universal:\n toggleRangeSelect: \n commits:\n cherryPickCopy: 'c'\n pasteCommits: 'v'", CommandDoesNotSupportOpeningInEditor: "This command doesn't support switching to the editor", CustomCommands: "Custom commands", diff --git a/pkg/integration/tests/submodule/reset.go b/pkg/integration/tests/submodule/reset.go index 1396ade1b..282417238 100644 --- a/pkg/integration/tests/submodule/reset.go +++ b/pkg/integration/tests/submodule/reset.go @@ -75,7 +75,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{ Equals(" M my_submodule_path (submodule)"), Equals(" ?? other_file").IsSelected(), ). - // Verify we can't use range select on submodules + // Verify we can't reset a submodule and file change at the same time. Press(keys.Universal.ToggleRangeSelect). SelectPreviousItem(). Lines( @@ -85,7 +85,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{ ). Press(keys.Universal.Remove). Tap(func() { - t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules")) }). Press(keys.Universal.ToggleRangeSelect). Lines( diff --git a/pkg/integration/tests/submodule/reset_folder.go b/pkg/integration/tests/submodule/reset_folder.go index 47d226b20..682e840c3 100644 --- a/pkg/integration/tests/submodule/reset_folder.go +++ b/pkg/integration/tests/submodule/reset_folder.go @@ -33,7 +33,7 @@ var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{ // Verify we cannot reset the entire folder (has nested file and submodule changes). Press(keys.Universal.Remove). Tap(func() { - t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules")) }). // Verify we cannot reset submodule + file or submodule + submodule via range select. SelectNextItem(). @@ -47,7 +47,7 @@ var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{ ). Press(keys.Universal.Remove). Tap(func() { - t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules")) }). Press(keys.Universal.ToggleRangeSelect). Press(keys.Universal.ToggleRangeSelect). @@ -60,7 +60,7 @@ var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{ ). Press(keys.Universal.Remove). Tap(func() { - t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules")) }). // Reset the file change. Press(keys.Universal.ToggleRangeSelect). @@ -81,7 +81,7 @@ var ResetFolder = NewIntegrationTest(NewIntegrationTestArgs{ // Verify we still cannot reset the entire folder (has two submodule changes). Press(keys.Universal.Remove). Tap(func() { - t.ExpectToast(Contains("Disabled: Range select not supported for submodules")) + t.ExpectToast(Contains("Disabled: Multiselection not supported for submodules")) }). // Reset one of the submodule changes. SelectNextItem(). From 4e5e21f94637d07f7000cd29587816dba97ccb85 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 9 Mar 2025 18:03:44 +0100 Subject: [PATCH 06/18] Revert commits related to using an interactive shell for running shell commands This reverts commits f28b6f439d, dbd407c01d, 5fac40c129, and 5a3049485c. --- pkg/commands/git_cmd_obj_builder.go | 4 -- pkg/commands/oscommands/cmd_obj_builder.go | 9 ----- pkg/commands/oscommands/dummies.go | 13 +++---- pkg/commands/oscommands/os.go | 13 +++---- .../oscommands/os_default_platform.go | 37 +++---------------- pkg/commands/oscommands/os_windows.go | 9 ++--- pkg/gui/controllers/shell_command_action.go | 2 +- 7 files changed, 19 insertions(+), 68 deletions(-) diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go index 7cb4f1496..21f300bb3 100644 --- a/pkg/commands/git_cmd_obj_builder.go +++ b/pkg/commands/git_cmd_obj_builder.go @@ -38,10 +38,6 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj { return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar) } -func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj { - return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar) -} - func (self *gitCmdObjBuilder) Quote(str string) string { return self.innerBuilder.Quote(str) } diff --git a/pkg/commands/oscommands/cmd_obj_builder.go b/pkg/commands/oscommands/cmd_obj_builder.go index c96f2c5bf..c72f3c187 100644 --- a/pkg/commands/oscommands/cmd_obj_builder.go +++ b/pkg/commands/oscommands/cmd_obj_builder.go @@ -14,8 +14,6 @@ type ICmdObjBuilder interface { New(args []string) ICmdObj // NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'` NewShell(commandStr string) ICmdObj - // Like NewShell, but uses the user's shell rather than "bash", and passes -i to it - NewInteractiveShell(commandStr string) ICmdObj // Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects. Quote(str string) string } @@ -51,13 +49,6 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj { return self.New(cmdArgs) } -func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj { - quotedCommand := self.quotedCommandString(commandStr + self.platform.InteractiveShellExit) - cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand)) - - return self.New(cmdArgs) -} - func (self *CmdObjBuilder) quotedCommandString(commandStr string) string { // Windows does not seem to like quotes around the command if self.platform.OS == "windows" { diff --git a/pkg/commands/oscommands/dummies.go b/pkg/commands/oscommands/dummies.go index 1e0150238..3f72a6f06 100644 --- a/pkg/commands/oscommands/dummies.go +++ b/pkg/commands/oscommands/dummies.go @@ -51,14 +51,11 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder { } var dummyPlatform = &Platform{ - OS: "darwin", - Shell: "bash", - InteractiveShell: "bash", - ShellArg: "-c", - InteractiveShellArg: "-i", - InteractiveShellExit: "; exit $?", - OpenCommand: "open {{filename}}", - OpenLinkCommand: "open {{link}}", + OS: "darwin", + Shell: "bash", + ShellArg: "-c", + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", } func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand { diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index 8da5572dc..dc5ef222c 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -35,14 +35,11 @@ type OSCommand struct { // Platform stores the os state type Platform struct { - OS string - Shell string - InteractiveShell string - ShellArg string - InteractiveShellArg string - InteractiveShellExit string - OpenCommand string - OpenLinkCommand string + OS string + Shell string + ShellArg string + OpenCommand string + OpenLinkCommand string } // NewOSCommand os command runner diff --git a/pkg/commands/oscommands/os_default_platform.go b/pkg/commands/oscommands/os_default_platform.go index 065d7f297..fd4967d95 100644 --- a/pkg/commands/oscommands/os_default_platform.go +++ b/pkg/commands/oscommands/os_default_platform.go @@ -4,42 +4,15 @@ package oscommands import ( - "os" "runtime" - "strings" ) func GetPlatform() *Platform { - shell := getUserShell() - - interactiveShell := shell - interactiveShellArg := "-i" - interactiveShellExit := "; exit $?" - - if strings.HasSuffix(shell, "fish") { - interactiveShellExit = "; exit $status" - } else if !(strings.HasSuffix(shell, "bash") || strings.HasSuffix(shell, "zsh")) { - interactiveShell = "bash" - interactiveShellArg = "" - interactiveShellExit = "" - } - return &Platform{ - OS: runtime.GOOS, - Shell: "bash", - InteractiveShell: interactiveShell, - ShellArg: "-c", - InteractiveShellArg: interactiveShellArg, - InteractiveShellExit: interactiveShellExit, - OpenCommand: "open {{filename}}", - OpenLinkCommand: "open {{link}}", + OS: runtime.GOOS, + Shell: "bash", + ShellArg: "-c", + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", } } - -func getUserShell() string { - if shell := os.Getenv("SHELL"); shell != "" { - return shell - } - - return "bash" -} diff --git a/pkg/commands/oscommands/os_windows.go b/pkg/commands/oscommands/os_windows.go index a2088a407..783f50518 100644 --- a/pkg/commands/oscommands/os_windows.go +++ b/pkg/commands/oscommands/os_windows.go @@ -2,11 +2,8 @@ package oscommands func GetPlatform() *Platform { return &Platform{ - OS: "windows", - Shell: "cmd", - InteractiveShell: "cmd", - ShellArg: "/c", - InteractiveShellArg: "", - InteractiveShellExit: "", + OS: "windows", + Shell: "cmd", + ShellArg: "/c", } } diff --git a/pkg/gui/controllers/shell_command_action.go b/pkg/gui/controllers/shell_command_action.go index 01b6d1e36..547ab012d 100644 --- a/pkg/gui/controllers/shell_command_action.go +++ b/pkg/gui/controllers/shell_command_action.go @@ -31,7 +31,7 @@ func (self *ShellCommandAction) Call() error { self.c.LogAction(self.c.Tr.Actions.CustomCommand) return self.c.RunSubprocessAndRefresh( - self.c.OS().Cmd.NewInteractiveShell(command), + self.c.OS().Cmd.NewShell(command), ) }, HandleDeleteSuggestion: func(index int) error { From 41e9335ea8f6846b4452fb2aa2e747377e037209 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 9 Mar 2025 18:09:24 +0100 Subject: [PATCH 07/18] Reorder configs in OSConfig There is a section at the end with deprecated settings, and a comment saying "The following configs are all deprecated". The clipboard-related settings were accidentally added to that section; they are not deprecated, so move them up to before that section. --- pkg/config/user_config.go | 16 ++++++++-------- schema/config.json | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 3cb945a8d..2ef595a86 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -581,6 +581,14 @@ type OSConfig struct { // Command for opening a link. Should contain "{{link}}". OpenLink string `yaml:"openLink,omitempty"` + // CopyToClipboardCmd is the command for copying to clipboard. + // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard + CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"` + + // ReadFromClipboardCmd is the command for reading the clipboard. + // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard + ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"` + // -------- // The following configs are all deprecated and kept for backward @@ -603,14 +611,6 @@ type OSConfig struct { // OpenLinkCommand is the command for opening a link // Deprecated: use OpenLink instead. OpenLinkCommand string `yaml:"openLinkCommand,omitempty" jsonschema:"deprecated"` - - // CopyToClipboardCmd is the command for copying to clipboard. - // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard - CopyToClipboardCmd string `yaml:"copyToClipboardCmd,omitempty"` - - // ReadFromClipboardCmd is the command for reading the clipboard. - // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard - ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"` } type CustomCommandAfterHook struct { diff --git a/schema/config.json b/schema/config.json index a4b4d0f43..4ce777b11 100644 --- a/schema/config.json +++ b/schema/config.json @@ -1551,6 +1551,14 @@ "type": "string", "description": "Command for opening a link. Should contain \"{{link}}\"." }, + "copyToClipboardCmd": { + "type": "string", + "description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard" + }, + "readFromClipboardCmd": { + "type": "string", + "description": "ReadFromClipboardCmd is the command for reading the clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard" + }, "editCommand": { "type": "string", "description": "EditCommand is the command for editing a file.\nDeprecated: use Edit instead. Note that semantics are different:\nEditCommand is just the command itself, whereas Edit contains a\n\"{{filename}}\" variable." @@ -1566,14 +1574,6 @@ "openLinkCommand": { "type": "string", "description": "OpenLinkCommand is the command for opening a link\nDeprecated: use OpenLink instead." - }, - "copyToClipboardCmd": { - "type": "string", - "description": "CopyToClipboardCmd is the command for copying to clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard" - }, - "readFromClipboardCmd": { - "type": "string", - "description": "ReadFromClipboardCmd is the command for reading the clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard" } }, "additionalProperties": false, From dc48cf963a107904384d0b944224699d0bf515d2 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 9 Mar 2025 18:15:48 +0100 Subject: [PATCH 08/18] Add config os.shellFunctionsFile --- docs/Config.md | 19 +++++++++++++ pkg/commands/git_cmd_obj_builder.go | 4 +-- pkg/commands/oscommands/cmd_obj_builder.go | 9 ++++-- pkg/commands/oscommands/os.go | 21 +++++++------- .../oscommands/os_default_platform.go | 28 +++++++++++++++---- pkg/config/user_config.go | 4 +++ pkg/gui/controllers/helpers/files_helper.go | 4 +-- pkg/gui/controllers/shell_command_action.go | 2 +- .../custom_commands/handler_creator.go | 4 +-- schema/config.json | 4 +++ 10 files changed, 75 insertions(+), 24 deletions(-) diff --git a/docs/Config.md b/docs/Config.md index 0a8c9d1e7..b7fcadc7a 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -442,6 +442,10 @@ os: # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard readFromClipboardCmd: "" + # A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands. + # See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands + shellFunctionsFile: "" + # If true, don't display introductory popups upon opening Lazygit. disableStartupPopups: false @@ -740,6 +744,21 @@ The `editInTerminal` option is used to decide whether lazygit needs to suspend i Contributions of new editor presets are welcome; see the `getPreset` function in [`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go). +## Using aliases or functions in shell commands + +Lazygit has a command prompt (`:`) for quickly executing shell commands without having to quit lazygit or switch to a different terminal. Most people find it convenient to have their usual shell aliases or shell functions available at this prompt. To achieve this, put your alias definitions in a separate shell startup file (which you source from your normal startup file, i.e. from `.bashrc` or `.zshrc`), and then tell lazygit about this file like so: + +```yml +os: + shellFunctionsFile: ~/.my_aliases.sh +``` + +For many people it might work well enough to use their entire shell config file (`~/.bashrc` or `~/.zshrc`) as the `shellFunctionsFile`, but these config files typically do a lot more than defining aliases (e.g. initialize the completion system, start an ssh-agent, etc.) and this may unnecessarily delay execution of shell commands. + +When using zsh, aliases can't be used here, but functions can. It is easy to convert your existing aliases into functions, just change `alias l="ls -la"` to `l() ls -la`, for example. This way it will work as before both in the shell and in lazygit. + +Note that the shell aliases file is not only used when executing shell commands, but also for [custom commands](Custom_Command_Keybindings.md), and when opening a file in the editor. + ## Overriding default config file location To override the default config directory, use `CONFIG_DIR="$HOME/.config/lazygit"`. This directory contains the config file in addition to some other files lazygit uses to keep track of state across sessions. diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go index 21f300bb3..b2877a5a1 100644 --- a/pkg/commands/git_cmd_obj_builder.go +++ b/pkg/commands/git_cmd_obj_builder.go @@ -34,8 +34,8 @@ func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj { return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar) } -func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj { - return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar) +func (self *gitCmdObjBuilder) NewShell(cmdStr string, shellFunctionsFile string) oscommands.ICmdObj { + return self.innerBuilder.NewShell(cmdStr, shellFunctionsFile).AddEnvVars(defaultEnvVar) } func (self *gitCmdObjBuilder) Quote(str string) string { diff --git a/pkg/commands/oscommands/cmd_obj_builder.go b/pkg/commands/oscommands/cmd_obj_builder.go index c72f3c187..2ab5c1071 100644 --- a/pkg/commands/oscommands/cmd_obj_builder.go +++ b/pkg/commands/oscommands/cmd_obj_builder.go @@ -13,7 +13,9 @@ type ICmdObjBuilder interface { // NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. New(args []string) ICmdObj // NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'` - NewShell(commandStr string) ICmdObj + // shellFunctionsFile is an optional file path that will be sourced before executing the command. Callers should pass + // the value of UserConfig.OS.ShellFunctionsFile. + NewShell(commandStr string, shellFunctionsFile string) ICmdObj // Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects. Quote(str string) string } @@ -42,7 +44,10 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj { } } -func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj { +func (self *CmdObjBuilder) NewShell(commandStr string, shellFunctionsFile string) ICmdObj { + if len(shellFunctionsFile) > 0 { + commandStr = fmt.Sprintf("%ssource %s\n%s", self.platform.PrefixForShellFunctionsFile, shellFunctionsFile, commandStr) + } quotedCommand := self.quotedCommandString(commandStr) cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand)) diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go index dc5ef222c..108e9b2ca 100644 --- a/pkg/commands/oscommands/os.go +++ b/pkg/commands/oscommands/os.go @@ -35,11 +35,12 @@ type OSCommand struct { // Platform stores the os state type Platform struct { - OS string - Shell string - ShellArg string - OpenCommand string - OpenLinkCommand string + OS string + Shell string + ShellArg string + PrefixForShellFunctionsFile string + OpenCommand string + OpenLinkCommand string } // NewOSCommand os command runner @@ -90,7 +91,7 @@ func (c *OSCommand) OpenFile(filename string) error { "filename": c.Quote(filename), } command := utils.ResolvePlaceholderString(commandTemplate, templateValues) - return c.Cmd.NewShell(command).Run() + return c.Cmd.NewShell(command, c.UserConfig().OS.ShellFunctionsFile).Run() } func (c *OSCommand) OpenLink(link string) error { @@ -107,7 +108,7 @@ func (c *OSCommand) OpenLink(link string) error { } command := utils.ResolvePlaceholderString(commandTemplate, templateValues) - return c.Cmd.NewShell(command).Run() + return c.Cmd.NewShell(command, c.UserConfig().OS.ShellFunctionsFile).Run() } // Quote wraps a message in platform-specific quotation marks @@ -296,7 +297,7 @@ func (c *OSCommand) CopyToClipboard(str string) error { cmdStr := utils.ResolvePlaceholderString(c.UserConfig().OS.CopyToClipboardCmd, map[string]string{ "text": c.Cmd.Quote(str), }) - return c.Cmd.NewShell(cmdStr).Run() + return c.Cmd.NewShell(cmdStr, c.UserConfig().OS.ShellFunctionsFile).Run() } return clipboard.WriteAll(str) @@ -307,7 +308,7 @@ func (c *OSCommand) PasteFromClipboard() (string, error) { var err error if c.UserConfig().OS.CopyToClipboardCmd != "" { cmdStr := c.UserConfig().OS.ReadFromClipboardCmd - s, err = c.Cmd.NewShell(cmdStr).RunWithOutput() + s, err = c.Cmd.NewShell(cmdStr, c.UserConfig().OS.ShellFunctionsFile).RunWithOutput() } else { s, err = clipboard.ReadAll() } @@ -357,5 +358,5 @@ func (c *OSCommand) UpdateWindowTitle() error { return getWdErr } argString := fmt.Sprint("title ", filepath.Base(path), " - Lazygit") - return c.Cmd.NewShell(argString).Run() + return c.Cmd.NewShell(argString, c.UserConfig().OS.ShellFunctionsFile).Run() } diff --git a/pkg/commands/oscommands/os_default_platform.go b/pkg/commands/oscommands/os_default_platform.go index fd4967d95..11e1dc9aa 100644 --- a/pkg/commands/oscommands/os_default_platform.go +++ b/pkg/commands/oscommands/os_default_platform.go @@ -4,15 +4,33 @@ package oscommands import ( + "os" "runtime" + "strings" ) func GetPlatform() *Platform { + shell := getUserShell() + + prefixForShellFunctionsFile := "" + if strings.HasSuffix(shell, "bash") { + prefixForShellFunctionsFile = "shopt -s expand_aliases\n" + } + return &Platform{ - OS: runtime.GOOS, - Shell: "bash", - ShellArg: "-c", - OpenCommand: "open {{filename}}", - OpenLinkCommand: "open {{link}}", + OS: runtime.GOOS, + Shell: shell, + ShellArg: "-c", + PrefixForShellFunctionsFile: prefixForShellFunctionsFile, + OpenCommand: "open {{filename}}", + OpenLinkCommand: "open {{link}}", } } + +func getUserShell() string { + if shell := os.Getenv("SHELL"); shell != "" { + return shell + } + + return "bash" +} diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 2ef595a86..fd682a9cb 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -589,6 +589,10 @@ type OSConfig struct { // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard ReadFromClipboardCmd string `yaml:"readFromClipboardCmd,omitempty"` + // A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands. + // See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands + ShellFunctionsFile string `yaml:"shellFunctionsFile"` + // -------- // The following configs are all deprecated and kept for backward diff --git a/pkg/gui/controllers/helpers/files_helper.go b/pkg/gui/controllers/helpers/files_helper.go index 328d0f889..24789ec0c 100644 --- a/pkg/gui/controllers/helpers/files_helper.go +++ b/pkg/gui/controllers/helpers/files_helper.go @@ -71,11 +71,11 @@ func (self *FilesHelper) OpenDirInEditor(path string) error { func (self *FilesHelper) callEditor(cmdStr string, suspend bool) error { if suspend { return self.c.RunSubprocessAndRefresh( - self.c.OS().Cmd.NewShell(cmdStr), + self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile), ) } - return self.c.OS().Cmd.NewShell(cmdStr).Run() + return self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile).Run() } func (self *FilesHelper) OpenFile(filename string) error { diff --git a/pkg/gui/controllers/shell_command_action.go b/pkg/gui/controllers/shell_command_action.go index 547ab012d..185f74893 100644 --- a/pkg/gui/controllers/shell_command_action.go +++ b/pkg/gui/controllers/shell_command_action.go @@ -31,7 +31,7 @@ func (self *ShellCommandAction) Call() error { self.c.LogAction(self.c.Tr.Actions.CustomCommand) return self.c.RunSubprocessAndRefresh( - self.c.OS().Cmd.NewShell(command), + self.c.OS().Cmd.NewShell(command, self.c.UserConfig().OS.ShellFunctionsFile), ) }, HandleDeleteSuggestion: func(index int) error { diff --git a/pkg/gui/services/custom_commands/handler_creator.go b/pkg/gui/services/custom_commands/handler_creator.go index 9e17ca9fa..5f647606b 100644 --- a/pkg/gui/services/custom_commands/handler_creator.go +++ b/pkg/gui/services/custom_commands/handler_creator.go @@ -148,7 +148,7 @@ func (self *HandlerCreator) generateFindSuggestionsFunc(prompt *config.CustomCom func (self *HandlerCreator) getCommandSuggestionsFn(command string) (func(string) []*types.Suggestion, error) { lines := []*types.Suggestion{} - err := self.c.OS().Cmd.NewShell(command).RunAndProcessLines(func(line string) (bool, error) { + err := self.c.OS().Cmd.NewShell(command, self.c.UserConfig().OS.ShellFunctionsFile).RunAndProcessLines(func(line string) (bool, error) { lines = append(lines, &types.Suggestion{Value: line, Label: line}) return false, nil }) @@ -259,7 +259,7 @@ func (self *HandlerCreator) finalHandler(customCommand config.CustomCommand, ses return err } - cmdObj := self.c.OS().Cmd.NewShell(cmdStr) + cmdObj := self.c.OS().Cmd.NewShell(cmdStr, self.c.UserConfig().OS.ShellFunctionsFile) if customCommand.Subprocess != nil && *customCommand.Subprocess { return self.c.RunSubprocessAndRefresh(cmdObj) diff --git a/schema/config.json b/schema/config.json index 4ce777b11..7c642af96 100644 --- a/schema/config.json +++ b/schema/config.json @@ -1559,6 +1559,10 @@ "type": "string", "description": "ReadFromClipboardCmd is the command for reading the clipboard.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#custom-command-for-copying-to-and-pasting-from-clipboard" }, + "shellFunctionsFile": { + "type": "string", + "description": "A shell startup file containing shell aliases or shell functions. This will be sourced before running any shell commands, so that shell functions are available in the `:` command prompt or even in custom commands.\nSee https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands" + }, "editCommand": { "type": "string", "description": "EditCommand is the command for editing a file.\nDeprecated: use Edit instead. Note that semantics are different:\nEditCommand is just the command itself, whereas Edit contains a\n\"{{filename}}\" variable." From d13306ac6480588a52dd2c8b7d60cd432f2d5f05 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Apr 2025 14:12:34 +0200 Subject: [PATCH 09/18] Add a breaking changes entry for using shell aliases --- pkg/i18n/english.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 8e63b60c9..0478b114a 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -2082,6 +2082,7 @@ gui: `, "0.44.0": `- The gui.branchColors config option is deprecated; it will be removed in a future version. Please use gui.branchColorPatterns instead. - The automatic coloring of branches starting with "feature/", "bugfix/", or "hotfix/" has been removed; if you want this, it's easy to set up using the new gui.branchColorPatterns option.`, + "0.49.0": `- Executing shell commands (with the ':' prompt) no longer uses an interactive shell, which means that if you want to use your shell aliases in this prompt, you need to do a little bit of setup work. See https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#using-aliases-or-functions-in-shell-commands for details.`, }, } } From 8d47928c94789f2cdc93eca964c321bc8519924c Mon Sep 17 00:00:00 2001 From: Chris McDonnell Date: Mon, 24 Mar 2025 20:09:12 -0400 Subject: [PATCH 10/18] Update kidpix link in README to active url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d26a47d27..33606a3b2 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Press `b` in the commits view to mark a commit as good/bad in order to begin a g ### Nuke the working tree -For when you really want to just get rid of anything that shows up when you run `git status` (and yes that includes dirty submodules) [kidpix style](https://www.youtube.com/watch?v=Ur7_A4JusMU), press `shift+d` to bring up the reset options menu and then select the 'nuke' option. +For when you really want to just get rid of anything that shows up when you run `git status` (and yes that includes dirty submodules) [kidpix style](https://www.youtube.com/watch?v=N4E2B_k2Bss), press `shift+d` to bring up the reset options menu and then select the 'nuke' option. ![Nuke working tree](../assets/demo/nuke_working_tree-compressed.gif) From 7322cf1a9e078f6bf0e11fec66e5e4e70133ccca Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 8 Apr 2025 16:04:02 +0200 Subject: [PATCH 11/18] Bump gocui This includes a fix to render hyperlinks in the last line of a view if it doesn't end with a line feed; see https://github.com/jesseduffield/gocui/pull/77. --- go.mod | 10 +- go.sum | 20 +-- vendor/github.com/jesseduffield/gocui/view.go | 2 + vendor/golang.org/x/sync/errgroup/errgroup.go | 5 +- vendor/golang.org/x/sync/errgroup/go120.go | 13 -- .../golang.org/x/sync/errgroup/pre_go120.go | 14 -- vendor/golang.org/x/sys/cpu/cpu.go | 12 ++ .../golang.org/x/sys/cpu/cpu_linux_loong64.go | 22 +++ .../golang.org/x/sys/cpu/cpu_linux_noinit.go | 2 +- vendor/golang.org/x/sys/cpu/cpu_loong64.go | 38 +++++ vendor/golang.org/x/sys/cpu/cpu_loong64.s | 13 ++ vendor/golang.org/x/sys/cpu/parse.go | 4 +- .../golang.org/x/sys/unix/syscall_darwin.go | 149 +++++++++++++++++- vendor/golang.org/x/sys/unix/syscall_linux.go | 42 ++--- .../x/sys/unix/zsyscall_darwin_amd64.go | 84 ++++++++++ .../x/sys/unix/zsyscall_darwin_amd64.s | 20 +++ .../x/sys/unix/zsyscall_darwin_arm64.go | 84 ++++++++++ .../x/sys/unix/zsyscall_darwin_arm64.s | 20 +++ .../golang.org/x/sys/windows/types_windows.go | 27 ++++ vendor/golang.org/x/term/terminal.go | 9 ++ vendor/modules.txt | 18 +-- 21 files changed, 525 insertions(+), 83 deletions(-) delete mode 100644 vendor/golang.org/x/sync/errgroup/go120.go delete mode 100644 vendor/golang.org/x/sync/errgroup/pre_go120.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_loong64.s diff --git a/go.mod b/go.mod index b520fc259..b95e5092f 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/integrii/flaggy v1.4.0 github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d - github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac + github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647 github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e @@ -37,7 +37,7 @@ require ( github.com/stretchr/testify v1.8.1 github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 - golang.org/x/sync v0.11.0 + golang.org/x/sync v0.13.0 gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -73,8 +73,8 @@ require ( github.com/xanzy/ssh-agent v0.2.1 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 81b13f6ab..aea780732 100644 --- a/go.sum +++ b/go.sum @@ -186,8 +186,8 @@ github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918 h1:meoUDZGF github.com/jesseduffield/generics v0.0.0-20250406224309-4f541cb84918/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= -github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac h1:vUNTiVEB9Bz16pTJ5kNgb/1HhnWdSA1P0GfFLUJeITI= -github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s= +github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647 h1:XpoXCyLGCB4mczPc1q/8dJefr6OB1xa7jyhH5a5eJqY= +github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647/go.mod h1:sLIyZ2J42R6idGdtemZzsiR3xY5EF0KsvYEGh3dQv3s= github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a h1:UDeJ3EBk04bXDLOPvuqM3on8HvyJfISw0+UMqW+0a4g= github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a/go.mod h1:FSWDLKT0NQpntbDd1H3lbz51fhCVlMzy/J0S6nM727Q= github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY= @@ -436,8 +436,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20170407050850-f3918c30c5c2/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -491,8 +491,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -502,8 +502,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -518,8 +518,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 5c40dbe77..2662ae6ac 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -822,6 +822,8 @@ func (v *View) writeRunes(p []rune) { if v.pendingNewline { finishLine() + } else { + v.autoRenderHyperlinksInCurrentLine() } v.updateSearchPositions() diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go index b8322598a..f8c3c0926 100644 --- a/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -18,7 +18,7 @@ import ( type token struct{} // A Group is a collection of goroutines working on subtasks that are part of -// the same overall task. +// the same overall task. A Group should not be reused for different tasks. // // A zero Group is valid, has no limit on the number of active goroutines, // and does not cancel on error. @@ -46,7 +46,7 @@ func (g *Group) done() { // returns a non-nil error or the first time Wait returns, whichever occurs // first. func WithContext(ctx context.Context) (*Group, context.Context) { - ctx, cancel := withCancelCause(ctx) + ctx, cancel := context.WithCancelCause(ctx) return &Group{cancel: cancel}, ctx } @@ -61,6 +61,7 @@ func (g *Group) Wait() error { } // Go calls the given function in a new goroutine. +// The first call to Go must happen before a Wait. // It blocks until the new goroutine can be added without the number of // active goroutines in the group exceeding the configured limit. // diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go deleted file mode 100644 index f93c740b6..000000000 --- a/vendor/golang.org/x/sync/errgroup/go120.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build go1.20 - -package errgroup - -import "context" - -func withCancelCause(parent context.Context) (context.Context, func(error)) { - return context.WithCancelCause(parent) -} diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go deleted file mode 100644 index 88ce33434..000000000 --- a/vendor/golang.org/x/sync/errgroup/pre_go120.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !go1.20 - -package errgroup - -import "context" - -func withCancelCause(parent context.Context) (context.Context, func(error)) { - ctx, cancel := context.WithCancel(parent) - return ctx, func(error) { cancel() } -} diff --git a/vendor/golang.org/x/sys/cpu/cpu.go b/vendor/golang.org/x/sys/cpu/cpu.go index 9c105f23a..2e73ee197 100644 --- a/vendor/golang.org/x/sys/cpu/cpu.go +++ b/vendor/golang.org/x/sys/cpu/cpu.go @@ -149,6 +149,18 @@ var ARM struct { _ CacheLinePad } +// The booleans in Loong64 contain the correspondingly named cpu feature bit. +// The struct is padded to avoid false sharing. +var Loong64 struct { + _ CacheLinePad + HasLSX bool // support 128-bit vector extension + HasLASX bool // support 256-bit vector extension + HasCRC32 bool // support CRC instruction + HasLAM_BH bool // support AM{SWAP/ADD}[_DB].{B/H} instruction + HasLAMCAS bool // support AMCAS[_DB].{B/H/W/D} instruction + _ CacheLinePad +} + // MIPS64X contains the supported CPU features of the current mips64/mips64le // platforms. If the current platform is not mips64/mips64le or the current // operating system is not Linux then all feature flags are false. diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go new file mode 100644 index 000000000..4f3411432 --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_loong64.go @@ -0,0 +1,22 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cpu + +// HWCAP bits. These are exposed by the Linux kernel. +const ( + hwcap_LOONGARCH_LSX = 1 << 4 + hwcap_LOONGARCH_LASX = 1 << 5 +) + +func doinit() { + // TODO: Features that require kernel support like LSX and LASX can + // be detected here once needed in std library or by the compiler. + Loong64.HasLSX = hwcIsSet(hwCap, hwcap_LOONGARCH_LSX) + Loong64.HasLASX = hwcIsSet(hwCap, hwcap_LOONGARCH_LASX) +} + +func hwcIsSet(hwc uint, val uint) bool { + return hwc&val != 0 +} diff --git a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go index 7d902b684..a428dec9c 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go +++ b/vendor/golang.org/x/sys/cpu/cpu_linux_noinit.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux && !arm && !arm64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 +//go:build linux && !arm && !arm64 && !loong64 && !mips64 && !mips64le && !ppc64 && !ppc64le && !s390x && !riscv64 package cpu diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.go b/vendor/golang.org/x/sys/cpu/cpu_loong64.go index 558635850..45ecb29ae 100644 --- a/vendor/golang.org/x/sys/cpu/cpu_loong64.go +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.go @@ -8,5 +8,43 @@ package cpu const cacheLineSize = 64 +// Bit fields for CPUCFG registers, Related reference documents: +// https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_cpucfg +const ( + // CPUCFG1 bits + cpucfg1_CRC32 = 1 << 25 + + // CPUCFG2 bits + cpucfg2_LAM_BH = 1 << 27 + cpucfg2_LAMCAS = 1 << 28 +) + func initOptions() { + options = []option{ + {Name: "lsx", Feature: &Loong64.HasLSX}, + {Name: "lasx", Feature: &Loong64.HasLASX}, + {Name: "crc32", Feature: &Loong64.HasCRC32}, + {Name: "lam_bh", Feature: &Loong64.HasLAM_BH}, + {Name: "lamcas", Feature: &Loong64.HasLAMCAS}, + } + + // The CPUCFG data on Loong64 only reflects the hardware capabilities, + // not the kernel support status, so features such as LSX and LASX that + // require kernel support cannot be obtained from the CPUCFG data. + // + // These features only require hardware capability support and do not + // require kernel specific support, so they can be obtained directly + // through CPUCFG + cfg1 := get_cpucfg(1) + cfg2 := get_cpucfg(2) + + Loong64.HasCRC32 = cfgIsSet(cfg1, cpucfg1_CRC32) + Loong64.HasLAMCAS = cfgIsSet(cfg2, cpucfg2_LAMCAS) + Loong64.HasLAM_BH = cfgIsSet(cfg2, cpucfg2_LAM_BH) +} + +func get_cpucfg(reg uint32) uint32 + +func cfgIsSet(cfg uint32, val uint32) bool { + return cfg&val != 0 } diff --git a/vendor/golang.org/x/sys/cpu/cpu_loong64.s b/vendor/golang.org/x/sys/cpu/cpu_loong64.s new file mode 100644 index 000000000..71cbaf1ce --- /dev/null +++ b/vendor/golang.org/x/sys/cpu/cpu_loong64.s @@ -0,0 +1,13 @@ +// Copyright 2025 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// func get_cpucfg(reg uint32) uint32 +TEXT ·get_cpucfg(SB), NOSPLIT|NOFRAME, $0 + MOVW reg+0(FP), R5 + // CPUCFG R5, R4 = 0x00006ca4 + WORD $0x00006ca4 + MOVW R4, ret+8(FP) + RET diff --git a/vendor/golang.org/x/sys/cpu/parse.go b/vendor/golang.org/x/sys/cpu/parse.go index 762b63d68..56a7e1a17 100644 --- a/vendor/golang.org/x/sys/cpu/parse.go +++ b/vendor/golang.org/x/sys/cpu/parse.go @@ -13,7 +13,7 @@ import "strconv" // https://golang.org/cl/209597. func parseRelease(rel string) (major, minor, patch int, ok bool) { // Strip anything after a dash or plus. - for i := 0; i < len(rel); i++ { + for i := range len(rel) { if rel[i] == '-' || rel[i] == '+' { rel = rel[:i] break @@ -21,7 +21,7 @@ func parseRelease(rel string) (major, minor, patch int, ok bool) { } next := func() (int, bool) { - for i := 0; i < len(rel); i++ { + for i := range len(rel) { if rel[i] == '.' { ver, err := strconv.Atoi(rel[:i]) rel = rel[i+1:] diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin.go b/vendor/golang.org/x/sys/unix/syscall_darwin.go index 099867dee..798f61ad3 100644 --- a/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -602,7 +602,150 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI return } -//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) +// sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) +const minIovec = 8 + +func Readv(fd int, iovs [][]byte) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + n, err = readv(fd, iovecs) + readvRacedetect(iovecs, n, err) + return n, err +} + +func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + n, err = preadv(fd, iovecs, offset) + readvRacedetect(iovecs, n, err) + return n, err +} + +func Writev(fd int, iovs [][]byte) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = writev(fd, iovecs) + writevRacedetect(iovecs, n) + return n, err +} + +func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) { + if !darwinKernelVersionMin(11, 0, 0) { + return 0, ENOSYS + } + + iovecs := make([]Iovec, 0, minIovec) + iovecs = appendBytes(iovecs, iovs) + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + n, err = pwritev(fd, iovecs, offset) + writevRacedetect(iovecs, n) + return n, err +} + +func appendBytes(vecs []Iovec, bs [][]byte) []Iovec { + for _, b := range bs { + var v Iovec + v.SetLen(len(b)) + if len(b) > 0 { + v.Base = &b[0] + } else { + v.Base = (*byte)(unsafe.Pointer(&_zero)) + } + vecs = append(vecs, v) + } + return vecs +} + +func writevRacedetect(iovecs []Iovec, n int) { + if !raceenabled { + return + } + for i := 0; n > 0 && i < len(iovecs); i++ { + m := int(iovecs[i].Len) + if m > n { + m = n + } + n -= m + if m > 0 { + raceReadRange(unsafe.Pointer(iovecs[i].Base), m) + } + } +} + +func readvRacedetect(iovecs []Iovec, n int, err error) { + if !raceenabled { + return + } + for i := 0; n > 0 && i < len(iovecs); i++ { + m := int(iovecs[i].Len) + if m > n { + m = n + } + n -= m + if m > 0 { + raceWriteRange(unsafe.Pointer(iovecs[i].Base), m) + } + } + if err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } +} + +func darwinMajorMinPatch() (maj, min, patch int, err error) { + var un Utsname + err = Uname(&un) + if err != nil { + return + } + + var mmp [3]int + c := 0 +Loop: + for _, b := range un.Release[:] { + switch { + case b >= '0' && b <= '9': + mmp[c] = 10*mmp[c] + int(b-'0') + case b == '.': + c++ + if c > 2 { + return 0, 0, 0, ENOTSUP + } + case b == 0: + break Loop + default: + return 0, 0, 0, ENOTSUP + } + } + if c != 2 { + return 0, 0, 0, ENOTSUP + } + return mmp[0], mmp[1], mmp[2], nil +} + +func darwinKernelVersionMin(maj, min, patch int) bool { + actualMaj, actualMin, actualPatch, err := darwinMajorMinPatch() + if err != nil { + return false + } + return actualMaj > maj || actualMaj == maj && (actualMin > min || actualMin == min && actualPatch >= patch) +} + //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) @@ -705,3 +848,7 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI //sys write(fd int, p []byte) (n int, err error) //sys mmap(addr uintptr, length uintptr, prot int, flag int, fd int, pos int64) (ret uintptr, err error) //sys munmap(addr uintptr, length uintptr) (err error) +//sys readv(fd int, iovecs []Iovec) (n int, err error) +//sys preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) +//sys writev(fd int, iovecs []Iovec) (n int, err error) +//sys pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 230a94549..4958a6570 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -13,6 +13,7 @@ package unix import ( "encoding/binary" + "slices" "strconv" "syscall" "time" @@ -417,7 +418,7 @@ func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) { return nil, 0, EINVAL } sa.raw.Family = AF_UNIX - for i := 0; i < n; i++ { + for i := range n { sa.raw.Path[i] = int8(name[i]) } // length is family (uint16), name, NUL. @@ -507,7 +508,7 @@ func (sa *SockaddrL2) sockaddr() (unsafe.Pointer, _Socklen, error) { psm := (*[2]byte)(unsafe.Pointer(&sa.raw.Psm)) psm[0] = byte(sa.PSM) psm[1] = byte(sa.PSM >> 8) - for i := 0; i < len(sa.Addr); i++ { + for i := range len(sa.Addr) { sa.raw.Bdaddr[i] = sa.Addr[len(sa.Addr)-1-i] } cid := (*[2]byte)(unsafe.Pointer(&sa.raw.Cid)) @@ -589,11 +590,11 @@ func (sa *SockaddrCAN) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Family = AF_CAN sa.raw.Ifindex = int32(sa.Ifindex) rx := (*[4]byte)(unsafe.Pointer(&sa.RxID)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i] = rx[i] } tx := (*[4]byte)(unsafe.Pointer(&sa.TxID)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i+4] = tx[i] } return unsafe.Pointer(&sa.raw), SizeofSockaddrCAN, nil @@ -618,11 +619,11 @@ func (sa *SockaddrCANJ1939) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Family = AF_CAN sa.raw.Ifindex = int32(sa.Ifindex) n := (*[8]byte)(unsafe.Pointer(&sa.Name)) - for i := 0; i < 8; i++ { + for i := range 8 { sa.raw.Addr[i] = n[i] } p := (*[4]byte)(unsafe.Pointer(&sa.PGN)) - for i := 0; i < 4; i++ { + for i := range 4 { sa.raw.Addr[i+8] = p[i] } sa.raw.Addr[12] = sa.Addr @@ -911,7 +912,7 @@ func (sa *SockaddrIUCV) sockaddr() (unsafe.Pointer, _Socklen, error) { // These are EBCDIC encoded by the kernel, but we still need to pad them // with blanks. Initializing with blanks allows the caller to feed in either // a padded or an unpadded string. - for i := 0; i < 8; i++ { + for i := range 8 { sa.raw.Nodeid[i] = ' ' sa.raw.User_id[i] = ' ' sa.raw.Name[i] = ' ' @@ -1148,7 +1149,7 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { var user [8]byte var name [8]byte - for i := 0; i < 8; i++ { + for i := range 8 { user[i] = byte(pp.User_id[i]) name[i] = byte(pp.Name[i]) } @@ -1173,11 +1174,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { Ifindex: int(pp.Ifindex), } name := (*[8]byte)(unsafe.Pointer(&sa.Name)) - for i := 0; i < 8; i++ { + for i := range 8 { name[i] = pp.Addr[i] } pgn := (*[4]byte)(unsafe.Pointer(&sa.PGN)) - for i := 0; i < 4; i++ { + for i := range 4 { pgn[i] = pp.Addr[i+8] } addr := (*[1]byte)(unsafe.Pointer(&sa.Addr)) @@ -1188,11 +1189,11 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { Ifindex: int(pp.Ifindex), } rx := (*[4]byte)(unsafe.Pointer(&sa.RxID)) - for i := 0; i < 4; i++ { + for i := range 4 { rx[i] = pp.Addr[i] } tx := (*[4]byte)(unsafe.Pointer(&sa.TxID)) - for i := 0; i < 4; i++ { + for i := range 4 { tx[i] = pp.Addr[i+4] } return sa, nil @@ -2216,10 +2217,7 @@ func readvRacedetect(iovecs []Iovec, n int, err error) { return } for i := 0; n > 0 && i < len(iovecs); i++ { - m := int(iovecs[i].Len) - if m > n { - m = n - } + m := min(int(iovecs[i].Len), n) n -= m if m > 0 { raceWriteRange(unsafe.Pointer(iovecs[i].Base), m) @@ -2270,10 +2268,7 @@ func writevRacedetect(iovecs []Iovec, n int) { return } for i := 0; n > 0 && i < len(iovecs); i++ { - m := int(iovecs[i].Len) - if m > n { - m = n - } + m := min(int(iovecs[i].Len), n) n -= m if m > 0 { raceReadRange(unsafe.Pointer(iovecs[i].Base), m) @@ -2320,12 +2315,7 @@ func isGroupMember(gid int) bool { return false } - for _, g := range groups { - if g == gid { - return true - } - } - return false + return slices.Contains(groups, gid) } func isCapDacOverrideSet() bool { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index 24b346e1a..813c05b66 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func readv(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_readv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_preadv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writev(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_writev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pwritev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := syscall_syscall(libc_fstat64_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index ebd213100..fda328582 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_readv(SB) +GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB) + +TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_preadv(SB) +GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB) + +TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_writev(SB) +GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB) + +TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pwritev(SB) +GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB) + TEXT libc_fstat64_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fstat64(SB) GLOBL ·libc_fstat64_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 824b9c2d5..e6f58f3c6 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -2512,6 +2512,90 @@ var libc_munmap_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func readv(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_readv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_readv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_readv readv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func preadv(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_preadv_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_preadv_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_preadv preadv "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func writev(fd int, iovecs []Iovec) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall(libc_writev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs))) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_writev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_writev writev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func pwritev(fd int, iovecs []Iovec, offset int64) (n int, err error) { + var _p0 unsafe.Pointer + if len(iovecs) > 0 { + _p0 = unsafe.Pointer(&iovecs[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + r0, _, e1 := syscall_syscall6(libc_pwritev_trampoline_addr, uintptr(fd), uintptr(_p0), uintptr(len(iovecs)), uintptr(offset), 0, 0) + n = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_pwritev_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_pwritev pwritev "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Fstat(fd int, stat *Stat_t) (err error) { _, _, e1 := syscall_syscall(libc_fstat_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0) if e1 != 0 { diff --git a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index 4f178a229..7f8998b90 100644 --- a/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -738,6 +738,26 @@ TEXT libc_munmap_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_munmap_trampoline_addr(SB), RODATA, $8 DATA ·libc_munmap_trampoline_addr(SB)/8, $libc_munmap_trampoline<>(SB) +TEXT libc_readv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_readv(SB) +GLOBL ·libc_readv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_readv_trampoline_addr(SB)/8, $libc_readv_trampoline<>(SB) + +TEXT libc_preadv_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_preadv(SB) +GLOBL ·libc_preadv_trampoline_addr(SB), RODATA, $8 +DATA ·libc_preadv_trampoline_addr(SB)/8, $libc_preadv_trampoline<>(SB) + +TEXT libc_writev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_writev(SB) +GLOBL ·libc_writev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_writev_trampoline_addr(SB)/8, $libc_writev_trampoline<>(SB) + +TEXT libc_pwritev_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_pwritev(SB) +GLOBL ·libc_pwritev_trampoline_addr(SB), RODATA, $8 +DATA ·libc_pwritev_trampoline_addr(SB)/8, $libc_pwritev_trampoline<>(SB) + TEXT libc_fstat_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_fstat(SB) GLOBL ·libc_fstat_trampoline_addr(SB), RODATA, $8 diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 9d138de5f..ad67df2fd 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -1074,6 +1074,7 @@ const ( IP_ADD_MEMBERSHIP = 0xc IP_DROP_MEMBERSHIP = 0xd IP_PKTINFO = 0x13 + IP_MTU_DISCOVER = 0x47 IPV6_V6ONLY = 0x1b IPV6_UNICAST_HOPS = 0x4 @@ -1083,6 +1084,7 @@ const ( IPV6_JOIN_GROUP = 0xc IPV6_LEAVE_GROUP = 0xd IPV6_PKTINFO = 0x13 + IPV6_MTU_DISCOVER = 0x47 MSG_OOB = 0x1 MSG_PEEK = 0x2 @@ -1132,6 +1134,15 @@ const ( WSASYS_STATUS_LEN = 128 ) +// enum PMTUD_STATE from ws2ipdef.h +const ( + IP_PMTUDISC_NOT_SET = 0 + IP_PMTUDISC_DO = 1 + IP_PMTUDISC_DONT = 2 + IP_PMTUDISC_PROBE = 3 + IP_PMTUDISC_MAX = 4 +) + type WSABuf struct { Len uint32 Buf *byte @@ -1146,6 +1157,22 @@ type WSAMsg struct { Flags uint32 } +type WSACMSGHDR struct { + Len uintptr + Level int32 + Type int32 +} + +type IN_PKTINFO struct { + Addr [4]byte + Ifindex uint32 +} + +type IN6_PKTINFO struct { + Addr [16]byte + Ifindex uint32 +} + // Flags for WSASocket const ( WSA_FLAG_OVERLAPPED = 0x01 diff --git a/vendor/golang.org/x/term/terminal.go b/vendor/golang.org/x/term/terminal.go index f636667fb..14f89470a 100644 --- a/vendor/golang.org/x/term/terminal.go +++ b/vendor/golang.org/x/term/terminal.go @@ -44,6 +44,8 @@ type Terminal struct { // bytes, as an index into |line|). If it returns ok=false, the key // press is processed normally. Otherwise it returns a replacement line // and the new cursor position. + // + // This will be disabled during ReadPassword. AutoCompleteCallback func(line string, pos int, key rune) (newLine string, newPos int, ok bool) // Escape contains a pointer to the escape codes for this terminal. @@ -692,6 +694,8 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { // ReadPassword temporarily changes the prompt and reads a password, without // echo, from the terminal. +// +// The AutoCompleteCallback is disabled during this call. func (t *Terminal) ReadPassword(prompt string) (line string, err error) { t.lock.Lock() defer t.lock.Unlock() @@ -699,6 +703,11 @@ func (t *Terminal) ReadPassword(prompt string) (line string, err error) { oldPrompt := t.prompt t.prompt = []rune(prompt) t.echo = false + oldAutoCompleteCallback := t.AutoCompleteCallback + t.AutoCompleteCallback = nil + defer func() { + t.AutoCompleteCallback = oldAutoCompleteCallback + }() line, err = t.readLine() diff --git a/vendor/modules.txt b/vendor/modules.txt index 3e68ca437..b233211ff 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -168,7 +168,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/noder -# github.com/jesseduffield/gocui v0.3.1-0.20250220081214-b376cb0857ac +# github.com/jesseduffield/gocui v0.3.1-0.20250408140206-7f1bb9232647 ## explicit; go 1.12 github.com/jesseduffield/gocui # github.com/jesseduffield/kill v0.0.0-20250101124109-e216ddbe133a @@ -307,20 +307,20 @@ golang.org/x/exp/slices golang.org/x/net/context golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/sync v0.11.0 -## explicit; go 1.18 +# golang.org/x/sync v0.13.0 +## explicit; go 1.23.0 golang.org/x/sync/errgroup -# golang.org/x/sys v0.30.0 -## explicit; go 1.18 +# golang.org/x/sys v0.32.0 +## explicit; go 1.23.0 golang.org/x/sys/cpu golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.29.0 -## explicit; go 1.18 +# golang.org/x/term v0.31.0 +## explicit; go 1.23.0 golang.org/x/term -# golang.org/x/text v0.22.0 -## explicit; go 1.18 +# golang.org/x/text v0.24.0 +## explicit; go 1.23.0 golang.org/x/text/encoding golang.org/x/text/encoding/internal/identifier golang.org/x/text/runes From 6f31f942668413767b0a020dbc9d6b292c560f14 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 3 Apr 2025 08:41:12 +0200 Subject: [PATCH 12/18] Enable AutoRenderHyperLinks for the tooltip view Sometimes tooltips contain documentation links, it's useful to be able to single-click them. --- pkg/gui/views.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/gui/views.go b/pkg/gui/views.go index d13dee7f4..bc7395fd6 100644 --- a/pkg/gui/views.go +++ b/pkg/gui/views.go @@ -134,6 +134,7 @@ func (gui *Gui) createAllViews() error { gui.Views.Menu.Visible = false gui.Views.Tooltip.Visible = false + gui.Views.Tooltip.AutoRenderHyperLinks = true gui.Views.Information.BgColor = gocui.ColorDefault gui.Views.Information.FgColor = gocui.ColorGreen From b3215a750c0ffdf09935ac7d0809ac731a2e7c22 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Thu, 10 Oct 2024 14:35:28 +0200 Subject: [PATCH 13/18] Cleanup: get rid of the variadic parameter of ContextMgr.Push Apparently this was an attempt at working around go's lack of default arguments, but it's very unidiomatic and a bit confusing. Make it a normal parameter instead, so all clients have to pass it explicitly. --- pkg/gui/context.go | 14 ++------------ .../custom_patch_options_menu_action.go | 2 +- pkg/gui/controllers/filtering_menu_action.go | 2 +- pkg/gui/controllers/helpers/commits_helper.go | 2 +- pkg/gui/controllers/helpers/confirmation_helper.go | 2 +- pkg/gui/controllers/helpers/fixup_helper.go | 2 +- .../controllers/helpers/merge_and_rebase_helper.go | 4 ++-- .../controllers/helpers/merge_conflicts_helper.go | 4 ++-- pkg/gui/controllers/helpers/refs_helper.go | 4 ++-- pkg/gui/controllers/helpers/search_helper.go | 4 ++-- pkg/gui/controllers/helpers/staging_helper.go | 2 +- pkg/gui/controllers/helpers/sub_commits_helper.go | 2 +- .../controllers/jump_to_side_window_controller.go | 2 +- pkg/gui/controllers/list_controller.go | 2 +- pkg/gui/controllers/quit_actions.go | 2 +- pkg/gui/controllers/remotes_controller.go | 2 +- pkg/gui/controllers/side_window_controller.go | 4 ++-- pkg/gui/controllers/snake_controller.go | 2 +- pkg/gui/controllers/staging_controller.go | 2 +- pkg/gui/controllers/stash_controller.go | 4 ++-- pkg/gui/controllers/status_controller.go | 2 +- pkg/gui/controllers/submodules_controller.go | 2 +- .../controllers/switch_to_diff_files_controller.go | 2 +- pkg/gui/controllers/tags_controller.go | 2 +- pkg/gui/extras_panel.go | 2 +- pkg/gui/gui.go | 2 +- pkg/gui/menu_panel.go | 2 +- pkg/gui/types/context.go | 2 +- pkg/gui/view_helpers.go | 2 +- 29 files changed, 36 insertions(+), 46 deletions(-) diff --git a/pkg/gui/context.go b/pkg/gui/context.go index 36f4e9dda..ca5727d38 100644 --- a/pkg/gui/context.go +++ b/pkg/gui/context.go @@ -55,17 +55,7 @@ func (self *ContextMgr) Replace(c types.Context) { self.Activate(c, types.OnFocusOpts{}) } -func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) { - if len(opts) > 1 { - panic("cannot pass multiple opts to Push") - } - - singleOpts := types.OnFocusOpts{} - if len(opts) > 0 { - // using triple dot but you should only ever pass one of these opt structs - singleOpts = opts[0] - } - +func (self *ContextMgr) Push(c types.Context, opts types.OnFocusOpts) { if !c.IsFocusable() { return } @@ -77,7 +67,7 @@ func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) { } if contextToActivate != nil { - self.Activate(contextToActivate, singleOpts) + self.Activate(contextToActivate, opts) } } diff --git a/pkg/gui/controllers/custom_patch_options_menu_action.go b/pkg/gui/controllers/custom_patch_options_menu_action.go index 04895ed6b..ced98b072 100644 --- a/pkg/gui/controllers/custom_patch_options_menu_action.go +++ b/pkg/gui/controllers/custom_patch_options_menu_action.go @@ -213,7 +213,7 @@ func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error { if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil { return err } - self.c.Context().Push(self.c.Contexts().LocalCommits) + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) return nil }) }, diff --git a/pkg/gui/controllers/filtering_menu_action.go b/pkg/gui/controllers/filtering_menu_action.go index 0f0f9ceec..f509e607a 100644 --- a/pkg/gui/controllers/filtering_menu_action.go +++ b/pkg/gui/controllers/filtering_menu_action.go @@ -120,7 +120,7 @@ func (self *FilteringMenuAction) setFiltering() error { repoState.SetScreenMode(types.SCREEN_HALF) } - self.c.Context().Push(self.c.Contexts().LocalCommits) + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() error { self.c.Contexts().LocalCommits.SetSelection(0) diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 9c89da706..27aafd7fc 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -150,7 +150,7 @@ func (self *CommitsHelper) OpenCommitMessagePanel(opts *OpenCommitMessagePanelOp self.UpdateCommitPanelView(opts.InitialMessage) - self.c.Context().Push(self.c.Contexts().CommitMessage) + self.c.Context().Push(self.c.Contexts().CommitMessage, types.OnFocusOpts{}) } func (self *CommitsHelper) OnCommitSuccess() { diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index 6e45087c0..20b224f4d 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -177,7 +177,7 @@ func (self *ConfirmationHelper) CreatePopupPanel(ctx goContext.Context, opts typ self.c.State().GetRepoState().SetCurrentPopupOpts(&opts) - self.c.Context().Push(self.c.Contexts().Confirmation) + self.c.Context().Push(self.c.Contexts().Confirmation, types.OnFocusOpts{}) } func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) { diff --git a/pkg/gui/controllers/helpers/fixup_helper.go b/pkg/gui/controllers/helpers/fixup_helper.go index 4d4709cf8..2c320ef53 100644 --- a/pkg/gui/controllers/helpers/fixup_helper.go +++ b/pkg/gui/controllers/helpers/fixup_helper.go @@ -137,7 +137,7 @@ func (self *FixupHelper) HandleFindBaseCommitForFixupPress() error { } self.c.Contexts().LocalCommits.SetSelection(index) - self.c.Context().Push(self.c.Contexts().LocalCommits) + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go index 04592519b..fba0b9014 100644 --- a/pkg/gui/controllers/helpers/merge_and_rebase_helper.go +++ b/pkg/gui/controllers/helpers/merge_and_rebase_helper.go @@ -206,7 +206,7 @@ func (self *MergeAndRebaseHelper) PromptForConflictHandling() error { { Label: self.c.Tr.ViewConflictsMenuItem, OnPress: func() error { - self.c.Context().Push(self.c.Contexts().Files) + self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{}) return nil }, }, @@ -357,7 +357,7 @@ func (self *MergeAndRebaseHelper) RebaseOntoRef(ref string) error { if err = self.ResetMarkedBaseCommit(); err != nil { return err } - self.c.Context().Push(self.c.Contexts().LocalCommits) + self.c.Context().Push(self.c.Contexts().LocalCommits, types.OnFocusOpts{}) return nil }, }, diff --git a/pkg/gui/controllers/helpers/merge_conflicts_helper.go b/pkg/gui/controllers/helpers/merge_conflicts_helper.go index d1f705be9..6e6a01531 100644 --- a/pkg/gui/controllers/helpers/merge_conflicts_helper.go +++ b/pkg/gui/controllers/helpers/merge_conflicts_helper.go @@ -62,7 +62,7 @@ func (self *MergeConflictsHelper) EscapeMerge() error { // files context over it. // So long as both places call OnUIThread, we're fine. if self.c.Context().IsCurrent(self.c.Contexts().MergeConflicts) { - self.c.Context().Push(self.c.Contexts().Files) + self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{}) } return nil }) @@ -93,7 +93,7 @@ func (self *MergeConflictsHelper) SwitchToMerge(path string) error { } } - self.c.Context().Push(self.c.Contexts().MergeConflicts) + self.c.Context().Push(self.c.Contexts().MergeConflicts, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index 02af116b5..fe78c09bd 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -118,7 +118,7 @@ func (self *RefsHelper) CheckoutRemoteBranch(fullBranchName string, localBranchN // Switch to the branches context _before_ starting to check out the // branch, so that we see the inline status if self.c.Context().Current() != self.c.Contexts().Branches { - self.c.Context().Push(self.c.Contexts().Branches) + self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{}) } return self.CheckoutRef(branchName, types.CheckoutRefOptions{}) } @@ -334,7 +334,7 @@ func (self *RefsHelper) NewBranch(from string, fromFormattedName string, suggest refresh := func() error { if self.c.Context().Current() != self.c.Contexts().Branches { - self.c.Context().Push(self.c.Contexts().Branches) + self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{}) } self.c.Contexts().LocalCommits.SetSelection(0) diff --git a/pkg/gui/controllers/helpers/search_helper.go b/pkg/gui/controllers/helpers/search_helper.go index e685df34a..cc9f22b22 100644 --- a/pkg/gui/controllers/helpers/search_helper.go +++ b/pkg/gui/controllers/helpers/search_helper.go @@ -41,7 +41,7 @@ func (self *SearchHelper) OpenFilterPrompt(context types.IFilterableContext) err self.OnPromptContentChanged("") promptView.RenderTextArea() - self.c.Context().Push(self.c.Contexts().Search) + self.c.Context().Push(self.c.Contexts().Search, types.OnFocusOpts{}) return self.c.ResetKeybindings() } @@ -58,7 +58,7 @@ func (self *SearchHelper) OpenSearchPrompt(context types.ISearchableContext) err promptView.ClearTextArea() promptView.RenderTextArea() - self.c.Context().Push(self.c.Contexts().Search) + self.c.Context().Push(self.c.Contexts().Search, types.OnFocusOpts{}) return self.c.ResetKeybindings() } diff --git a/pkg/gui/controllers/helpers/staging_helper.go b/pkg/gui/controllers/helpers/staging_helper.go index 69760a193..3d9762541 100644 --- a/pkg/gui/controllers/helpers/staging_helper.go +++ b/pkg/gui/controllers/helpers/staging_helper.go @@ -114,7 +114,7 @@ func (self *StagingHelper) RefreshStagingPanel(focusOpts types.OnFocusOpts) { } func (self *StagingHelper) handleStagingEscape() { - self.c.Context().Push(self.c.Contexts().Files) + self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{}) } func (self *StagingHelper) secondaryStagingFocused() bool { diff --git a/pkg/gui/controllers/helpers/sub_commits_helper.go b/pkg/gui/controllers/helpers/sub_commits_helper.go index a38acd2a7..3e8f88b7d 100644 --- a/pkg/gui/controllers/helpers/sub_commits_helper.go +++ b/pkg/gui/controllers/helpers/sub_commits_helper.go @@ -69,6 +69,6 @@ func (self *SubCommitsHelper) ViewSubCommits(opts ViewSubCommitsOpts) error { self.c.PostRefreshUpdate(self.c.Contexts().SubCommits) - self.c.Context().Push(self.c.Contexts().SubCommits) + self.c.Context().Push(self.c.Contexts().SubCommits, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/jump_to_side_window_controller.go b/pkg/gui/controllers/jump_to_side_window_controller.go index 0cbbc6c85..c0ef2faec 100644 --- a/pkg/gui/controllers/jump_to_side_window_controller.go +++ b/pkg/gui/controllers/jump_to_side_window_controller.go @@ -56,7 +56,7 @@ func (self *JumpToSideWindowController) goToSideWindow(window string) func() err context := self.c.Helpers().Window.GetContextForWindow(window) - self.c.Context().Push(context) + self.c.Context().Push(context, types.OnFocusOpts{}) return nil } } diff --git a/pkg/gui/controllers/list_controller.go b/pkg/gui/controllers/list_controller.go index 388f805f1..c7a589af4 100644 --- a/pkg/gui/controllers/list_controller.go +++ b/pkg/gui/controllers/list_controller.go @@ -179,7 +179,7 @@ func (self *ListController) HandleClick(opts gocui.ViewMouseBindingOpts) error { func (self *ListController) pushContextIfNotFocused() error { if !self.isFocused() { - self.c.Context().Push(self.context) + self.c.Context().Push(self.context, types.OnFocusOpts{}) } return nil diff --git a/pkg/gui/controllers/quit_actions.go b/pkg/gui/controllers/quit_actions.go index 000fe1792..8d5b0c9ca 100644 --- a/pkg/gui/controllers/quit_actions.go +++ b/pkg/gui/controllers/quit_actions.go @@ -79,7 +79,7 @@ func (self *QuitActions) Escape() error { parentContext := currentContext.GetParentContext() if parentContext != nil { // TODO: think about whether this should be marked as a return rather than adding to the stack - self.c.Context().Push(parentContext) + self.c.Context().Push(parentContext, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/remotes_controller.go b/pkg/gui/controllers/remotes_controller.go index d8a467eb0..3f4b5a604 100644 --- a/pkg/gui/controllers/remotes_controller.go +++ b/pkg/gui/controllers/remotes_controller.go @@ -129,7 +129,7 @@ func (self *RemotesController) enter(remote *models.Remote) error { self.c.PostRefreshUpdate(remoteBranchesContext) - self.c.Context().Push(remoteBranchesContext) + self.c.Context().Push(remoteBranchesContext, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/side_window_controller.go b/pkg/gui/controllers/side_window_controller.go index 799c87c2e..2cd421e0e 100644 --- a/pkg/gui/controllers/side_window_controller.go +++ b/pkg/gui/controllers/side_window_controller.go @@ -69,7 +69,7 @@ func (self *SideWindowController) previousSideWindow() error { context := self.c.Helpers().Window.GetContextForWindow(newWindow) - self.c.Context().Push(context) + self.c.Context().Push(context, types.OnFocusOpts{}) return nil } @@ -93,6 +93,6 @@ func (self *SideWindowController) nextSideWindow() error { context := self.c.Helpers().Window.GetContextForWindow(newWindow) - self.c.Context().Push(context) + self.c.Context().Push(context, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/snake_controller.go b/pkg/gui/controllers/snake_controller.go index 08dddf1be..a2a2030b7 100644 --- a/pkg/gui/controllers/snake_controller.go +++ b/pkg/gui/controllers/snake_controller.go @@ -73,6 +73,6 @@ func (self *SnakeController) SetDirection(direction snake.Direction) func() erro } func (self *SnakeController) Escape() error { - self.c.Context().Push(self.c.Contexts().Submodules) + self.c.Context().Push(self.c.Contexts().Submodules, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/staging_controller.go b/pkg/gui/controllers/staging_controller.go index f353cc215..93696a6ec 100644 --- a/pkg/gui/controllers/staging_controller.go +++ b/pkg/gui/controllers/staging_controller.go @@ -180,7 +180,7 @@ func (self *StagingController) Escape() error { func (self *StagingController) TogglePanel() error { if self.otherContext.GetState() != nil { - self.c.Context().Push(self.otherContext) + self.c.Context().Push(self.otherContext, types.OnFocusOpts{}) } return nil diff --git a/pkg/gui/controllers/stash_controller.go b/pkg/gui/controllers/stash_controller.go index 6b88e6849..84dbc456c 100644 --- a/pkg/gui/controllers/stash_controller.go +++ b/pkg/gui/controllers/stash_controller.go @@ -112,7 +112,7 @@ func (self *StashController) handleStashApply(stashEntry *models.StashEntry) err return err } if self.c.UserConfig().Gui.SwitchToFilesAfterStashApply { - self.c.Context().Push(self.c.Contexts().Files) + self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{}) } return nil } @@ -141,7 +141,7 @@ func (self *StashController) handleStashPop(stashEntry *models.StashEntry) error return err } if self.c.UserConfig().Gui.SwitchToFilesAfterStashPop { - self.c.Context().Push(self.c.Contexts().Files) + self.c.Context().Push(self.c.Contexts().Files, types.OnFocusOpts{}) } return nil } diff --git a/pkg/gui/controllers/status_controller.go b/pkg/gui/controllers/status_controller.go index 68028302f..d517b870c 100644 --- a/pkg/gui/controllers/status_controller.go +++ b/pkg/gui/controllers/status_controller.go @@ -104,7 +104,7 @@ func (self *StatusController) onClick(opts gocui.ViewMouseBindingOpts) error { return nil } - self.c.Context().Push(self.Context()) + self.c.Context().Push(self.Context(), types.OnFocusOpts{}) upstreamStatus := utils.Decolorise(presentation.BranchStatus(currentBranch, types.ItemOperationNone, self.c.Tr, time.Now(), self.c.UserConfig())) repoName := self.c.Git().RepoPaths.RepoName() diff --git a/pkg/gui/controllers/submodules_controller.go b/pkg/gui/controllers/submodules_controller.go index eb98c1656..aaeb75251 100644 --- a/pkg/gui/controllers/submodules_controller.go +++ b/pkg/gui/controllers/submodules_controller.go @@ -309,7 +309,7 @@ func (self *SubmodulesController) remove(submodule *models.SubmoduleConfig) erro } func (self *SubmodulesController) easterEgg() error { - self.c.Context().Push(self.c.Contexts().Snake) + self.c.Context().Push(self.c.Contexts().Snake, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/switch_to_diff_files_controller.go b/pkg/gui/controllers/switch_to_diff_files_controller.go index 3580e9227..fc764ca7e 100644 --- a/pkg/gui/controllers/switch_to_diff_files_controller.go +++ b/pkg/gui/controllers/switch_to_diff_files_controller.go @@ -91,7 +91,7 @@ func (self *SwitchToDiffFilesController) enter() error { return err } - self.c.Context().Push(commitFilesContext) + self.c.Context().Push(commitFilesContext, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/controllers/tags_controller.go b/pkg/gui/controllers/tags_controller.go index 75db8a6a6..664a0f279 100644 --- a/pkg/gui/controllers/tags_controller.go +++ b/pkg/gui/controllers/tags_controller.go @@ -115,7 +115,7 @@ func (self *TagsController) checkout(tag *models.Tag) error { if err := self.c.Helpers().Refs.CheckoutRef(tag.FullRefName(), types.CheckoutRefOptions{}); err != nil { return err } - self.c.Context().Push(self.c.Contexts().Branches) + self.c.Context().Push(self.c.Contexts().Branches, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go index 2319c813a..96fa225d3 100644 --- a/pkg/gui/extras_panel.go +++ b/pkg/gui/extras_panel.go @@ -38,7 +38,7 @@ func (gui *Gui) handleFocusCommandLog() error { gui.c.State().SetShowExtrasWindow(true) // TODO: is this necessary? Can't I just call 'return from context'? gui.State.Contexts.CommandLog.SetParentContext(gui.c.Context().CurrentSide()) - gui.c.Context().Push(gui.State.Contexts.CommandLog) + gui.c.Context().Push(gui.State.Contexts.CommandLog, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index ff6b68748..42922611a 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -394,7 +394,7 @@ func (gui *Gui) onNewRepo(startArgs appTypes.StartArgs, contextKey types.Context } } - gui.c.Context().Push(contextToPush) + gui.c.Context().Push(contextToPush, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index c9783db69..75fd6700d 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -60,6 +60,6 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error { gui.c.PostRefreshUpdate(gui.State.Contexts.Menu) // TODO: ensure that if we're opened a menu from within a menu that it renders correctly - gui.c.Context().Push(gui.State.Contexts.Menu) + gui.c.Context().Push(gui.State.Contexts.Menu, types.OnFocusOpts{}) return nil } diff --git a/pkg/gui/types/context.go b/pkg/gui/types/context.go index 5ad8ff3bb..eea207360 100644 --- a/pkg/gui/types/context.go +++ b/pkg/gui/types/context.go @@ -286,7 +286,7 @@ type ListItem interface { } type IContextMgr interface { - Push(context Context, opts ...OnFocusOpts) + Push(context Context, opts OnFocusOpts) Pop() Replace(context Context) Activate(context Context, opts OnFocusOpts) diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 9b066db4f..624d6e25f 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -72,7 +72,7 @@ func (gui *Gui) onViewTabClick(windowName string, tabIndex int) error { return nil } - gui.c.Context().Push(context) + gui.c.Context().Push(context, types.OnFocusOpts{}) return nil } From 8ba57b6bd04a1f83a2ffbc7fcb2703b4dc9976d0 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Fri, 11 Oct 2024 16:53:27 +0200 Subject: [PATCH 14/18] Cleanup: remove unused interfaces for helpers I can only guess here: maybe they were added to more clearly document the public interface of the classes? If so, I don't think that works. Developers who are not familiar with the convention will just add a new public method to the class without updating the interface. --- pkg/gui/controllers/helpers/commits_helper.go | 6 ------ pkg/gui/controllers/helpers/files_helper.go | 8 -------- pkg/gui/controllers/helpers/host_helper.go | 5 ----- pkg/gui/controllers/helpers/patch_building_helper.go | 4 ---- pkg/gui/controllers/helpers/refs_helper.go | 11 ----------- pkg/gui/controllers/helpers/suggestions_helper.go | 10 ---------- pkg/gui/controllers/helpers/upstream_helper.go | 9 --------- pkg/gui/controllers/helpers/working_tree_helper.go | 7 ------- pkg/gui/controllers/helpers/worktree_helper.go | 5 ----- 9 files changed, 65 deletions(-) diff --git a/pkg/gui/controllers/helpers/commits_helper.go b/pkg/gui/controllers/helpers/commits_helper.go index 27aafd7fc..c542b2d67 100644 --- a/pkg/gui/controllers/helpers/commits_helper.go +++ b/pkg/gui/controllers/helpers/commits_helper.go @@ -12,10 +12,6 @@ import ( "github.com/samber/lo" ) -type ICommitsHelper interface { - UpdateCommitPanelView(message string) -} - type CommitsHelper struct { c *HelperCommon @@ -26,8 +22,6 @@ type CommitsHelper struct { setCommitDescription func(string) } -var _ ICommitsHelper = &CommitsHelper{} - func NewCommitsHelper( c *HelperCommon, getCommitSummary func() string, diff --git a/pkg/gui/controllers/helpers/files_helper.go b/pkg/gui/controllers/helpers/files_helper.go index 24789ec0c..81c9b272e 100644 --- a/pkg/gui/controllers/helpers/files_helper.go +++ b/pkg/gui/controllers/helpers/files_helper.go @@ -6,12 +6,6 @@ import ( "github.com/samber/lo" ) -type IFilesHelper interface { - EditFiles(filenames []string) error - EditFileAtLine(filename string, lineNumber int) error - OpenFile(filename string) error -} - type FilesHelper struct { c *HelperCommon } @@ -22,8 +16,6 @@ func NewFilesHelper(c *HelperCommon) *FilesHelper { } } -var _ IFilesHelper = &FilesHelper{} - func (self *FilesHelper) EditFiles(filenames []string) error { absPaths := lo.Map(filenames, func(filename string, _ int) string { absPath, err := filepath.Abs(filename) diff --git a/pkg/gui/controllers/helpers/host_helper.go b/pkg/gui/controllers/helpers/host_helper.go index ab8631d36..42115e86f 100644 --- a/pkg/gui/controllers/helpers/host_helper.go +++ b/pkg/gui/controllers/helpers/host_helper.go @@ -6,11 +6,6 @@ import ( // this helper just wraps our hosting_service package -type IHostHelper interface { - GetPullRequestURL(from string, to string) (string, error) - GetCommitURL(commitHash string) (string, error) -} - type HostHelper struct { c *HelperCommon } diff --git a/pkg/gui/controllers/helpers/patch_building_helper.go b/pkg/gui/controllers/helpers/patch_building_helper.go index cde561fbc..1cf64b3f8 100644 --- a/pkg/gui/controllers/helpers/patch_building_helper.go +++ b/pkg/gui/controllers/helpers/patch_building_helper.go @@ -9,10 +9,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/types" ) -type IPatchBuildingHelper interface { - ValidateNormalWorkingTreeState() (bool, error) -} - type PatchBuildingHelper struct { c *HelperCommon } diff --git a/pkg/gui/controllers/helpers/refs_helper.go b/pkg/gui/controllers/helpers/refs_helper.go index fe78c09bd..c78b42b30 100644 --- a/pkg/gui/controllers/helpers/refs_helper.go +++ b/pkg/gui/controllers/helpers/refs_helper.go @@ -14,15 +14,6 @@ import ( "github.com/samber/lo" ) -type IRefsHelper interface { - CheckoutRef(ref string, options types.CheckoutRefOptions) error - GetCheckedOutRef() *models.Branch - CreateGitResetMenu(ref string) error - CreateCheckoutMenu(commit *models.Commit) error - ResetToRef(ref string, strength string, envVars []string) error - NewBranch(from string, fromDescription string, suggestedBranchname string) error -} - type RefsHelper struct { c *HelperCommon } @@ -35,8 +26,6 @@ func NewRefsHelper( } } -var _ IRefsHelper = &RefsHelper{} - func (self *RefsHelper) CheckoutRef(ref string, options types.CheckoutRefOptions) error { waitingStatus := options.WaitingStatus if waitingStatus == "" { diff --git a/pkg/gui/controllers/helpers/suggestions_helper.go b/pkg/gui/controllers/helpers/suggestions_helper.go index e5e933a8c..912e6cf59 100644 --- a/pkg/gui/controllers/helpers/suggestions_helper.go +++ b/pkg/gui/controllers/helpers/suggestions_helper.go @@ -26,20 +26,10 @@ import ( // finding suggestions in this file, so that it's easy to see if a function already // exists for fetching a particular model. -type ISuggestionsHelper interface { - GetRemoteSuggestionsFunc() func(string) []*types.Suggestion - GetBranchNameSuggestionsFunc() func(string) []*types.Suggestion - GetFilePathSuggestionsFunc() func(string) []*types.Suggestion - GetRemoteBranchesSuggestionsFunc(separator string) func(string) []*types.Suggestion - GetRefsSuggestionsFunc() func(string) []*types.Suggestion -} - type SuggestionsHelper struct { c *HelperCommon } -var _ ISuggestionsHelper = &SuggestionsHelper{} - func NewSuggestionsHelper( c *HelperCommon, ) *SuggestionsHelper { diff --git a/pkg/gui/controllers/helpers/upstream_helper.go b/pkg/gui/controllers/helpers/upstream_helper.go index c5d6ebe5b..092ca6bb1 100644 --- a/pkg/gui/controllers/helpers/upstream_helper.go +++ b/pkg/gui/controllers/helpers/upstream_helper.go @@ -14,15 +14,6 @@ type UpstreamHelper struct { getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion } -type IUpstreamHelper interface { - ParseUpstream(string) (string, string, error) - PromptForUpstreamWithInitialContent(*models.Branch, func(string) error) error - PromptForUpstreamWithoutInitialContent(*models.Branch, func(string) error) error - GetSuggestedRemote() string -} - -var _ IUpstreamHelper = &UpstreamHelper{} - func NewUpstreamHelper( c *HelperCommon, getRemoteBranchesSuggestionsFunc func(string) func(string) []*types.Suggestion, diff --git a/pkg/gui/controllers/helpers/working_tree_helper.go b/pkg/gui/controllers/helpers/working_tree_helper.go index e85e6702e..482c31c0d 100644 --- a/pkg/gui/controllers/helpers/working_tree_helper.go +++ b/pkg/gui/controllers/helpers/working_tree_helper.go @@ -12,13 +12,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/types" ) -type IWorkingTreeHelper interface { - AnyStagedFiles() bool - AnyTrackedFiles() bool - IsWorkingTreeDirty() bool - FileForSubmodule(submodule *models.SubmoduleConfig) *models.File -} - type WorkingTreeHelper struct { c *HelperCommon refHelper *RefsHelper diff --git a/pkg/gui/controllers/helpers/worktree_helper.go b/pkg/gui/controllers/helpers/worktree_helper.go index 6f28692d3..2d6460dcb 100644 --- a/pkg/gui/controllers/helpers/worktree_helper.go +++ b/pkg/gui/controllers/helpers/worktree_helper.go @@ -12,11 +12,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -type IWorktreeHelper interface { - GetMainWorktreeName() string - GetCurrentWorktreeName() string -} - type WorktreeHelper struct { c *HelperCommon reposHelper *ReposHelper From 2b783d1bc6c162ac889a85d2019276be6a63ad6f Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 19 Mar 2025 08:45:13 +0100 Subject: [PATCH 15/18] Add pageUp/pageDown/top/bottom keybindings to confirmation panel --- pkg/gui/global_handlers.go | 40 ++++++++++++++++++++++++++++++++++++++ pkg/gui/keybindings.go | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/pkg/gui/global_handlers.go b/pkg/gui/global_handlers.go index 9721b4b2a..0301cf63e 100644 --- a/pkg/gui/global_handlers.go +++ b/pkg/gui/global_handlers.go @@ -109,6 +109,46 @@ func (gui *Gui) scrollDownConfirmationPanel() error { return nil } +func (gui *Gui) pageUpConfirmationPanel() error { + if gui.Views.Confirmation.Editable { + return nil + } + + gui.Views.Confirmation.ScrollUp(gui.Contexts().Confirmation.GetViewTrait().PageDelta()) + + return nil +} + +func (gui *Gui) pageDownConfirmationPanel() error { + if gui.Views.Confirmation.Editable { + return nil + } + + gui.Views.Confirmation.ScrollDown(gui.Contexts().Confirmation.GetViewTrait().PageDelta()) + + return nil +} + +func (gui *Gui) goToConfirmationPanelTop() error { + if gui.Views.Confirmation.Editable { + return nil + } + + gui.Views.Confirmation.ScrollUp(gui.Views.Confirmation.ViewLinesHeight()) + + return nil +} + +func (gui *Gui) goToConfirmationPanelBottom() error { + if gui.Views.Confirmation.Editable { + return nil + } + + gui.Views.Confirmation.ScrollDown(gui.Views.Confirmation.ViewLinesHeight()) + + return nil +} + func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error { return gui.handleCopySelectedSideContextItemToClipboardWithTruncation(-1) } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 20163b90b..54c639145 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -260,6 +260,42 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi Key: gocui.MouseWheelDown, Handler: self.scrollDownConfirmationPanel, }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.NextPage), + Modifier: gocui.ModNone, + Handler: self.pageDownConfirmationPanel, + }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.PrevPage), + Modifier: gocui.ModNone, + Handler: self.pageUpConfirmationPanel, + }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.GotoTop), + Modifier: gocui.ModNone, + Handler: self.goToConfirmationPanelTop, + }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), + Modifier: gocui.ModNone, + Handler: self.goToConfirmationPanelTop, + }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.GotoBottom), + Modifier: gocui.ModNone, + Handler: self.goToConfirmationPanelBottom, + }, + { + ViewName: "confirmation", + Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), + Modifier: gocui.ModNone, + Handler: self.goToConfirmationPanelBottom, + }, { ViewName: "submodules", Key: opts.GetKey(opts.Config.Universal.CopyToClipboard), From 8a4506066a12b73bc0c4871bd0381bad464b948a Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 19 Mar 2025 09:09:12 +0100 Subject: [PATCH 16/18] Add pageUp/pageDown/top/bottom keybindings to focused command log panel --- pkg/gui/extras_panel.go | 32 ++++++++++++++++++++++++++++++++ pkg/gui/keybindings.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/pkg/gui/extras_panel.go b/pkg/gui/extras_panel.go index 96fa225d3..0c3f20cd5 100644 --- a/pkg/gui/extras_panel.go +++ b/pkg/gui/extras_panel.go @@ -58,6 +58,38 @@ func (gui *Gui) scrollDownExtra() error { return nil } +func (gui *Gui) pageUpExtrasPanel() error { + gui.Views.Extras.Autoscroll = false + + gui.Views.Extras.ScrollUp(gui.Contexts().CommandLog.GetViewTrait().PageDelta()) + + return nil +} + +func (gui *Gui) pageDownExtrasPanel() error { + gui.Views.Extras.Autoscroll = false + + gui.Views.Extras.ScrollDown(gui.Contexts().CommandLog.GetViewTrait().PageDelta()) + + return nil +} + +func (gui *Gui) goToExtrasPanelTop() error { + gui.Views.Extras.Autoscroll = false + + gui.Views.Extras.ScrollUp(gui.Views.Extras.ViewLinesHeight()) + + return nil +} + +func (gui *Gui) goToExtrasPanelBottom() error { + gui.Views.Extras.Autoscroll = true + + gui.Views.Extras.ScrollDown(gui.Views.Extras.ViewLinesHeight()) + + return nil +} + func (gui *Gui) getCmdWriter() io.Writer { return &prefixWriter{writer: gui.Views.Extras, prefix: style.FgMagenta.Sprintf("\n\n%s\n", gui.c.Tr.GitOutput)} } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 54c639145..e52f9a815 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -341,6 +341,42 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi Modifier: gocui.ModNone, Handler: self.scrollDownExtra, }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.NextPage), + Modifier: gocui.ModNone, + Handler: self.pageDownExtrasPanel, + }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.PrevPage), + Modifier: gocui.ModNone, + Handler: self.pageUpExtrasPanel, + }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.GotoTop), + Modifier: gocui.ModNone, + Handler: self.goToExtrasPanelTop, + }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.GotoTopAlt), + Modifier: gocui.ModNone, + Handler: self.goToExtrasPanelTop, + }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.GotoBottom), + Modifier: gocui.ModNone, + Handler: self.goToExtrasPanelBottom, + }, + { + ViewName: "extras", + Key: opts.GetKey(opts.Config.Universal.GotoBottomAlt), + Modifier: gocui.ModNone, + Handler: self.goToExtrasPanelBottom, + }, { ViewName: "extras", Tag: "navigation", From f20b7ea593ab8e3198d858cde520bd8756bc3ad1 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 1 Apr 2025 16:51:23 +0200 Subject: [PATCH 17/18] Add test demonstrating problem with main view display when pressing `e` in a stack of branches We keep the same commit selected (even though its index changed because of the added update-ref todo), which is nice; however, the main view shows the diff of the wrong commit, which is very confusing. I'm suprised that this hasn't been noticed yet. The reason why this happens is that we first do the refresh, which includes re-rendering the main view diff (with the same commit index as before, so the wrong one), and then we restore the correct commit index but don't render the main view again. --- .../interactive_rebase/drop_todo_commit_with_update_ref.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go index 02efec9da..8eff10632 100644 --- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go +++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go @@ -48,6 +48,12 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{ Contains("<-- YOU ARE HERE --- commit 02").IsSelected(), Contains("CI commit 01"), ). + Tap(func() { + /* EXPECTED: + t.Views().Main().Content(Contains("commit 02")) + ACTUAL: */ + t.Views().Main().Content(Contains("commit 03")) + }). NavigateToLine(Contains("commit 06")). Press(keys.Universal.Remove) From 59af407eb17092a6284bef251f44f1bd173cf5e8 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Tue, 1 Apr 2025 16:51:05 +0200 Subject: [PATCH 18/18] Fix the bug described in the previous commit --- pkg/gui/controllers/local_commits_controller.go | 1 + .../interactive_rebase/drop_todo_commit_with_update_ref.go | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index c459f4261..79e3a6549 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -608,6 +608,7 @@ func (self *LocalCommitsController) restoreSelectionRangeAndMode(selectionRangeA }) if ok1 && ok2 { self.context().SetSelectionRangeAndMode(newSelectedIdx, newRangeStartIdx, selectionRangeAndMode.mode) + self.context().HandleFocus(types.OnFocusOpts{}) } } diff --git a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go index 8eff10632..8510055af 100644 --- a/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go +++ b/pkg/integration/tests/interactive_rebase/drop_todo_commit_with_update_ref.go @@ -49,10 +49,7 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{ Contains("CI commit 01"), ). Tap(func() { - /* EXPECTED: t.Views().Main().Content(Contains("commit 02")) - ACTUAL: */ - t.Views().Main().Content(Contains("commit 03")) }). NavigateToLine(Contains("commit 06")). Press(keys.Universal.Remove)