issue-9: added the RC file

Added the RC file which can be used to load commands on startup.  Also added the "echo"
command which can be used for debugging.
This commit is contained in:
Leon Mika 2022-08-28 10:54:25 +10:00
parent d9c9e5d845
commit 24304d21c3
8 changed files with 92 additions and 42 deletions

View file

@ -33,7 +33,7 @@ func main() {
ctrl := controllers.NewLogFileController(service, flag.Arg(0))
cmdController := commandctrl.NewCommandController()
//cmdController.AddCommands(&commandctrl.CommandContext{
//cmdController.AddCommands(&commandctrl.CommandList{
// Commands: map[string]commandctrl.Command{
// "cd": func(args []string) tea.Cmd {
// return ctrl.ChangePrefix(args[0])

View file

@ -48,7 +48,7 @@ func main() {
ctrl := controllers.New(service)
cmdController := commandctrl.NewCommandController()
cmdController.AddCommands(&commandctrl.CommandContext{
cmdController.AddCommands(&commandctrl.CommandList{
Commands: map[string]commandctrl.Command{
"cd": func(args []string) tea.Msg {
return ctrl.ChangePrefix(args[0])

View file

@ -1,9 +1,13 @@
package commandctrl
import (
"bufio"
"bytes"
tea "github.com/charmbracelet/bubbletea"
"github.com/pkg/errors"
"log"
"os"
"path/filepath"
"strings"
"github.com/lmika/audax/internal/common/ui/events"
@ -11,7 +15,7 @@ import (
)
type CommandController struct {
commandList *CommandContext
commandList *CommandList
}
func NewCommandController() *CommandController {
@ -20,7 +24,7 @@ func NewCommandController() *CommandController {
}
}
func (c *CommandController) AddCommands(ctx *CommandContext) {
func (c *CommandController) AddCommands(ctx *CommandList) {
ctx.parent = c.commandList
c.commandList = ctx
}
@ -35,6 +39,10 @@ func (c *CommandController) Prompt() tea.Msg {
}
func (c *CommandController) Execute(commandInput string) tea.Msg {
return c.execute(ExecContext{FromFile: false}, commandInput)
}
func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Msg {
input := strings.TrimSpace(commandInput)
if input == "" {
return nil
@ -43,31 +51,65 @@ func (c *CommandController) Execute(commandInput string) tea.Msg {
tokens := shellwords.Split(input)
command := c.lookupCommand(tokens[0])
if command == nil {
log.Println("No such command: ", tokens)
return events.Error(errors.New("no such command: " + tokens[0]))
}
return command(tokens[1:])
return command(ctx, tokens[1:])
}
func (c *CommandController) Alias(commandName string) Command {
return func(args []string) tea.Msg {
return func(ctx ExecContext, args []string) tea.Msg {
command := c.lookupCommand(commandName)
if command == nil {
log.Println("No such command: ", commandName)
return events.Error(errors.New("no such command: " + commandName))
}
return command(args)
return command(ctx, args)
}
}
func (c *CommandController) lookupCommand(name string) Command {
for ctx := c.commandList; ctx != nil; ctx = ctx.parent {
log.Printf("Looking in command list: %v", c.commandList)
if cmd, ok := ctx.Commands[name]; ok {
return cmd
}
}
return nil
}
func (c *CommandController) ExecuteFile(filename string) error {
baseFilename := filepath.Base(filename)
if rcFile, err := os.ReadFile(filename); err == nil {
if err := c.executeFile(rcFile, baseFilename); err != nil {
return errors.Wrapf(err, "error executing %v", filename)
}
} else {
return errors.Wrapf(err, "error loading %v", filename)
}
return nil
}
func (c *CommandController) executeFile(file []byte, filename string) error {
scnr := bufio.NewScanner(bytes.NewReader(file))
lineNo := 0
for scnr.Scan() {
lineNo++
line := strings.TrimSpace(scnr.Text())
if line == "" {
continue
} else if line[0] == '#' {
continue
}
msg := c.execute(ExecContext{FromFile: true}, line)
switch m := msg.(type) {
case events.ErrorMsg:
log.Printf("%v:%v: error - %v", filename, lineNo, m.Error())
case events.StatusMsg:
log.Printf("%v:%v: %v", filename, lineNo, string(m))
}
}
return scnr.Err()
}

View file

@ -1,16 +1,6 @@
package commandctrl
import "context"
type commandArgContextKeyType struct{}
var commandArgContextKey = commandArgContextKeyType{}
func WithCommandArgs(ctx context.Context, args []string) context.Context {
return context.WithValue(ctx, commandArgContextKey, args)
}
func CommandArgs(ctx context.Context) []string {
args, _ := ctx.Value(commandArgContextKey).([]string)
return args
type ExecContext struct {
// FromFile is true if the command is executed as part of a command
FromFile bool
}

View file

@ -2,16 +2,16 @@ package commandctrl
import tea "github.com/charmbracelet/bubbletea"
type Command func(args []string) tea.Msg
type Command func(ctx ExecContext, args []string) tea.Msg
func NoArgCommand(cmd tea.Cmd) Command {
return func(args []string) tea.Msg {
return func(ctx ExecContext, args []string) tea.Msg {
return cmd()
}
}
type CommandContext struct {
type CommandList struct {
Commands map[string]Command
parent *CommandContext
parent *CommandList
}

View file

@ -16,10 +16,12 @@ func NewKeyBindingController(service *keybindings.Service) *KeyBindingController
return &KeyBindingController{service: service}
}
func (kb *KeyBindingController) Rebind(bindingName string, newKey string) tea.Msg {
err := kb.service.Rebind(bindingName, newKey, false)
func (kb *KeyBindingController) Rebind(bindingName string, newKey string, force bool) tea.Msg {
err := kb.service.Rebind(bindingName, newKey, force)
if err == nil {
return events.SetStatus(fmt.Sprintf("Binding '%v' now bound to '%v'", bindingName, newKey))
} else if force {
return events.Error(errors.Wrapf(err, "cannot bind '%v' to '%v'", bindingName, newKey))
}
var keyAlreadyBoundErr keybindings.KeyAlreadyBoundError

View file

@ -20,6 +20,7 @@ import (
"github.com/lmika/audax/internal/dynamo-browse/ui/teamodels/utils"
"github.com/pkg/errors"
"log"
"os"
"strings"
)
@ -31,6 +32,8 @@ const (
ViewModeTableOnly = 4
ViewModeCount = 5
initRCFilename = "$HOME/.config/audax/dynamo-browse/init.rc"
)
type Model struct {
@ -69,17 +72,17 @@ func NewModel(
dialogPrompt := dialogprompt.New(statusAndPrompt)
tableSelect := tableselect.New(dialogPrompt, uiStyles)
cc.AddCommands(&commandctrl.CommandContext{
cc.AddCommands(&commandctrl.CommandList{
Commands: map[string]commandctrl.Command{
"quit": commandctrl.NoArgCommand(tea.Quit),
"table": func(args []string) tea.Msg {
"table": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
if len(args) == 0 {
return rc.ListTables()
} else {
return rc.ScanTable(args[0])
}
},
"export": func(args []string) tea.Msg {
"export": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
if len(args) == 0 {
return events.Error(errors.New("expected filename"))
}
@ -90,7 +93,7 @@ func NewModel(
// TEMP
"new-item": commandctrl.NoArgCommand(wc.NewItem),
"set-attr": func(args []string) tea.Msg {
"set-attr": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
if len(args) == 0 {
return events.Error(errors.New("expected field"))
}
@ -114,28 +117,35 @@ func NewModel(
return wc.SetAttributeValue(dtv.SelectedItemIndex(), itemType, args[0])
},
"del-attr": func(args []string) tea.Msg {
"del-attr": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
if len(args) == 0 {
return events.Error(errors.New("expected field"))
}
return wc.DeleteAttribute(dtv.SelectedItemIndex(), args[0])
},
"put": func(args []string) tea.Msg {
"put": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
return wc.PutItems()
},
"touch": func(args []string) tea.Msg {
"touch": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
return wc.TouchItem(dtv.SelectedItemIndex())
},
"noisy-touch": func(args []string) tea.Msg {
"noisy-touch": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
return wc.NoisyTouchItem(dtv.SelectedItemIndex())
},
"rebind": func(args []string) tea.Msg {
if len(args) != 2 {
return events.Error(errors.New("expected: name newKey"))
"echo": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
s := new(strings.Builder)
for _, arg := range args {
s.WriteString(arg)
}
return keyBindingController.Rebind(args[0], args[1])
return events.SetStatus(s.String())
},
"rebind": func(ctx commandctrl.ExecContext, args []string) tea.Msg {
if len(args) != 2 {
return events.Error(errors.New("expected: bindingName newKey"))
}
return keyBindingController.Rebind(args[0], args[1], ctx.FromFile)
},
// Aliases
@ -164,6 +174,12 @@ func NewModel(
}
func (m Model) Init() tea.Cmd {
// TODO: this should probably be moved somewhere else
rcFilename := os.ExpandEnv(initRCFilename)
if err := m.commandController.ExecuteFile(rcFilename); err != nil {
log.Println(err)
}
return m.tableReadController.Init
}

View file

@ -30,7 +30,7 @@ func NewModel(controller *controllers.SSMController, cmdController *commandctrl.
statusAndPrompt := statusandprompt.New(
layout.NewVBox(layout.LastChildFixedAt(17), ssmList, ssmdDetails), "", defaultStyles.StatusAndPrompt)
cmdController.AddCommands(&commandctrl.CommandContext{
cmdController.AddCommands(&commandctrl.CommandList{
Commands: map[string]commandctrl.Command{
"clone": func(args []string) tea.Msg {
if currentParam := ssmList.CurrentParameter(); currentParam != nil {