2020-03-29 01:31:34 +02:00
|
|
|
package gui
|
|
|
|
|
|
|
|
import (
|
2020-11-21 08:15:43 +02:00
|
|
|
"fmt"
|
2020-03-29 01:31:34 +02:00
|
|
|
"math"
|
|
|
|
"strings"
|
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
"github.com/jesseduffield/gocui"
|
2022-01-08 05:00:36 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
2022-01-28 11:44:36 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
2020-03-29 01:31:34 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
|
|
)
|
|
|
|
|
2021-11-02 11:35:53 +02:00
|
|
|
const HORIZONTAL_SCROLL_FACTOR = 3
|
|
|
|
|
2020-08-19 11:07:14 +02:00
|
|
|
// these views need to be re-rendered when the screen mode changes. The commits view,
|
|
|
|
// for example, will show authorship information in half and full screen mode.
|
2020-08-19 11:26:05 +02:00
|
|
|
func (gui *Gui) rerenderViewsWithScreenModeDependentContent() error {
|
2021-04-04 16:44:13 +02:00
|
|
|
for _, view := range []*gocui.View{gui.Views.Branches, gui.Views.Commits} {
|
|
|
|
if err := gui.rerenderView(view); err != nil {
|
2020-08-19 11:07:14 +02:00
|
|
|
return err
|
|
|
|
}
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-03-31 14:55:06 +02:00
|
|
|
// TODO: GENERICS
|
|
|
|
func nextIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
|
|
|
for i, val := range sl {
|
|
|
|
if val == current {
|
|
|
|
if i == len(sl)-1 {
|
|
|
|
return sl[0]
|
|
|
|
}
|
|
|
|
return sl[i+1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sl[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: GENERICS
|
|
|
|
func prevIntInCycle(sl []WindowMaximisation, current WindowMaximisation) WindowMaximisation {
|
|
|
|
for i, val := range sl {
|
|
|
|
if val == current {
|
|
|
|
if i > 0 {
|
|
|
|
return sl[i-1]
|
|
|
|
}
|
|
|
|
return sl[len(sl)-1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sl[len(sl)-1]
|
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) nextScreenMode() error {
|
2021-03-31 14:55:06 +02:00
|
|
|
gui.State.ScreenMode = nextIntInCycle([]WindowMaximisation{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
2020-08-19 11:26:05 +02:00
|
|
|
|
|
|
|
return gui.rerenderViewsWithScreenModeDependentContent()
|
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) prevScreenMode() error {
|
2021-03-31 14:55:06 +02:00
|
|
|
gui.State.ScreenMode = prevIntInCycle([]WindowMaximisation{SCREEN_NORMAL, SCREEN_HALF, SCREEN_FULL}, gui.State.ScreenMode)
|
2020-08-19 11:07:14 +02:00
|
|
|
|
2020-08-19 11:26:05 +02:00
|
|
|
return gui.rerenderViewsWithScreenModeDependentContent()
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
func (gui *Gui) scrollUpView(view *gocui.View) error {
|
|
|
|
ox, oy := view.Origin()
|
2022-01-16 05:46:53 +02:00
|
|
|
newOy := int(math.Max(0, float64(oy-gui.c.UserConfig.Gui.ScrollHeight)))
|
2021-04-04 15:51:59 +02:00
|
|
|
return view.SetOrigin(ox, newOy)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
func (gui *Gui) scrollDownView(view *gocui.View) error {
|
|
|
|
ox, oy := view.Origin()
|
2021-04-11 03:43:07 +02:00
|
|
|
scrollHeight := gui.linesToScrollDown(view)
|
|
|
|
if scrollHeight > 0 {
|
|
|
|
if err := view.SetOrigin(ox, oy+scrollHeight); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if manager, ok := gui.viewBufferManagerMap[view.Name()]; ok {
|
|
|
|
manager.ReadLines(scrollHeight)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) linesToScrollDown(view *gocui.View) int {
|
|
|
|
_, oy := view.Origin()
|
2020-03-29 01:31:34 +02:00
|
|
|
y := oy
|
2022-01-16 05:46:53 +02:00
|
|
|
canScrollPastBottom := gui.c.UserConfig.Gui.ScrollPastBottom
|
2021-04-02 06:25:27 +02:00
|
|
|
if !canScrollPastBottom {
|
2021-04-04 15:51:59 +02:00
|
|
|
_, sy := view.Size()
|
2020-03-29 01:31:34 +02:00
|
|
|
y += sy
|
|
|
|
}
|
2022-01-16 05:46:53 +02:00
|
|
|
scrollHeight := gui.c.UserConfig.Gui.ScrollHeight
|
2021-04-04 15:51:59 +02:00
|
|
|
scrollableLines := view.ViewLinesHeight() - y
|
2021-04-11 03:43:07 +02:00
|
|
|
if scrollableLines < 0 {
|
|
|
|
return 0
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
2021-04-11 03:43:07 +02:00
|
|
|
|
|
|
|
// margin is about how many lines must still appear if you scroll
|
|
|
|
// all the way down. In practice every file ends in a newline so it will really
|
|
|
|
// just show a single line
|
|
|
|
margin := 1
|
|
|
|
if canScrollPastBottom {
|
|
|
|
margin = 2
|
|
|
|
}
|
|
|
|
if scrollableLines-margin < scrollHeight {
|
|
|
|
scrollHeight = scrollableLines - margin
|
|
|
|
}
|
|
|
|
if oy+scrollHeight < 0 {
|
|
|
|
return 0
|
|
|
|
} else {
|
|
|
|
return scrollHeight
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
2021-04-11 03:43:07 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 05:33:20 +02:00
|
|
|
func (gui *Gui) scrollUpMain() error {
|
2022-01-25 16:20:19 +02:00
|
|
|
if gui.renderingConflicts() {
|
2021-11-02 11:35:53 +02:00
|
|
|
gui.State.Panels.Merging.UserVerticalScrolling = true
|
2020-05-19 10:29:56 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollUpView(gui.Views.Main)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 05:33:20 +02:00
|
|
|
func (gui *Gui) scrollDownMain() error {
|
2022-01-25 16:20:19 +02:00
|
|
|
if gui.renderingConflicts() {
|
2021-11-02 11:35:53 +02:00
|
|
|
gui.State.Panels.Merging.UserVerticalScrolling = true
|
2020-05-19 10:29:56 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollDownView(gui.Views.Main)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-11-02 11:35:53 +02:00
|
|
|
func (gui *Gui) scrollLeftMain() error {
|
|
|
|
gui.scrollLeft(gui.Views.Main)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) scrollRightMain() error {
|
|
|
|
gui.scrollRight(gui.Views.Main)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) scrollLeft(view *gocui.View) {
|
|
|
|
newOriginX := utils.Max(view.OriginX()-view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR, 0)
|
|
|
|
_ = view.SetOriginX(newOriginX)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gui *Gui) scrollRight(view *gocui.View) {
|
|
|
|
_ = view.SetOriginX(view.OriginX() + view.InnerWidth()/HORIZONTAL_SCROLL_FACTOR)
|
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) scrollUpSecondary() error {
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollUpView(gui.Views.Secondary)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) scrollDownSecondary() error {
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollDownView(gui.Views.Secondary)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) scrollUpConfirmationPanel() error {
|
2021-04-04 15:51:59 +02:00
|
|
|
if gui.Views.Confirmation.Editable {
|
2020-03-29 01:31:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
2021-04-02 10:20:40 +02:00
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollUpView(gui.Views.Confirmation)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) scrollDownConfirmationPanel() error {
|
2021-04-04 15:51:59 +02:00
|
|
|
if gui.Views.Confirmation.Editable {
|
2020-03-29 01:31:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
2021-04-02 10:20:40 +02:00
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
return gui.scrollDownView(gui.Views.Confirmation)
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2021-04-02 10:20:40 +02:00
|
|
|
func (gui *Gui) handleRefresh() error {
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
2020-03-29 01:31:34 +02:00
|
|
|
}
|
|
|
|
|
2022-01-06 13:05:18 +02:00
|
|
|
func (gui *Gui) backgroundFetch() (err error) {
|
2022-01-16 05:46:53 +02:00
|
|
|
err = gui.git.Sync.Fetch(git_commands.FetchOptions{Background: true})
|
2022-01-06 13:05:18 +02:00
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
_ = gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
2022-01-06 13:05:18 +02:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-22 04:07:03 +02:00
|
|
|
func (gui *Gui) handleCopySelectedSideContextItemToClipboard() error {
|
|
|
|
// important to note that this assumes we've selected an item in a side context
|
2020-08-22 07:56:30 +02:00
|
|
|
itemId := gui.getSideContextSelectedItemId()
|
2020-08-22 04:07:03 +02:00
|
|
|
|
2020-08-22 07:56:30 +02:00
|
|
|
if itemId == "" {
|
2020-08-22 04:07:03 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
gui.c.LogAction(gui.c.Tr.Actions.CopyToClipboard)
|
2022-02-06 04:42:17 +02:00
|
|
|
if err := gui.os.CopyToClipboard(itemId); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.Error(err)
|
2020-11-21 08:15:43 +02:00
|
|
|
}
|
|
|
|
|
2020-12-24 11:21:54 +02:00
|
|
|
truncatedItemId := utils.TruncateWithEllipsis(strings.Replace(itemId, "\n", " ", -1), 50)
|
2020-11-21 08:15:43 +02:00
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
gui.c.Toast(fmt.Sprintf("'%s' %s", truncatedItemId, gui.c.Tr.LcCopiedToClipboard))
|
2020-11-21 08:15:43 +02:00
|
|
|
|
|
|
|
return nil
|
2020-08-22 04:07:03 +02:00
|
|
|
}
|