Started re-engineering the UCL command instance
This commit is contained in:
parent
94b58e2168
commit
17381f3d0b
9 changed files with 309 additions and 172 deletions
60
internal/common/ui/commandctrl/cmdpacks/stdcmds.go
Normal file
60
internal/common/ui/commandctrl/cmdpacks/stdcmds.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package cmdpacks
|
||||
|
||||
import (
|
||||
"context"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||
"ucl.lmika.dev/repl"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type StandardCommands struct {
|
||||
ReadController *controllers.TableReadController
|
||||
}
|
||||
|
||||
var cmdQuitDoc = repl.Doc{
|
||||
Brief: "Quits dynamo-browse",
|
||||
Usage: "quit",
|
||||
Detailed: `
|
||||
This will quit dynamo-browse immediately, without prompting to apply
|
||||
any changes.
|
||||
`,
|
||||
}
|
||||
|
||||
func (sc StandardCommands) cmdQuit(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
commandctrl.PostMsg(ctx, tea.Quit)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var cmdTableDoc = repl.Doc{
|
||||
Brief: "Prompt for table to scan",
|
||||
Usage: "table [NAME]",
|
||||
Args: []repl.ArgDoc{
|
||||
{Name: "name", Brief: "Name of the table to scan"},
|
||||
},
|
||||
Detailed: `
|
||||
If called with an argument, it will scan the table with that name and
|
||||
replace the current result set. If called without an argument, it will
|
||||
prompt for a table to scan.
|
||||
|
||||
This command is intended only for interactive sessions and is not suitable
|
||||
for scripting. The scan or table prompts will happen asynchronously.
|
||||
`,
|
||||
}
|
||||
|
||||
func (sc StandardCommands) cmdTable(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var tableName string
|
||||
if err := args.Bind(&tableName); err == nil {
|
||||
commandctrl.PostMsg(ctx, sc.ReadController.ScanTable(tableName))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
commandctrl.PostMsg(ctx, sc.ReadController.ListTables(false))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (sc StandardCommands) ConfigureUCL(ucl *ucl.Inst) {
|
||||
ucl.SetBuiltin("quit", sc.cmdQuit)
|
||||
ucl.SetBuiltin("table", sc.cmdTable)
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package commandctrl
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/pkg/errors"
|
||||
"log"
|
||||
|
|
@ -17,30 +18,42 @@ import (
|
|||
|
||||
const commandsCategory = "commands"
|
||||
|
||||
type cmdMessage struct {
|
||||
cmd string
|
||||
}
|
||||
|
||||
type CommandController struct {
|
||||
uclInst *ucl.Inst
|
||||
historyProvider IterProvider
|
||||
commandList *CommandList
|
||||
lookupExtensions []CommandLookupExtension
|
||||
completionProvider CommandCompletionProvider
|
||||
cmdChan chan cmdMessage
|
||||
msgChan chan tea.Msg
|
||||
interactive bool
|
||||
}
|
||||
|
||||
func NewCommandController(historyProvider IterProvider) *CommandController {
|
||||
func NewCommandController(historyProvider IterProvider, pkgs ...CommandPack) *CommandController {
|
||||
cc := &CommandController{
|
||||
historyProvider: historyProvider,
|
||||
commandList: nil,
|
||||
lookupExtensions: nil,
|
||||
cmdChan: make(chan cmdMessage),
|
||||
msgChan: make(chan tea.Msg),
|
||||
interactive: true,
|
||||
}
|
||||
cc.uclInst = ucl.New(
|
||||
ucl.WithOut(ucl.LineHandler(cc.printLine)),
|
||||
ucl.WithMissingBuiltinHandler(cc.cmdInvoker),
|
||||
ucl.WithModule(builtins.OS()),
|
||||
ucl.WithModule(builtins.FS(nil)),
|
||||
)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
pkg.ConfigureUCL(cc.uclInst)
|
||||
}
|
||||
|
||||
go cc.cmdLooper()
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
|
|
@ -101,17 +114,42 @@ func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Ms
|
|||
return nil
|
||||
}
|
||||
|
||||
res, err := c.uclInst.Eval(context.Background(), commandInput)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
select {
|
||||
case c.cmdChan <- cmdMessage{cmd: input}:
|
||||
// good
|
||||
default:
|
||||
return events.Error(errors.New("command currently running"))
|
||||
}
|
||||
|
||||
if teaMsg, ok := res.(teaMsgWrapper); ok {
|
||||
return teaMsg.msg
|
||||
}
|
||||
/*
|
||||
res, err := c.uclInst.Eval(context.Background(), commandInput)
|
||||
if err != nil {
|
||||
return events.Error(err)
|
||||
}
|
||||
|
||||
if teaMsg, ok := res.(teaMsgWrapper); ok {
|
||||
return teaMsg.msg
|
||||
}
|
||||
*/
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandController) cmdLooper() {
|
||||
ctx := context.WithValue(context.Background(), commandCtlKey, c)
|
||||
|
||||
for {
|
||||
select {
|
||||
case cmdChan := <-c.cmdChan:
|
||||
res, err := c.uclInst.Eval(ctx, cmdChan.cmd)
|
||||
if err != nil {
|
||||
c.postMessage(events.Error(err))
|
||||
} else if res != nil {
|
||||
c.postMessage(events.StatusMsg(fmt.Sprint(res)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommandController) Alias(commandName string) Command {
|
||||
return func(ctx ExecContext, args ucl.CallArgs) tea.Msg {
|
||||
command := c.lookupCommand(commandName)
|
||||
|
|
@ -158,13 +196,13 @@ func (c *CommandController) ExecuteFile(filename string) error {
|
|||
}
|
||||
|
||||
func (c *CommandController) executeFile(file []byte, filename string) error {
|
||||
msg := c.execute(ExecContext{FromFile: true}, string(file))
|
||||
switch m := msg.(type) {
|
||||
case events.ErrorMsg:
|
||||
log.Printf("%v: error - %v", filename, m.Error())
|
||||
case events.StatusMsg:
|
||||
log.Printf("%v: %v", filename, string(m))
|
||||
}
|
||||
//msg := c.execute(ExecContext{FromFile: true}, string(file))
|
||||
//switch m := msg.(type) {
|
||||
//case events.ErrorMsg:
|
||||
// log.Printf("%v: error - %v", filename, m.Error())
|
||||
//case events.StatusMsg:
|
||||
// log.Printf("%v: %v", filename, string(m))
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -194,6 +232,14 @@ func (c *CommandController) printLine(s string) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *CommandController) postMessage(msg tea.Msg) {
|
||||
if c.msgChan == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.msgChan <- msg
|
||||
}
|
||||
|
||||
type teaMsgWrapper struct {
|
||||
msg tea.Msg
|
||||
}
|
||||
|
|
|
|||
17
internal/common/ui/commandctrl/ctx.go
Normal file
17
internal/common/ui/commandctrl/ctx.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package commandctrl
|
||||
|
||||
import (
|
||||
"context"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type commandCtlKeyType struct{}
|
||||
|
||||
var commandCtlKey = commandCtlKeyType{}
|
||||
|
||||
func PostMsg(ctx context.Context, msg tea.Msg) {
|
||||
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
||||
if ok {
|
||||
cmdCtl.postMessage(msg)
|
||||
}
|
||||
}
|
||||
7
internal/common/ui/commandctrl/packs.go
Normal file
7
internal/common/ui/commandctrl/packs.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package commandctrl
|
||||
|
||||
import "ucl.lmika.dev/ucl"
|
||||
|
||||
type CommandPack interface {
|
||||
ConfigureUCL(ucl *ucl.Inst)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue