mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-21 23:01:03 +00:00
better way of rendering to the main view
This commit is contained in:
parent
5ffbeae3e8
commit
52040d1d99
17 changed files with 266 additions and 167 deletions
|
|
@ -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) tasks.TaskFunc {
|
||||
return gui.NewSimpleRenderStringTask(func() string { return gui.containerEnv(container) })
|
||||
}
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
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())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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
|
||||
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()
|
||||
|
||||
gui.clearMainView()
|
||||
cmd := gui.OSCommand.RunCustomCommand(
|
||||
utils.ApplyTemplate(
|
||||
gui.Config.UserConfig.CommandTemplates.AllLogs,
|
||||
gui.DockerCommand.NewCommandObject(commands.CommandObject{}),
|
||||
),
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
cmd.Stdout = mainView
|
||||
cmd.Stderr = mainView
|
||||
gui.OSCommand.PrepareForChildren(cmd)
|
||||
_ = cmd.Start()
|
||||
|
||||
gui.OSCommand.PrepareForChildren(cmd)
|
||||
_ = cmd.Start()
|
||||
go func() {
|
||||
<-stop
|
||||
if err := gui.OSCommand.Kill(cmd); err != nil {
|
||||
gui.Log.Error(err)
|
||||
}
|
||||
}()
|
||||
|
||||
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
|
||||
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())
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
|||
100
pkg/gui/tasks_adapter.go
Normal file
100
pkg/gui/tasks_adapter.go
Normal file
|
|
@ -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…
Add table
Reference in a new issue