1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
|
package types
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sasha-s/go-deadlock"
"gopkg.in/ozeidan/fuzzy-patricia.v3/patricia"
)
type HelperCommon struct {
*ContextCommon
}
type ContextCommon struct {
*common.Common
IGuiCommon
}
type IGuiCommon interface {
IPopupHandler
LogAction(action string)
LogCommand(cmdStr string, isCommandLine bool)
// we call this when we want to refetch some models and render the result. Internally calls PostRefreshUpdate
Refresh(RefreshOptions) error
// we call this when we've changed something in the view model but not the actual model,
// e.g. expanding or collapsing a folder in a file view. Calling 'Refresh' in this
// case would be overkill, although refresh will internally call 'PostRefreshUpdate'
PostRefreshUpdate(Context) error
// renders string to a view without resetting its origin
SetViewContent(view *gocui.View, content string)
// resets cursor and origin of view. Often used before calling SetViewContent
ResetViewOrigin(view *gocui.View)
// this just re-renders the screen
Render()
// allows rendering to main views (i.e. the ones to the right of the side panel)
// in such a way that avoids concurrency issues when there are slow commands
// to display the output of
RenderToMainViews(opts RefreshMainOpts) error
// used purely for the sake of RenderToMainViews to provide the pair of main views we want to render to
MainViewPairs() MainViewPairs
// returns true if command completed successfully
RunSubprocess(cmdObj oscommands.ICmdObj) (bool, error)
RunSubprocessAndRefresh(oscommands.ICmdObj) error
PushContext(context Context, opts ...OnFocusOpts) error
PopContext() error
ReplaceContext(context Context) error
// Removes all given contexts from the stack. If a given context is not in the stack, it is ignored.
// This is for when you have a group of contexts that are bundled together e.g. with the commit message panel.
// If you want to remove a single context, you should probably use PopContext instead.
RemoveContexts([]Context) error
CurrentContext() Context
CurrentStaticContext() Context
CurrentSideContext() Context
IsCurrentContext(Context) bool
// TODO: replace the above context-based methods with just using Context() e.g. replace PushContext() with Context().Push()
Context() IContextMgr
ActivateContext(context Context) error
GetConfig() config.AppConfigurer
GetAppState() *config.AppState
SaveAppState() error
// Runs the given function on the UI thread (this is for things like showing a popup asking a user for input).
// Only necessary to call if you're not already on the UI thread i.e. you're inside a goroutine.
// All controller handlers are executed on the UI thread.
OnUIThread(f func() error)
// Runs a function in a goroutine. Use this whenever you want to run a goroutine and keep track of the fact
// that lazygit is still busy. See docs/dev/Busy.md
OnWorker(f func(gocui.Task))
// Function to call at the end of our 'layout' function which renders views
// For example, you may want a view's line to be focused only after that view is
// resized, if in accordion mode.
AfterLayout(f func() error)
// returns the gocui Gui struct. There is a good chance you don't actually want to use
// this struct and instead want to use another method above
GocuiGui() *gocui.Gui
Views() Views
Git() *commands.GitCommand
OS() *oscommands.OSCommand
Model() *Model
Modes() *Modes
Mutexes() Mutexes
State() IStateAccessor
KeybindingsOpts() KeybindingsOpts
// hopefully we can remove this once we've moved all our keybinding stuff out of the gui god struct.
GetInitialKeybindingsWithCustomCommands() ([]*Binding, []*gocui.ViewMouseBinding)
}
type IModeMgr interface {
IsAnyModeActive() bool
}
type IPopupHandler interface {
// Shows a popup with a (localized) "Error" caption and the given error message (in red).
//
// This is a convenience wrapper around Alert().
ErrorMsg(message string) error
Error(err error) error
// Shows a notification popup with the given title and message to the user.
//
// This is a convenience wrapper around Confirm(), thus the popup can be closed using both 'Enter' and 'ESC'.
Alert(title string, message string) error
// Shows a popup asking the user for confirmation.
Confirm(opts ConfirmOpts) error
// Shows a popup prompting the user for input.
Prompt(opts PromptOpts) error
WithLoaderPanel(message string, f func(gocui.Task) error) error
WithWaitingStatus(message string, f func(gocui.Task) error) error
Menu(opts CreateMenuOptions) error
Toast(message string)
GetPromptInput() string
}
type CreateMenuOptions struct {
Title string
Items []*MenuItem
HideCancel bool
}
type CreatePopupPanelOpts struct {
HasLoader bool
Editable bool
Title string
Prompt string
HandleConfirm func() error
HandleConfirmPrompt func(string) error
HandleClose func() error
FindSuggestionsFunc func(string) []*Suggestion
Mask bool
}
type ConfirmOpts struct {
Title string
Prompt string
HandleConfirm func() error
HandleClose func() error
HasLoader bool
FindSuggestionsFunc func(string) []*Suggestion
Editable bool
Mask bool
}
type PromptOpts struct {
Title string
InitialContent string
FindSuggestionsFunc func(string) []*Suggestion
HandleConfirm func(string) error
// CAPTURE THIS
HandleClose func() error
Mask bool
}
type MenuItem struct {
Label string
// alternative to Label. Allows specifying columns which will be auto-aligned
LabelColumns []string
OnPress func() error
// Only applies when Label is used
OpensMenu bool
// If Key is defined it allows the user to press the key to invoke the menu
// item, as opposed to having to navigate to it
Key Key
// The tooltip will be displayed upon highlighting the menu item
Tooltip string
}
type Model struct {
CommitFiles []*models.CommitFile
Files []*models.File
Submodules []*models.SubmoduleConfig
Branches []*models.Branch
Commits []*models.Commit
StashEntries []*models.StashEntry
SubCommits []*models.Commit
Remotes []*models.Remote
// FilteredReflogCommits are the ones that appear in the reflog panel.
// when in filtering mode we only include the ones that match the given path
FilteredReflogCommits []*models.Commit
// ReflogCommits are the ones used by the branches panel to obtain recency values
// if we're not in filtering mode, CommitFiles and FilteredReflogCommits will be
// one and the same
ReflogCommits []*models.Commit
BisectInfo *git_commands.BisectInfo
WorkingTreeStateAtLastCommitRefresh enums.RebaseMode
RemoteBranches []*models.RemoteBranch
Tags []*models.Tag
// for displaying suggestions while typing in a file name
FilesTrie *patricia.Trie
}
// if you add a new mutex here be sure to instantiate it. We're using pointers to
// mutexes so that we can pass the mutexes to controllers.
type Mutexes struct {
RefreshingFilesMutex *deadlock.Mutex
RefreshingBranchesMutex *deadlock.Mutex
RefreshingStatusMutex *deadlock.Mutex
SyncMutex *deadlock.Mutex
LocalCommitsMutex *deadlock.Mutex
SubCommitsMutex *deadlock.Mutex
SubprocessMutex *deadlock.Mutex
PopupMutex *deadlock.Mutex
PtyMutex *deadlock.Mutex
}
type IStateAccessor interface {
GetIgnoreWhitespaceInDiffView() bool
SetIgnoreWhitespaceInDiffView(value bool)
GetRepoPathStack() *utils.StringStack
GetRepoState() IRepoStateAccessor
// tells us whether we're currently updating lazygit
GetUpdating() bool
SetUpdating(bool)
SetIsRefreshingFiles(bool)
GetIsRefreshingFiles() bool
GetShowExtrasWindow() bool
SetShowExtrasWindow(bool)
GetRetainOriginalDir() bool
SetRetainOriginalDir(bool)
}
type IRepoStateAccessor interface {
GetViewsSetup() bool
GetWindowViewNameMap() *utils.ThreadSafeMap[string, string]
GetStartupStage() StartupStage
SetStartupStage(stage StartupStage)
GetCurrentPopupOpts() *CreatePopupPanelOpts
SetCurrentPopupOpts(*CreatePopupPanelOpts)
GetScreenMode() WindowMaximisation
SetScreenMode(WindowMaximisation)
InSearchPrompt() bool
GetSearchState() *SearchState
SetSplitMainPanel(bool)
GetSplitMainPanel() bool
}
// startup stages so we don't need to load everything at once
type StartupStage int
const (
INITIAL StartupStage = iota
COMPLETE
)
type IFileWatcher interface {
AddFilesToFileWatcher(files []*models.File) error
}
// screen sizing determines how much space your selected window takes up (window
// as in panel, not your terminal's window). Sometimes you want a bit more space
// to see the contents of a panel, and this keeps track of how much maximisation
// you've set
type WindowMaximisation int
const (
SCREEN_NORMAL WindowMaximisation = iota
SCREEN_HALF
SCREEN_FULL
)
|