mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Merge pull request #680 from noxer/master
Make the cmdrunner more interactive
This commit is contained in:
commit
abfdad01fe
@ -7,24 +7,30 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFocusable = false
|
||||
defaultFocusable = true
|
||||
defaultTitle = "CmdRunner"
|
||||
)
|
||||
|
||||
// Settings for the cmdrunner widget
|
||||
type Settings struct {
|
||||
common *cfg.Common
|
||||
|
||||
args []string `help:"The arguments to the command, with each item as an element in an array. Example: for curl -I cisco.com, the arguments array would be ['-I', 'cisco.com']."`
|
||||
cmd string `help:"The terminal command to be run, withouth the arguments. Ie: ping, whoami, curl."`
|
||||
args []string `help:"The arguments to the command, with each item as an element in an array. Example: for curl -I cisco.com, the arguments array would be ['-I', 'cisco.com']."`
|
||||
cmd string `help:"The terminal command to be run, withouth the arguments. Ie: ping, whoami, curl."`
|
||||
tail bool `help:"Automatically scroll to the end of the command output."`
|
||||
maxLines int `help:"Maximum number of lines kept in the buffer."`
|
||||
}
|
||||
|
||||
// NewSettingsFromYAML loads the cmdrunner portion of the WTF config
|
||||
func NewSettingsFromYAML(name string, moduleConfig *config.Config, globalConfig *config.Config) *Settings {
|
||||
|
||||
settings := Settings{
|
||||
common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, moduleConfig, globalConfig),
|
||||
|
||||
args: utils.ToStrs(moduleConfig.UList("args")),
|
||||
cmd: moduleConfig.UString("cmd"),
|
||||
args: utils.ToStrs(moduleConfig.UList("args")),
|
||||
cmd: moduleConfig.UString("cmd"),
|
||||
tail: moduleConfig.UBool("tail"),
|
||||
maxLines: moduleConfig.UInt("maxLines", 256),
|
||||
}
|
||||
|
||||
return &settings
|
||||
|
@ -1,21 +1,25 @@
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/rivo/tview"
|
||||
"github.com/wtfutil/wtf/utils"
|
||||
"github.com/wtfutil/wtf/view"
|
||||
)
|
||||
|
||||
// Widget contains the data for this widget
|
||||
type Widget struct {
|
||||
view.TextWidget
|
||||
|
||||
args []string
|
||||
cmd string
|
||||
settings *Settings
|
||||
|
||||
m sync.Mutex
|
||||
buffer *bytes.Buffer
|
||||
running bool
|
||||
}
|
||||
|
||||
// NewWidget creates a new instance of the widget
|
||||
@ -23,18 +27,18 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
|
||||
widget := Widget{
|
||||
TextWidget: view.NewTextWidget(app, settings.common),
|
||||
|
||||
args: settings.args,
|
||||
cmd: settings.cmd,
|
||||
settings: settings,
|
||||
buffer: &bytes.Buffer{},
|
||||
}
|
||||
|
||||
widget.View.SetWrap(true)
|
||||
widget.View.SetScrollable(true)
|
||||
|
||||
return &widget
|
||||
}
|
||||
|
||||
func (widget *Widget) content() (string, string, bool) {
|
||||
result := widget.execute()
|
||||
result := widget.buffer.String()
|
||||
|
||||
ansiTitle := tview.TranslateANSI(widget.CommonSettings().Title)
|
||||
if ansiTitle == defaultTitle {
|
||||
@ -47,23 +51,86 @@ func (widget *Widget) content() (string, string, bool) {
|
||||
|
||||
// Refresh executes the command and updates the view with the results
|
||||
func (widget *Widget) Refresh() {
|
||||
widget.m.Lock()
|
||||
defer widget.m.Unlock()
|
||||
|
||||
widget.execute()
|
||||
widget.Redraw(widget.content)
|
||||
if widget.settings.tail {
|
||||
widget.View.ScrollToEnd()
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string representation of the widget
|
||||
func (widget *Widget) String() string {
|
||||
args := strings.Join(widget.args, " ")
|
||||
args := strings.Join(widget.settings.args, " ")
|
||||
|
||||
if args != "" {
|
||||
return fmt.Sprintf(" %s %s ", widget.cmd, args)
|
||||
return fmt.Sprintf(" %s %s ", widget.settings.cmd, args)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" %s ", widget.cmd)
|
||||
return fmt.Sprintf(" %s ", widget.settings.cmd)
|
||||
}
|
||||
|
||||
func (widget *Widget) Write(p []byte) (n int, err error) {
|
||||
widget.m.Lock()
|
||||
defer widget.m.Unlock()
|
||||
|
||||
// Write the new data into the buffer
|
||||
n, err = widget.buffer.Write(p)
|
||||
|
||||
// Remove lines that exceed maxLines
|
||||
lines := widget.countLines()
|
||||
if lines > widget.settings.maxLines {
|
||||
widget.drainLines(lines - widget.settings.maxLines)
|
||||
}
|
||||
|
||||
// Redraw the widget
|
||||
widget.Redraw(widget.content)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/* -------------------- Unexported Functions -------------------- */
|
||||
|
||||
func (widget *Widget) execute() string {
|
||||
cmd := exec.Command(widget.cmd, widget.args...)
|
||||
return utils.ExecuteCommand(cmd)
|
||||
func (widget *Widget) execute() {
|
||||
// Make sure the command is not already running
|
||||
if widget.running {
|
||||
return
|
||||
}
|
||||
|
||||
// Reset the buffer
|
||||
widget.buffer.Reset()
|
||||
|
||||
// Indicate that the command is running
|
||||
widget.running = true
|
||||
|
||||
// Setup the command to run
|
||||
cmd := exec.Command(widget.settings.cmd, widget.settings.args...)
|
||||
cmd.Stdout = widget
|
||||
|
||||
// Run the command and wait for it to exit in another Go-routine
|
||||
go func() {
|
||||
err := cmd.Run()
|
||||
|
||||
// The command has exited, print any error messages
|
||||
widget.m.Lock()
|
||||
if err != nil {
|
||||
widget.buffer.WriteString(err.Error())
|
||||
}
|
||||
widget.running = false
|
||||
widget.m.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
// countLines counts the lines of data in the buffer
|
||||
func (widget *Widget) countLines() int {
|
||||
return bytes.Count(widget.buffer.Bytes(), []byte{'\n'})
|
||||
}
|
||||
|
||||
// drainLines removed the first n lines from the buffer
|
||||
func (widget *Widget) drainLines(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
widget.buffer.ReadBytes('\n')
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user