File: common.go

package info (click to toggle)
lazygit 0.50.0%2Bds1-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,808 kB
  • sloc: sh: 128; makefile: 76
file content (396 lines) | stat: -rw-r--r-- 12,538 bytes parent folder | download
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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
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/common"
	"github.com/jesseduffield/lazygit/pkg/config"
	"github.com/jesseduffield/lazygit/pkg/tasks"
	"github.com/jesseduffield/lazygit/pkg/utils"
	"github.com/sasha-s/go-deadlock"
	"github.com/ozeidan/fuzzy-patricia/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)

	// 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)
	// used purely for the sake of RenderToMainViews to provide the pair of main views we want to render to
	MainViewPairs() MainViewPairs

	// return the view buffer manager for the given view, or nil if it doesn't have one
	GetViewBufferManagerForView(view *gocui.View) *tasks.ViewBufferManager

	// returns true if command completed successfully
	RunSubprocess(cmdObj oscommands.ICmdObj) (bool, error)
	RunSubprocessAndRefresh(oscommands.ICmdObj) error

	Context() IContextMgr
	ContextForKey(key ContextKey) Context

	GetConfig() config.AppConfigurer
	GetAppState() *config.AppState
	SaveAppState() error
	SaveAppStateAndLogError()

	// 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) error)
	// 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)

	// Wraps a function, attaching the given operation to the given item while
	// the function is executing, and also causes the given context to be
	// redrawn periodically. This allows the operation to be visualized with a
	// spinning loader animation (e.g. when a branch is being pushed).
	WithInlineStatus(item HasUrn, operation ItemOperation, contextKey ContextKey, f func(gocui.Task) error) 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
	CallKeybindingHandler(binding *Binding) error

	ResetKeybindings() error

	// hopefully we can remove this once we've moved all our keybinding stuff out of the gui god struct.
	GetInitialKeybindingsWithCustomCommands() ([]*Binding, []*gocui.ViewMouseBinding)

	// Returns true if we're running an integration test
	RunningIntegrationTest() bool

	// Returns true if we're in a demo recording/playback
	InDemo() bool
}

type IModeMgr interface {
	IsAnyModeActive() bool
}

type IPopupHandler interface {
	// The global error handler for gocui. Not to be used by application code.
	ErrorHandler(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)
	// Shows a popup asking the user for confirmation.
	Confirm(opts ConfirmOpts)
	// Shows a popup prompting the user for input.
	Prompt(opts PromptOpts)
	WithWaitingStatus(message string, f func(gocui.Task) error) error
	WithWaitingStatusSync(message string, f func() error) error
	Menu(opts CreateMenuOptions) error
	Toast(message string)
	ErrorToast(message string)
	SetToastFunc(func(string, ToastKind))
	GetPromptInput() string
}

type ToastKind int

const (
	ToastKindStatus ToastKind = iota
	ToastKindError
)

type CreateMenuOptions struct {
	Title           string
	Prompt          string // a message that will be displayed above the menu options
	Items           []*MenuItem
	HideCancel      bool
	ColumnAlignment []utils.Alignment
}

type CreatePopupPanelOpts struct {
	HasLoader              bool
	Editable               bool
	Title                  string
	Prompt                 string
	HandleConfirm          func() error
	HandleConfirmPrompt    func(string) error
	HandleClose            func() error
	HandleDeleteSuggestion func(int) error

	FindSuggestionsFunc func(string) []*Suggestion
	Mask                bool
	AllowEditSuggestion bool
}

type ConfirmOpts struct {
	Title               string
	Prompt              string
	HandleConfirm       func() error
	HandleClose         func() error
	FindSuggestionsFunc func(string) []*Suggestion
	Editable            bool
	Mask                bool
}

type PromptOpts struct {
	Title               string
	InitialContent      string
	FindSuggestionsFunc func(string) []*Suggestion
	HandleConfirm       func(string) error
	AllowEditSuggestion bool
	// CAPTURE THIS
	HandleClose            func() error
	HandleDeleteSuggestion func(int) error
	Mask                   bool
}

