summaryrefslogtreecommitdiffstats
path: root/pkg/gui/tasks_adapter.go
blob: 69b64d7b12f454a027fba4fc944ed4aaedc564ab (plain)
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
package gui

import (
	"io"
	"os/exec"
	"strings"

	"github.com/jesseduffield/gocui"
	"github.com/jesseduffield/lazygit/pkg/tasks"
)

func (gui *Gui) newCmdTask(view *gocui.View, cmd *exec.Cmd, prefix string) error {
	cmdStr := strings.Join(cmd.Args, " ")
	gui.c.Log.WithField(
		"command",
		cmdStr,
	).Debug("RunCommand")

	manager := gui.getManager(view)

	start := func() (*exec.Cmd, io.Reader) {
		r, err := cmd.StdoutPipe()
		if err != nil {
			gui.c.Log.Error(err)
		}
		cmd.Stderr = cmd.Stdout

		if err := cmd.Start(); err != nil {
			gui.c.Log.Error(err)
		}

		return cmd, r
	}

	linesToRead := gui.linesToReadFromCmdTask(view)
	if err := manager.NewTask(manager.NewCmdTask(start, prefix, linesToRead, nil), cmdStr); err != nil {
		gui.c.Log.Error(err)
	}

	return nil
}

func (gui *Gui) newStringTask(view *gocui.View, str string) error {
	// using str so that if rendering the exact same thing we don't reset the origin
	return gui.newStringTaskWithKey(view, str, str)
}

func (gui *Gui) newStringTaskWithoutScroll(view *gocui.View, str string) error {
	manager := gui.getManager(view)

	f := func(stop chan struct{}) error {
		gui.setViewContent(view, str)
		return nil
	}

	// Using empty key so that on subsequent calls we won't reset the view's origin.
	// Note this means that we will be scrolling back to the top if we're switching from a different key
	if err := manager.NewTask(f, ""); err != nil {
		return err
	}

	return nil
}

func (gui *Gui) newStringTaskWithScroll(view *gocui.View, str string, originX int, originY int) error {
	manager := gui.getManager(view)

	f := func(stop chan struct{}) error {
		gui.setViewContent(view, str)
		_ = view.SetOrigin(originX, originY)
		return nil
	}

	if err := manager.NewTask(f, ""); err != nil {
		return err
	}

	return nil
}

func (gui *Gui) newStringTaskWithKey(view *gocui.View, str string, key string) error {
	manager := gui.getManager(view)

	f := func(stop chan struct{}) error {
		return gui.renderString(view, str)
	}

	if err := manager.NewTask(f, key); err != nil {
		return err
	}

	return nil
}

func (gui *Gui) getManager(view *gocui.View) *tasks.ViewBufferManager {
	manager, ok := gui.viewBufferManagerMap[view.Name()]
	if !ok {
		manager = tasks.NewViewBufferManager(
			gui.Log,
			view,
			func() {
				// we could clear here, but that actually has the effect of causing a flicker
				// where the view may contain no content momentarily as the gui refreshes.
				// Instead, we're rewinding the write pointer so that we will just start
				// overwriting the existing content from the top down. Once we've reached
				// the end of the content do display, we call view.FlushStaleCells() to
				// clear out the remaining content from the previous render.
				view.Reset()
			},
			func() {
				gui.render()
			},
			func() {
				// Need to check if the content of the view is well past the origin.
				linesHeight := view.ViewLinesHeight()
				_, originY := view.Origin()
				if linesHeight < originY {
					newOriginY := linesHeight

					err := view.SetOrigin(0, newOriginY)
					if err != nil {
						panic(err)
					}
				}

				view.FlushStaleCells()
			},
			func() {
				_ = view.SetOrigin(0, 0)
			},
		)
		gui.viewBufferManagerMap[view.Name()] = manager
	}

	return manager
}