Clean up utils package (#4538)

- **PR Description**

The utils package is a bit of a heterogeneous bag of miscellaneous
things at different abstraction levels right now; ideally it should only
contain low-level utilities similar to the helpers in utils/slice.go.
Further cleanup is possible here, e.g. something like rebase_todo.go
shouldn't be in this utils package. This PR doesn't address that,
however.

The goal of this PR is just to make it possible to import utils from any
other package. Previously it wasn't possible to import it from config,
because some of the stuff in utils depended on the config package. So
here we move only those things to better places. See the individual
commit messages for details.
This commit is contained in:
Stefan Haller 2025-05-06 13:07:30 +02:00 committed by GitHub
commit c16c9f982f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 77 additions and 68 deletions

View file

@ -8,6 +8,7 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
@ -296,7 +297,7 @@ func TestGetCommits(t *testing.T) {
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
common := utils.NewDummyCommon()
common := common.NewDummyCommon()
common.AppState = &config.AppState{}
common.AppState.GitLogOrder = scenario.logOrder
cmd := oscommands.NewDummyCmdObjBuilder(scenario.runner)
@ -516,7 +517,7 @@ func TestCommitLoader_getConflictedCommitImpl(t *testing.T) {
}
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
common := utils.NewDummyCommon()
common := common.NewDummyCommon()
builder := &CommitLoader{
Common: common,

View file

@ -9,7 +9,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/spf13/afero"
)
@ -32,7 +31,7 @@ func buildGitCommon(deps commonDeps) *GitCommon {
gitCommon.Common = deps.common
if gitCommon.Common == nil {
gitCommon.Common = utils.NewDummyCommonWithUserConfigAndAppState(deps.userConfig, deps.appState)
gitCommon.Common = common.NewDummyCommonWithUserConfigAndAppState(deps.userConfig, deps.appState)
}
if deps.fs != nil {

View file

@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sanity-io/litter"
@ -181,7 +182,7 @@ func TestGetReflogCommits(t *testing.T) {
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
builder := &ReflogCommitLoader{
Common: utils.NewDummyCommon(),
Common: common.NewDummyCommon(),
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
}

View file

@ -5,7 +5,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/stretchr/testify/assert"
)
@ -50,7 +50,7 @@ func TestGetStashEntries(t *testing.T) {
t.Run(s.testName, func(t *testing.T) {
cmd := oscommands.NewDummyCmdObjBuilder(s.runner)
loader := NewStashLoader(utils.NewDummyCommon(), cmd)
loader := NewStashLoader(common.NewDummyCommon(), cmd)
assert.EqualValues(t, s.expectedStashEntries, loader.GetStashEntries(s.filterPath))
})

View file

@ -5,7 +5,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/stretchr/testify/assert"
)
@ -46,7 +46,7 @@ func TestGetTags(t *testing.T) {
for _, scenario := range scenarios {
t.Run(scenario.testName, func(t *testing.T) {
loader := &TagLoader{
Common: utils.NewDummyCommon(),
Common: common.NewDummyCommon(),
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
}

View file

@ -8,7 +8,7 @@ import (
// NewDummyOSCommand creates a new dummy OSCommand for testing
func NewDummyOSCommand() *OSCommand {
osCmd := NewOSCommand(utils.NewDummyCommon(), config.NewDummyAppConfig(), dummyPlatform, NewNullGuiIO(utils.NewDummyLog()))
osCmd := NewOSCommand(common.NewDummyCommon(), config.NewDummyAppConfig(), dummyPlatform, NewNullGuiIO(utils.NewDummyLog()))
return osCmd
}
@ -23,9 +23,9 @@ type OSCommandDeps struct {
}
func NewDummyOSCommandWithDeps(deps OSCommandDeps) *OSCommand {
common := deps.Common
if common == nil {
common = utils.NewDummyCommon()
cmn := deps.Common
if cmn == nil {
cmn = common.NewDummyCommon()
}
platform := deps.Platform
@ -34,7 +34,7 @@ func NewDummyOSCommandWithDeps(deps OSCommandDeps) *OSCommand {
}
return &OSCommand{
Common: common,
Common: cmn,
Platform: platform,
getenvFn: deps.GetenvFn,
removeFileFn: deps.RemoveFileFn,
@ -59,7 +59,7 @@ var dummyPlatform = &Platform{
}
func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {
osCommand := NewOSCommand(utils.NewDummyCommon(), config.NewDummyAppConfig(), dummyPlatform, NewNullGuiIO(utils.NewDummyLog()))
osCommand := NewOSCommand(common.NewDummyCommon(), config.NewDummyAppConfig(), dummyPlatform, NewNullGuiIO(utils.NewDummyLog()))
osCommand.Cmd = NewDummyCmdObjBuilder(runner)
return osCommand

33
pkg/common/dummies.go Normal file
View file

@ -0,0 +1,33 @@
package common
import (
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/spf13/afero"
)
func NewDummyCommon() *Common {
tr := i18n.EnglishTranslationSet()
cmn := &Common{
Log: utils.NewDummyLog(),
Tr: tr,
Fs: afero.NewOsFs(),
}
cmn.SetUserConfig(config.GetDefaultConfig())
return cmn
}
func NewDummyCommonWithUserConfigAndAppState(userConfig *config.UserConfig, appState *config.AppState) *Common {
tr := i18n.EnglishTranslationSet()
cmn := &Common{
Log: utils.NewDummyLog(),
Tr: tr,
AppState: appState,
// TODO: remove dependency on actual filesystem in tests and switch to using
// in-memory for everything
Fs: afero.NewOsFs(),
}
cmn.SetUserConfig(userConfig)
return cmn
}

View file

@ -3,20 +3,20 @@ package gui
import (
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/updates"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func NewDummyUpdater() *updates.Updater {
newAppConfig := config.NewDummyAppConfig()
dummyUpdater, _ := updates.NewUpdater(utils.NewDummyCommon(), newAppConfig, oscommands.NewDummyOSCommand())
dummyUpdater, _ := updates.NewUpdater(common.NewDummyCommon(), newAppConfig, oscommands.NewDummyOSCommand())
return dummyUpdater
}
// NewDummyGui creates a new dummy GUI for testing
func NewDummyGui() *Gui {
newAppConfig := config.NewDummyAppConfig()
dummyGui, _ := NewGui(utils.NewDummyCommon(), newAppConfig, &git_commands.GitVersion{Major: 2, Minor: 0, Patch: 0}, NewDummyUpdater(), false, "", nil)
dummyGui, _ := NewGui(common.NewDummyCommon(), newAppConfig, &git_commands.GitVersion{Major: 2, Minor: 0, Patch: 0}, NewDummyUpdater(), false, "", nil)
return dummyGui
}

View file

@ -165,7 +165,7 @@ func BranchStatus(
) string {
itemOperationStr := ItemOperationToString(itemOperation, tr)
if itemOperationStr != "" {
return style.FgCyan.Sprintf("%s %s", itemOperationStr, utils.Loader(now, userConfig.Gui.Spinner))
return style.FgCyan.Sprintf("%s %s", itemOperationStr, Loader(now, userConfig.Gui.Spinner))
}
result := ""

View file

@ -8,9 +8,9 @@ import (
"github.com/gookit/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stretchr/testify/assert"
"github.com/xo/terminfo"
@ -320,7 +320,7 @@ func Test_getBranchDisplayStrings(t *testing.T) {
oldColorLevel := color.ForceSetColorLevel(terminfo.ColorLevelNone)
defer color.ForceSetColorLevel(oldColorLevel)
c := utils.NewDummyCommon()
c := common.NewDummyCommon()
SetCustomBranches(c.UserConfig().Gui.BranchColorPatterns, true)
for i, s := range scenarios {

View file

@ -10,6 +10,7 @@ import (
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/stefanhaller/git-todo-parser/todo"
@ -539,7 +540,7 @@ func TestGetCommitListDisplayStrings(t *testing.T) {
}
}
common := utils.NewDummyCommon()
common := common.NewDummyCommon()
for _, s := range scenarios {
if !focusing || s.focus {

View file

@ -0,0 +1,14 @@
package presentation
import (
"time"
"github.com/jesseduffield/lazygit/pkg/config"
)
// Loader dumps a string to be displayed as a loader
func Loader(now time.Time, config config.SpinnerConfig) string {
milliseconds := now.UnixMilli()
index := milliseconds / int64(config.Rate) % int64(len(config.Frames))
return config.Frames[index]
}

View file

@ -10,7 +10,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
@ -49,7 +48,7 @@ func getRemoteDisplayStrings(
descriptionStr := style.FgBlue.Sprintf("%d branches", branchCount)
itemOperationStr := ItemOperationToString(itemOperation, tr)
if itemOperationStr != "" {
descriptionStr += " " + style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now(), userConfig.Gui.Spinner))
descriptionStr += " " + style.FgCyan.Sprint(itemOperationStr+" "+Loader(time.Now(), userConfig.Gui.Spinner))
}
res = append(res, textStyle.Sprint(r.Name), descriptionStr)
return res

View file

@ -10,7 +10,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
@ -47,7 +46,7 @@ func getTagDisplayStrings(
descriptionStr := descriptionColor.Sprint(t.Description())
itemOperationStr := ItemOperationToString(itemOperation, tr)
if itemOperationStr != "" {
descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+utils.Loader(time.Now(), userConfig.Gui.Spinner)) + " " + descriptionStr
descriptionStr = style.FgCyan.Sprint(itemOperationStr+" "+Loader(time.Now(), userConfig.Gui.Spinner)) + " " + descriptionStr
}
res = append(res, textStyle.Sprint(t.Name), descriptionStr)
return res

View file

@ -3,7 +3,7 @@ package custom_commands
import (
"testing"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/stretchr/testify/assert"
)
@ -82,7 +82,7 @@ func TestMenuGenerator(t *testing.T) {
for _, s := range scenarios {
t.Run(s.testName, func(t *testing.T) {
s.test(NewMenuGenerator(utils.NewDummyCommon()).call(s.cmdOut, s.filter, s.valueFormat, s.labelFormat))
s.test(NewMenuGenerator(common.NewDummyCommon()).call(s.cmdOut, s.filter, s.valueFormat, s.labelFormat))
})
}
}

View file

@ -5,8 +5,8 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
@ -75,7 +75,7 @@ func (self *StatusManager) GetStatusString(userConfig *config.UserConfig) (strin
}
topStatus := self.statuses[0]
if topStatus.statusType == "waiting" {
return topStatus.message + " " + utils.Loader(time.Now(), userConfig.Gui.Spinner), topStatus.color
return topStatus.message + " " + presentation.Loader(time.Now(), userConfig.Gui.Spinner), topStatus.color
}
return topStatus.message, topStatus.color
}

View file

@ -3,11 +3,7 @@ package utils
import (
"io"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
)
// NewDummyLog creates a new dummy Log for testing
@ -16,28 +12,3 @@ func NewDummyLog() *logrus.Entry {
log.Out = io.Discard
return log.WithField("test", "test")
}
func NewDummyCommon() *common.Common {
tr := i18n.EnglishTranslationSet()
cmn := &common.Common{
Log: NewDummyLog(),
Tr: tr,
Fs: afero.NewOsFs(),
}
cmn.SetUserConfig(config.GetDefaultConfig())
return cmn
}
func NewDummyCommonWithUserConfigAndAppState(userConfig *config.UserConfig, appState *config.AppState) *common.Common {
tr := i18n.EnglishTranslationSet()
cmn := &common.Common{
Log: NewDummyLog(),
Tr: tr,
AppState: appState,
// TODO: remove dependency on actual filesystem in tests and switch to using
// in-memory for everything
Fs: afero.NewOsFs(),
}
cmn.SetUserConfig(userConfig)
return cmn
}

View file

@ -8,10 +8,8 @@ import (
"runtime"
"strconv"
"strings"
"time"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
)
// GetProjectRoot returns the path to the root of the project. Only to be used
@ -25,13 +23,6 @@ func GetProjectRoot() string {
return strings.Split(dir, "lazygit")[0] + "lazygit"
}
// Loader dumps a string to be displayed as a loader
func Loader(now time.Time, config config.SpinnerConfig) string {
milliseconds := now.UnixMilli()
index := milliseconds / int64(config.Rate) % int64(len(config.Frames))
return config.Frames[index]
}
func SortRange(x int, y int) (int, int) {
if x < y {
return x, y