type MenuSection struct {
	Title  string
	Column int // The column that this section title should be aligned with
}

type DisabledReason struct {
	Text string

	// When trying to invoke a disabled key binding or menu item, we normally
	// show the disabled reason as a toast; setting this to true shows it as an
	// error panel instead. This is useful if the text is very long, or if it is
	// important enough to show it more prominently, or both.
	ShowErrorInPanel bool
}

type MenuWidget int

const (
	MenuWidgetNone MenuWidget = iota
	MenuWidgetRadioButtonSelected
	MenuWidgetRadioButtonUnselected
	MenuWidgetCheckboxSelected
	MenuWidgetCheckboxUnselected
)

func MakeMenuRadioButton(value bool) MenuWidget {
	if value {
		return MenuWidgetRadioButtonSelected
	}
	return MenuWidgetRadioButtonUnselected
}

func MakeMenuCheckBox(value bool) MenuWidget {
	if value {
		return MenuWidgetCheckboxSelected
	}
	return MenuWidgetCheckboxUnselected
}

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

	// A widget to show in front of the menu item. Supported widget types are
	// checkboxes and radio buttons,
	// This only handles the rendering of the widget; the behavior needs to be
	// provided by the client.
	Widget MenuWidget

	// The tooltip will be displayed upon highlighting the menu item
	Tooltip string

	// If non-nil, show this in a tooltip, style the menu item as disabled,
	// and refuse to invoke the command
	DisabledReason *DisabledReason

	// Can be used to group menu items into sections with headers. MenuItems
	// with the same Section should be contiguous, and will automatically get a
	// section header. If nil, the item is not part of a section.
	// Note that pointer comparison is used to determine whether two menu items
	// belong to the same section, so make sure all your items in a given
	// section point to the same MenuSection instance.
	Section *MenuSection
}

// Defining this for the sake of conforming to the HasID interface, which is used
// in list contexts.
func (self *MenuItem) ID() string {
	return self.Label
}

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
	Worktrees    []*models.Worktree

	// 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 models.WorkingTreeState
	RemoteBranches                      []*models.RemoteBranch
	Tags                                []*models.Tag

	// Name of the currently checked out branch. This will be set even when
	// we're on a detached head because we're rebasing or bisecting.
	CheckedOutBranch string

	MainBranches *git_commands.MainBranches

	// for displaying suggestions while typing in a file name
	FilesTrie *patricia.Trie

	Authors map[string]*models.Author

	HashPool *utils.StringPool
}

// 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
	LocalCommitsMutex       *deadlock.Mutex
	SubCommitsMutex         *deadlock.Mutex
	AuthorsMutex            *deadlock.Mutex
	SubprocessMutex         *deadlock.Mutex
	PopupMutex              *deadlock.Mutex
	PtyMutex                *deadlock.Mutex
}

// A long-running operation associated with an item. For example, we'll show
// that a branch is being pushed from so that there's visual feedback about
// what's happening and so that you can see multiple branches' concurrent
// operations
type ItemOperation int

const (
	ItemOperationNone ItemOperation = iota
	ItemOperationPushing
	ItemOperationPulling
	ItemOperationFastForwarding
	ItemOperationDeleting
	ItemOperationFetching
	ItemOperationCheckingOut
)

type HasUrn interface {
	URN() string
}

type IStateAccessor interface {
	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)
	GetItemOperation(item HasUrn) ItemOperation
	SetItemOperation(item HasUrn, operation ItemOperation)
	ClearItemOperation(item HasUrn)
}

type IRepoStateAccessor interface {
	GetViewsSetup() bool
	GetWindowViewNameMap() *utils.ThreadSafeMap[string, string]
	GetStartupStage() StartupStage
	SetStartupStage(stage StartupStage)
	GetCurrentPopupOpts() *CreatePopupPanelOpts
	SetCurrentPopupOpts(*CreatePopupPanelOpts)
	GetScreenMode() ScreenMode
	SetScreenMode(ScreenMode)
	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
)

// 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 ScreenMode int

const (
	SCREEN_NORMAL ScreenMode = iota
	SCREEN_HALF
	SCREEN_FULL
)