Almost feature complete
- Added reading of UCL scripts - Added pasteboard commands - Added ui:command which will define a proc at the top-level
This commit is contained in:
parent
7ae99b009b
commit
cae7509a76
12 changed files with 185 additions and 47 deletions
46
internal/common/ui/commandctrl/cmdpacks/modpb.go
Normal file
46
internal/common/ui/commandctrl/cmdpacks/modpb.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package cmdpacks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/pasteboardprovider"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
||||
type pbModule struct {
|
||||
pasteboardProvider *pasteboardprovider.Provider
|
||||
}
|
||||
|
||||
func (m pbModule) pbGet(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
s, ok := m.pasteboardProvider.ReadText()
|
||||
if !ok {
|
||||
return "", nil
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (m pbModule) pbPut(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var s string
|
||||
if err := args.Bind(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := m.pasteboardProvider.WriteText([]byte(s)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func modulePB(
|
||||
pasteboardProvider *pasteboardprovider.Provider,
|
||||
) ucl.Module {
|
||||
m := &pbModule{
|
||||
pasteboardProvider: pasteboardProvider,
|
||||
}
|
||||
|
||||
return ucl.Module{
|
||||
Name: "pb",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"get": m.pbGet,
|
||||
"put": m.pbPut,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -252,13 +252,36 @@ func (rs *rsModule) rsSet(ctx context.Context, args ucl.CallArgs) (_ any, err er
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TEMP
|
||||
// TEMP: attribute is always S
|
||||
if err := q.SetEvalItem(item.item, &types.AttributeValueMemberS{Value: val.String()}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.resultSet.SetDirty(item.idx, true)
|
||||
commandctrl.QueueRefresh(ctx)
|
||||
// END TEMP
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (rs *rsModule) rsDel(ctx context.Context, args ucl.CallArgs) (_ any, err error) {
|
||||
var (
|
||||
item itemProxy
|
||||
expr string
|
||||
)
|
||||
|
||||
if err := args.Bind(&item, &expr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q, err := queryexpr.Parse(expr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := q.DeleteAttribute(item.item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.resultSet.SetDirty(item.idx, true)
|
||||
commandctrl.QueueRefresh(ctx)
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
|
@ -279,6 +302,7 @@ func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module
|
|||
"next-page": m.rsNextPage,
|
||||
"union": m.rsUnion,
|
||||
"set": m.rsSet,
|
||||
"del": m.rsDel,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,20 @@ type uiModule struct {
|
|||
readController *controllers.TableReadController
|
||||
}
|
||||
|
||||
func (m *uiModule) uiCommand(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var (
|
||||
name string
|
||||
cmd ucl.Invokable
|
||||
)
|
||||
if err := args.Bind(&name, &cmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
invoker := commandctrl.GetInvoker(ctx)
|
||||
invoker.Inst().SetBuiltinInvokable(name, cmd)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *uiModule) uiPrompt(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var prompt string
|
||||
if err := args.Bind(&prompt); err != nil {
|
||||
|
|
@ -152,6 +166,7 @@ func moduleUI(
|
|||
return ucl.Module{
|
||||
Name: "ui",
|
||||
Builtins: map[string]ucl.BuiltinHandler{
|
||||
"command": m.uiCommand,
|
||||
"prompt": m.uiPrompt,
|
||||
"prompt-table": m.uiPromptTable,
|
||||
"confirm": m.uiConfirm,
|
||||
|
|
|
|||
|
|
@ -126,8 +126,8 @@ var keyAttributeProxyFields = &proxyInfo[models.KeyAttribute]{
|
|||
return fmt.Sprintf("KeyAttribute(%v,%v)", t.PartitionKey, t.SortKey)
|
||||
},
|
||||
fields: map[string]func(t models.KeyAttribute) ucl.Object{
|
||||
"PartitionKey": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.PartitionKey) },
|
||||
"SortKey": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.SortKey) },
|
||||
"PK": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.PartitionKey) },
|
||||
"SK": func(t models.KeyAttribute) ucl.Object { return ucl.StringObject(t.SortKey) },
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/providers/pasteboardprovider"
|
||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||
"github.com/pkg/errors"
|
||||
"ucl.lmika.dev/repl"
|
||||
|
|
@ -19,6 +20,7 @@ type StandardCommands struct {
|
|||
WriteController *controllers.TableWriteController
|
||||
ExportController *controllers.ExportController
|
||||
KeyBindingController *controllers.KeyBindingController
|
||||
PBProvider *pasteboardprovider.Provider
|
||||
|
||||
modUI ucl.Module
|
||||
}
|
||||
|
|
@ -30,6 +32,7 @@ func NewStandardCommands(
|
|||
writeController *controllers.TableWriteController,
|
||||
exportController *controllers.ExportController,
|
||||
keyBindingController *controllers.KeyBindingController,
|
||||
pbProvider *pasteboardprovider.Provider,
|
||||
) StandardCommands {
|
||||
modUI, ckbs := moduleUI(tableService, state, readController)
|
||||
keyBindingController.SetCustomKeyBindingSource(ckbs)
|
||||
|
|
@ -41,6 +44,7 @@ func NewStandardCommands(
|
|||
WriteController: writeController,
|
||||
ExportController: exportController,
|
||||
KeyBindingController: keyBindingController,
|
||||
PBProvider: pbProvider,
|
||||
modUI: modUI,
|
||||
}
|
||||
}
|
||||
|
|
@ -389,6 +393,7 @@ func (sc StandardCommands) InstOptions() []ucl.InstOption {
|
|||
return []ucl.InstOption{
|
||||
ucl.WithModule(moduleRS(sc.TableService, sc.State)),
|
||||
ucl.WithModule(sc.modUI),
|
||||
ucl.WithModule(modulePB(sc.PBProvider)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package commandctrl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
|
|
@ -136,7 +137,7 @@ func (c *CommandController) execute(ctx ExecContext, commandInput string) tea.Ms
|
|||
}
|
||||
|
||||
func (c *CommandController) ExecuteAndWait(ctx context.Context, commandInput string) (any, error) {
|
||||
return c.uclInst.Eval(ctx, commandInput)
|
||||
return c.uclInst.EvalString(ctx, commandInput)
|
||||
}
|
||||
|
||||
func (c *CommandController) Invoke(invokable ucl.Invokable, args []any) (msg tea.Msg) {
|
||||
|
|
@ -202,7 +203,47 @@ func (c *CommandController) lookupCommand(name string) Command {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandController) ExecuteFile(filename string) error {
|
||||
func (c *CommandController) LoadExtensions(ctx context.Context, baseDirs []string) error {
|
||||
log.Printf("loading extensions: %v", baseDirs)
|
||||
for _, baseDir := range baseDirs {
|
||||
baseDir = os.ExpandEnv(baseDir)
|
||||
descendIntoSubDirs := !strings.HasSuffix(baseDir, ".")
|
||||
|
||||
if stat, err := os.Stat(baseDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
} else if !stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("walking %v", baseDir)
|
||||
if err := filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if !descendIntoSubDirs && path != baseDir {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if strings.HasSuffix(info.Name(), ".ucl") {
|
||||
if err := c.ExecuteFile(ctx, path); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Printf("loaded %v\n", path)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandController) ExecuteFile(ctx context.Context, filename string) error {
|
||||
oldInteractive := c.interactive
|
||||
c.interactive = false
|
||||
defer func() {
|
||||
|
|
@ -211,27 +252,31 @@ func (c *CommandController) ExecuteFile(filename string) error {
|
|||
|
||||
baseFilename := filepath.Base(filename)
|
||||
|
||||
execCtx := execContext{ctrl: c}
|
||||
ctx = context.WithValue(context.Background(), commandCtlKey, &execCtx)
|
||||
|
||||
if rcFile, err := os.ReadFile(filename); err == nil {
|
||||
if err := c.executeFile(rcFile, baseFilename); err != nil {
|
||||
return errors.Wrapf(err, "error executing %v", filename)
|
||||
if err := c.executeFile(ctx, rcFile); err != nil {
|
||||
return errors.Wrapf(err, "error executing %v", baseFilename)
|
||||
}
|
||||
} else {
|
||||
return errors.Wrapf(err, "error loading %v", filename)
|
||||
return errors.Wrapf(err, "error loading %v", baseFilename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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))
|
||||
//}
|
||||
func (c *CommandController) executeFile(ctx context.Context, file []byte) error {
|
||||
if _, err := c.uclInst.Eval(ctx, bytes.NewReader(file), ucl.WithSubEnv()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CommandController) Inst() *ucl.Inst {
|
||||
return c.uclInst
|
||||
}
|
||||
|
||||
func (c *CommandController) cmdInvoker(ctx context.Context, name string, args ucl.CallArgs) (any, error) {
|
||||
command := c.lookupCommand(name)
|
||||
if command == nil {
|
||||
|
|
|
|||
|
|
@ -59,4 +59,5 @@ func QueueRefresh(ctx context.Context) {
|
|||
|
||||
type Invoker interface {
|
||||
Invoke(invokable ucl.Invokable, args []any) tea.Msg
|
||||
Inst() *ucl.Inst
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue