better way of rendering to the main view

pull/392/head
Jesse Duffield 2 years ago
parent 5ffbeae3e8
commit 52040d1d99

@ -105,7 +105,7 @@ func (c *Container) Attach() (*exec.Cmd, error) {
}
// Top returns process information
func (c *Container) Top() (container.ContainerTopOKBody, error) {
func (c *Container) Top(ctx context.Context) (container.ContainerTopOKBody, error) {
detail, err := c.Inspect()
if err != nil {
return container.ContainerTopOKBody{}, err
@ -116,7 +116,7 @@ func (c *Container) Top() (container.ContainerTopOKBody, error) {
return container.ContainerTopOKBody{}, errors.New("container is not running")
}
return c.Client.ContainerTop(context.Background(), c.ID, []string{})
return c.Client.ContainerTop(ctx, c.ID, []string{})
}
// PruneContainers prunes containers
@ -131,8 +131,8 @@ func (c *Container) Inspect() (dockerTypes.ContainerJSON, error) {
}
// RenderTop returns details about the container
func (c *Container) RenderTop() (string, error) {
result, err := c.Top()
func (c *Container) RenderTop(ctx context.Context) (string, error) {
result, err := c.Top(ctx)
if err != nil {
return "", err
}

@ -1,6 +1,7 @@
package commands
import (
"context"
"fmt"
"io/ioutil"
"os"
@ -65,6 +66,15 @@ func (c *OSCommand) RunCommandWithOutput(command string) (string, error) {
return output, err
}
// RunCommandWithOutput wrapper around commands returning their output and error
func (c *OSCommand) RunCommandWithOutputContext(ctx context.Context, command string) (string, error) {
cmd := c.ExecutableFromStringContext(ctx, command)
before := time.Now()
output, err := sanitisedCommandOutput(cmd.Output())
c.Log.Warn(fmt.Sprintf("'%s': %s", command, time.Since(before)))
return output, err
}
// RunExecutableWithOutput runs an executable file and returns its output
func (c *OSCommand) RunExecutableWithOutput(cmd *exec.Cmd) (string, error) {
return sanitisedCommandOutput(cmd.CombinedOutput())
@ -79,10 +89,15 @@ func (c *OSCommand) RunExecutable(cmd *exec.Cmd) error {
// ExecutableFromString takes a string like `docker ps -a` and returns an executable command for it
func (c *OSCommand) ExecutableFromString(commandStr string) *exec.Cmd {
splitCmd := str.ToArgv(commandStr)
// c.Log.Info(splitCmd)
return c.command(splitCmd[0], splitCmd[1:]...)
}
// Same as ExecutableFromString but cancellable via a context
func (c *OSCommand) ExecutableFromStringContext(ctx context.Context, commandStr string) *exec.Cmd {
splitCmd := str.ToArgv(commandStr)
return exec.CommandContext(ctx, splitCmd[0], splitCmd[1:]...)
}
// RunCommand runs a command and just returns the error
func (c *OSCommand) RunCommand(command string) error {
_, err := c.RunCommandWithOutput(command)

@ -1,10 +1,9 @@
package commands
import (
"context"
"os/exec"
"github.com/docker/docker/api/types/container"
dockerTypes "github.com/docker/docker/api/types"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/sirupsen/logrus"
@ -58,11 +57,6 @@ func (s *Service) Attach() (*exec.Cmd, error) {
return s.Container.Attach()
}
// Top returns process information
func (s *Service) Top() (container.ContainerTopOKBody, error) {
return s.Container.Top()
}
// ViewLogs attaches to a subprocess viewing the service's logs
func (s *Service) ViewLogs() (*exec.Cmd, error) {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ViewServiceLogs
@ -78,12 +72,12 @@ func (s *Service) ViewLogs() (*exec.Cmd, error) {
}
// RenderTop renders the process list of the service
func (s *Service) RenderTop() (string, error) {
func (s *Service) RenderTop(ctx context.Context) (string, error) {
templateString := s.OSCommand.Config.UserConfig.CommandTemplates.ServiceTop
command := utils.ApplyTemplate(
templateString,
s.DockerCommand.NewCommandObject(CommandObject{Service: s}),
)
return s.OSCommand.RunCommandWithOutput(command)
return s.OSCommand.RunCommandWithOutputContext(ctx, command)
}

@ -12,16 +12,19 @@ import (
"github.com/docker/docker/pkg/stdcopy"
"github.com/fatih/color"
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
)
func (gui *Gui) renderContainerLogsToMain(container *commands.Container) error {
mainView := gui.Views.Main
mainView.Autoscroll = true
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
return gui.T.NewTickerTask(time.Millisecond*200, nil, func(stop, notifyStopped chan struct{}) {
gui.renderContainerLogsToMainAux(container, stop, notifyStopped)
func (gui *Gui) renderContainerLogsToMain(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
gui.renderContainerLogsToMainAux(container, stop, notifyStopped)
},
Duration: time.Millisecond * 200,
Before: nil,
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: true,
})
}

@ -1,6 +1,7 @@
package gui
import (
"context"
"encoding/json"
"fmt"
"strings"
@ -14,6 +15,7 @@ import (
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
"github.com/jesseduffield/lazydocker/pkg/gui/types"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/samber/lo"
)
@ -119,28 +121,10 @@ func sortContainers(a *commands.Container, b *commands.Container, legacySort boo
return containerStates[a.Container.State] < containerStates[b.Container.State]
}
type MainRenderTask struct {
Wrap bool
Autoscroll bool
StrContent string
// TicketTask
}
func (gui *Gui) renderContainerEnv(container *commands.Container) error {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
return gui.T.NewTask(func(stop chan struct{}) {
_ = gui.RenderStringMain(gui.containerEnv(container))
})
func (gui *Gui) renderContainerEnv(container *commands.Container) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string { return gui.containerEnv(container) })
}
// I want a struct that gets returned explaining what needs to happen. So for example,
// autoscroll: true, wrap: true, content: string
func (gui *Gui) containerEnv(container *commands.Container) string {
if !container.DetailsLoaded() {
return gui.Tr.WaitingForContainerInfo
@ -172,14 +156,8 @@ func (gui *Gui) containerEnv(container *commands.Container) string {
return output
}
func (gui *Gui) renderContainerConfig(container *commands.Container) error {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
return gui.T.NewTask(func(stop chan struct{}) {
_ = gui.RenderStringMain(gui.containerConfigStr(container))
})
func (gui *Gui) renderContainerConfig(container *commands.Container) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string { return gui.containerConfigStr(container) })
}
func (gui *Gui) containerConfigStr(container *commands.Container) string {
@ -232,35 +210,48 @@ func (gui *Gui) containerConfigStr(container *commands.Container) string {
return output
}
func (gui *Gui) renderContainerStats(container *commands.Container) error {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
return gui.T.NewTickerTask(time.Second, func(stop chan struct{}) { gui.clearMainView() }, func(stop, notifyStopped chan struct{}) {
width, _ := mainView.Size()
contents, err := presentation.RenderStats(gui.Config.UserConfig, container, width)
if err != nil {
_ = gui.createErrorPanel(err.Error())
}
func (gui *Gui) renderContainerStats(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
contents, err := presentation.RenderStats(gui.Config.UserConfig, container, gui.Views.Main.Width())
if err != nil {
_ = gui.createErrorPanel(err.Error())
}
gui.reRenderStringMain(contents)
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Wrap: false, // wrapping looks bad here so we're overriding the config value
Autoscroll: false,
})
}
func (gui *Gui) renderContainerTop(container *commands.Container) error {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
// TODO: remove this and just use a context
func stopIntoCtx(stop chan struct{}) context.Context {
ctx, ctxCancel := context.WithCancel(context.Background())
go func() {
<-stop
ctxCancel()
}()
return ctx
}
return gui.T.NewTickerTask(time.Second, func(stop chan struct{}) { gui.clearMainView() }, func(stop, notifyStopped chan struct{}) {
contents, err := container.RenderTop()
if err != nil {
gui.reRenderStringMain(err.Error())
}
func (gui *Gui) renderContainerTop(container *commands.Container) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
ctx := stopIntoCtx(stop)
contents, err := container.RenderTop(ctx)
if err != nil {
gui.RenderStringMain(err.Error())
}
gui.reRenderStringMain(contents)
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: false,
})
}

@ -36,7 +36,7 @@ type Gui struct {
Config *config.AppConfig
Tr *i18n.TranslationSet
statusManager *statusManager
T *tasks.TaskManager
taskManager *tasks.TaskManager
ErrorChan chan error
Views Views
@ -135,7 +135,7 @@ func NewGui(log *logrus.Entry, dockerCommand *commands.DockerCommand, oSCommand
Config: config,
Tr: tr,
statusManager: &statusManager{},
T: tasks.NewTaskManager(log, tr),
taskManager: tasks.NewTaskManager(log, tr),
ErrorChan: errorChan,
}
@ -173,7 +173,7 @@ func (gui *Gui) goEvery(interval time.Duration, function func() error) {
// Run setup the gui with keybindings and start the mainloop
func (gui *Gui) Run() error {
// closing our task manager which in turn closes the current task if there is any, so we aren't leaving processes lying around after closing lazydocker
defer gui.T.Close()
defer gui.taskManager.Close()
g, err := gocui.NewGui(gocui.OutputTrue, OverlappingEdges, gocui.NORMAL, false, map[rune]string{})
if err != nil {

@ -13,6 +13,7 @@ import (
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
"github.com/jesseduffield/lazydocker/pkg/gui/types"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/samber/lo"
)
@ -25,11 +26,9 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
GetMainTabs: func() []panels.MainTab[*commands.Image] {
return []panels.MainTab[*commands.Image]{
{
Key: "config",
Title: gui.Tr.ConfigTitle,
Render: func(image *commands.Image) error {
return gui.renderImageConfig(image)
},
Key: "config",
Title: gui.Tr.ConfigTitle,
Render: gui.renderImageConfigTask,
},
}
},
@ -66,13 +65,11 @@ func (gui *Gui) getImagesPanel() *panels.SideListPanel[*commands.Image] {
}
}
func (gui *Gui) renderImageConfig(image *commands.Image) error {
return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = false // don't care what your config is this page is ugly without wrapping
_ = gui.RenderStringMain(gui.imageConfigStr(image))
func (gui *Gui) renderImageConfigTask(image *commands.Image) tasks.TaskFunc {
return gui.NewRenderStringTask(RenderStringTaskOpts{
GetStrContent: func() string { return gui.imageConfigStr(image) },
Autoscroll: false,
Wrap: false, // don't care what your config is this page is ugly without wrapping
})
}

@ -1,6 +1,9 @@
package panels
import "github.com/samber/lo"
import (
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/samber/lo"
)
// A 'context' generally corresponds to an item and the tab in the main panel that we're
// displaying. So if we switch to a new item, or change the tab in the panel panel
@ -24,7 +27,7 @@ type MainTab[T any] struct {
// title of the tab, rendered in the main view
Title string
// function to render the content of the tab
Render func(item T) error
Render func(item T) tasks.TaskFunc
}
func (self *ContextState[T]) GetMainTabTitles() []string {

@ -63,7 +63,7 @@ var _ ISideListPanel = &SideListPanel[int]{}
type IGui interface {
HandleClick(v *gocui.View, itemCount int, selectedLine *int, handleSelect func() error) error
RenderStringMain(message string) error
RenderStringMain(message string)
FocusY(selectedLine int, itemCount int, view *gocui.View)
ShouldRefresh(contextKey string) bool
GetMainView() *gocui.View
@ -72,6 +72,8 @@ type IGui interface {
FilterString(view *gocui.View) string
IgnoreStrings() []string
Update(func() error)
QueueTask(f func(stop chan struct{})) error
}
func (self *SideListPanel[T]) HandleClick() error {
@ -105,7 +107,8 @@ func (self *SideListPanel[T]) HandleSelect() error {
}
if self.NoItemsMessage != "" {
return self.Gui.RenderStringMain(self.NoItemsMessage)
// TODO: use task for this
self.Gui.RenderStringMain(self.NoItemsMessage)
}
return nil
@ -130,7 +133,9 @@ func (self *SideListPanel[T]) renderContext(item T) error {
mainView.Tabs = self.ContextState.GetMainTabTitles()
mainView.TabIndex = self.ContextState.mainTabIdx
return self.ContextState.GetCurrentMainTab().Render(item)
task := self.ContextState.GetCurrentMainTab().Render(item)
return self.Gui.QueueTask(task)
}
func (self *SideListPanel[T]) GetSelectedItem() (T, error) {

@ -14,18 +14,20 @@ import (
)
func GetContainerDisplayStrings(container *commands.Container) []string {
image := strings.TrimPrefix(container.Container.Image, "sha256:")
return []string{
getContainerDisplayStatus(container),
getContainerDisplaySubstatus(container),
container.Name,
getDisplayCPUPerc(container),
utils.ColoredString(image, color.FgMagenta),
utils.ColoredString(displayPorts(container), color.FgYellow),
utils.ColoredString(displayContainerImage(container), color.FgMagenta),
}
}
func displayContainerImage(container *commands.Container) string {
return strings.TrimPrefix(container.Container.Image, "sha256:")
}
func displayPorts(c *commands.Container) string {
portStrings := lo.Map(c.Container.Ports, func(port dockerTypes.Port, _ int) string {
if port.PublicPort == 0 {

@ -14,6 +14,7 @@ func GetServiceDisplayStrings(service *commands.Service) []string {
service.Name,
"",
"",
"",
}
}
@ -24,5 +25,6 @@ func GetServiceDisplayStrings(service *commands.Service) []string {
service.Name,
getDisplayCPUPerc(container),
utils.ColoredString(displayPorts(container), color.FgYellow),
utils.ColoredString(displayContainerImage(container), color.FgMagenta),
}
}

@ -10,6 +10,7 @@ import (
"github.com/jesseduffield/lazydocker/pkg/commands"
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/jesseduffield/yaml"
)
@ -89,14 +90,8 @@ func (gui *Gui) getProjectName() string {
return projectName
}
func (gui *Gui) renderCredits(_project *commands.Project) error {
return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
_ = gui.RenderStringMain(gui.creditsStr())
})
func (gui *Gui) renderCredits(_project *commands.Project) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string { return gui.creditsStr() })
}
func (gui *Gui) creditsStr() string {
@ -116,47 +111,40 @@ func (gui *Gui) creditsStr() string {
}, "\n\n")
}
func (gui *Gui) renderAllLogs(_project *commands.Project) error {
return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = true
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
gui.clearMainView()
cmd := gui.OSCommand.RunCustomCommand(
utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.AllLogs,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
),
)
cmd.Stdout = mainView
cmd.Stderr = mainView
gui.OSCommand.PrepareForChildren(cmd)
_ = cmd.Start()
go func() {
<-stop
if err := gui.OSCommand.Kill(cmd); err != nil {
gui.Log.Error(err)
}
}()
func (gui *Gui) renderAllLogs(_project *commands.Project) tasks.TaskFunc {
return gui.NewTask(TaskOpts{
Autoscroll: true,
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Func: func(stop chan struct{}) {
gui.clearMainView()
cmd := gui.OSCommand.RunCustomCommand(
utils.ApplyTemplate(
gui.Config.UserConfig.CommandTemplates.AllLogs,
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
),
)
cmd.Stdout = gui.Views.Main
cmd.Stderr = gui.Views.Main
gui.OSCommand.PrepareForChildren(cmd)
_ = cmd.Start()
go func() {
<-stop
if err := gui.OSCommand.Kill(cmd); err != nil {
gui.Log.Error(err)
}
}()
_ = cmd.Wait()
_ = cmd.Wait()
},
})
}
func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) error {
return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
config := gui.DockerCommand.DockerComposeConfig()
_ = gui.RenderStringMain(config)
})
func (gui *Gui) renderDockerComposeConfig(_project *commands.Project) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string { return gui.DockerCommand.DockerComposeConfig() })
}
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {

@ -11,6 +11,7 @@ import (
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
"github.com/jesseduffield/lazydocker/pkg/gui/types"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/samber/lo"
)
@ -79,50 +80,51 @@ func (gui *Gui) getServicesPanel() *panels.SideListPanel[*commands.Service] {
}
}
func (gui *Gui) renderServiceContainerConfig(service *commands.Service) error {
func (gui *Gui) renderServiceContainerConfig(service *commands.Service) tasks.TaskFunc {
if service.Container == nil {
return gui.RenderStringMain(gui.Tr.NoContainer)
return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
}
return gui.renderContainerConfig(service.Container)
}
func (gui *Gui) renderServiceContainerEnv(service *commands.Service) error {
func (gui *Gui) renderServiceContainerEnv(service *commands.Service) tasks.TaskFunc {
if service.Container == nil {
return gui.RenderStringMain(gui.Tr.NoContainer)
return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
}
return gui.renderContainerEnv(service.Container)
}
func (gui *Gui) renderServiceStats(service *commands.Service) error {
func (gui *Gui) renderServiceStats(service *commands.Service) tasks.TaskFunc {
if service.Container == nil {
return nil
return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainer })
}
return gui.renderContainerStats(service.Container)
}
func (gui *Gui) renderServiceTop(service *commands.Service) error {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
return gui.T.NewTickerTask(time.Second, func(stop chan struct{}) { gui.clearMainView() }, func(stop, notifyStopped chan struct{}) {
contents, err := service.RenderTop()
if err != nil {
gui.reRenderStringMain(err.Error())
}
func (gui *Gui) renderServiceTop(service *commands.Service) tasks.TaskFunc {
return gui.NewTickerTask(TickerTaskOpts{
Func: func(stop, notifyStopped chan struct{}) {
ctx := stopIntoCtx(stop)
contents, err := service.RenderTop(ctx)
if err != nil {
gui.RenderStringMain(err.Error())
}
gui.reRenderStringMain(contents)
gui.reRenderStringMain(contents)
},
Duration: time.Second,
Before: func(stop chan struct{}) { gui.clearMainView() },
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
Autoscroll: false,
})
}
func (gui *Gui) renderServiceLogs(service *commands.Service) error {
func (gui *Gui) renderServiceLogs(service *commands.Service) tasks.TaskFunc {
if service.Container == nil {
return gui.T.NewTask(func(stop chan struct{}) {
gui.reRenderStringMain(gui.Tr.NoContainerForService)
})
return gui.NewSimpleRenderStringTask(func() string { return gui.Tr.NoContainerForService })
}
return gui.renderContainerLogsToMain(service.Container)

@ -0,0 +1,100 @@
package gui
import (
"time"
"github.com/jesseduffield/lazydocker/pkg/tasks"
)
func (gui *Gui) QueueTask(f func(stop chan struct{})) error {
return gui.taskManager.NewTask(f)
}
type RenderStringTaskOpts struct {
Autoscroll bool
Wrap bool
GetStrContent func() string
}
type TaskOpts struct {
Autoscroll bool
Wrap bool
Func func(stop chan struct{})
}
type TickerTaskOpts struct {
Duration time.Duration
Before func(stop chan struct{})
Func func(stop, notifyStopped chan struct{})
Autoscroll bool
Wrap bool
}
func (gui *Gui) NewRenderStringTask(opts RenderStringTaskOpts) tasks.TaskFunc {
taskOpts := TaskOpts{
Autoscroll: opts.Autoscroll,
Wrap: opts.Wrap,
Func: func(stop chan struct{}) {
gui.RenderStringMain(opts.GetStrContent())
},
}
return gui.NewTask(taskOpts)
}
// assumes it's cheap to obtain the content (otherwise we would pass a function that returns the content)
func (gui *Gui) NewSimpleRenderStringTask(getContent func() string) tasks.TaskFunc {
return gui.NewRenderStringTask(RenderStringTaskOpts{
GetStrContent: getContent,
Autoscroll: false,
Wrap: gui.Config.UserConfig.Gui.WrapMainPanel,
})
}
func (gui *Gui) NewTask(opts TaskOpts) tasks.TaskFunc {
return func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = opts.Autoscroll
mainView.Wrap = opts.Wrap
opts.Func(stop)
}
}
// NewTickerTask is a convenience function for making a new task that repeats some action once per e.g. second
// the before function gets called after the lock is obtained, but before the ticker starts.
// if you handle a message on the stop channel in f() you need to send a message on the notifyStopped channel because returning is not sufficient. Here, unlike in a regular task, simply returning means we're now going to wait till the next tick to run again.
func (gui *Gui) NewTickerTask(opts TickerTaskOpts) tasks.TaskFunc {
notifyStopped := make(chan struct{}, 10)
task := func(stop chan struct{}) {
if opts.Before != nil {
opts.Before(stop)
}
tickChan := time.NewTicker(opts.Duration)
defer tickChan.Stop()
// calling f first so that we're not waiting for the first tick
opts.Func(stop, notifyStopped)
for {
select {
case <-notifyStopped:
gui.Log.Info("exiting ticker task due to notifyStopped channel")
return
case <-stop:
gui.Log.Info("exiting ticker task due to stopped cahnnel")
return
case <-tickChan.C:
gui.Log.Info("running ticker task again")
opts.Func(stop, notifyStopped)
}
}
}
taskOpts := TaskOpts{
Autoscroll: opts.Autoscroll,
Wrap: opts.Wrap,
Func: task,
}
return gui.NewTask(taskOpts)
}

@ -145,8 +145,8 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error {
return nil
}
func (gui *Gui) RenderStringMain(s string) error {
return gui.renderString(gui.g, "main", s)
func (gui *Gui) RenderStringMain(s string) {
_ = gui.renderString(gui.g, "main", s)
}
// reRenderString sets the main view's content, without changing its origin

@ -10,6 +10,7 @@ import (
"github.com/jesseduffield/lazydocker/pkg/gui/panels"
"github.com/jesseduffield/lazydocker/pkg/gui/presentation"
"github.com/jesseduffield/lazydocker/pkg/gui/types"
"github.com/jesseduffield/lazydocker/pkg/tasks"
"github.com/jesseduffield/lazydocker/pkg/utils"
"github.com/samber/lo"
)
@ -52,14 +53,8 @@ func (gui *Gui) getVolumesPanel() *panels.SideListPanel[*commands.Volume] {
}
}
func (gui *Gui) renderVolumeConfig(volume *commands.Volume) error {
return gui.T.NewTask(func(stop chan struct{}) {
mainView := gui.Views.Main
mainView.Autoscroll = false
mainView.Wrap = gui.Config.UserConfig.Gui.WrapMainPanel
_ = gui.RenderStringMain(gui.volumeConfigStr(volume))
})
func (gui *Gui) renderVolumeConfig(volume *commands.Volume) tasks.TaskFunc {
return gui.NewSimpleRenderStringTask(func() string { return gui.volumeConfigStr(volume) })
}
func (gui *Gui) volumeConfigStr(volume *commands.Volume) string {

@ -27,6 +27,8 @@ type Task struct {
f func(chan struct{})
}
type TaskFunc func(stop chan struct{})
func NewTaskManager(log *logrus.Entry, translationSet *i18n.TranslationSet) *TaskManager {
return &TaskManager{Log: log, Tr: translationSet}
}

Loading…
Cancel
Save