ucl: added rs:set
This commit is contained in:
parent
5088009672
commit
7ae99b009b
|
@ -158,18 +158,18 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
keyBindingService := keybindings_service.NewService(keyBindings)
|
keyBindingService := keybindings_service.NewService(keyBindings)
|
||||||
keyBindingController := controllers.NewKeyBindingController(keyBindingService, scriptController)
|
keyBindingController := controllers.NewKeyBindingController(keyBindingService, nil)
|
||||||
|
|
||||||
commandController := commandctrl.NewCommandController(inputHistoryService,
|
stdCommands := cmdpacks.NewStandardCommands(
|
||||||
cmdpacks.StandardCommands{
|
tableService,
|
||||||
TableService: tableService,
|
state,
|
||||||
State: state,
|
tableReadController,
|
||||||
ReadController: tableReadController,
|
tableWriteController,
|
||||||
WriteController: tableWriteController,
|
exportController,
|
||||||
ExportController: exportController,
|
keyBindingController,
|
||||||
KeyBindingController: keyBindingController,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
commandController := commandctrl.NewCommandController(inputHistoryService, stdCommands)
|
||||||
commandController.AddCommandLookupExtension(scriptController)
|
commandController.AddCommandLookupExtension(scriptController)
|
||||||
commandController.SetCommandCompletionProvider(columnsController)
|
commandController.SetCommandCompletionProvider(columnsController)
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package cmdpacks
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
|
||||||
|
"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/controllers"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/models/queryexpr"
|
||||||
|
@ -65,21 +66,26 @@ var rsQueryDoc = repl.Doc{
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *rsModule) rsQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
|
func parseQuery(
|
||||||
|
ctx context.Context,
|
||||||
|
args ucl.CallArgs,
|
||||||
|
currentRS *models.ResultSet,
|
||||||
|
tablesService *tables.Service,
|
||||||
|
) (*queryexpr.QueryExpr, *models.TableInfo, error) {
|
||||||
var expr string
|
var expr string
|
||||||
if err := args.Bind(&expr); err != nil {
|
if err := args.Bind(&expr); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
q, err := queryexpr.Parse(expr)
|
q, err := queryexpr.Parse(expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.NArgs() > 0 {
|
if args.NArgs() > 0 {
|
||||||
var queryArgs ucl.Hashable
|
var queryArgs ucl.Hashable
|
||||||
if err := args.Bind(&queryArgs); err != nil {
|
if err := args.Bind(&queryArgs); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
queryNames := map[string]string{}
|
queryNames := map[string]string{}
|
||||||
|
@ -108,17 +114,26 @@ func (rs *rsModule) rsQuery(ctx context.Context, args ucl.CallArgs) (any, error)
|
||||||
if args.HasSwitch("table") {
|
if args.HasSwitch("table") {
|
||||||
var tblName string
|
var tblName string
|
||||||
if err := args.BindSwitch("table", &tblName); err != nil {
|
if err := args.BindSwitch("table", &tblName); err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tableInfo, err = rs.tableService.Describe(ctx, tblName)
|
tableInfo, err = tablesService.Describe(ctx, tblName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
} else if currRs := rs.state.ResultSet(); currRs != nil && currRs.TableInfo != nil {
|
} else if currentRS != nil && currentRS.TableInfo != nil {
|
||||||
tableInfo = currRs.TableInfo
|
tableInfo = currentRS.TableInfo
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.New("no table specified")
|
return nil, nil, errors.New("no table specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
return q, tableInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *rsModule) rsQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
q, tableInfo, err := parseQuery(ctx, args, rs.state.ResultSet(), rs.tableService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newResultSet, err := rs.tableService.ScanOrQuery(context.Background(), tableInfo, q, nil)
|
newResultSet, err := rs.tableService.ScanOrQuery(context.Background(), tableInfo, q, nil)
|
||||||
|
@ -167,6 +182,20 @@ func (rs *rsModule) rsScan(ctx context.Context, args ucl.CallArgs) (_ any, err e
|
||||||
return newResultSetProxy(newResultSet), nil
|
return newResultSetProxy(newResultSet), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *rsModule) rsFilter(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var (
|
||||||
|
rsProxy SimpleProxy[*models.ResultSet]
|
||||||
|
filter string
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := args.Bind(&rsProxy, &filter); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newResultSet := rs.tableService.Filter(rsProxy.ProxyValue(), filter)
|
||||||
|
return newResultSetProxy(newResultSet), nil
|
||||||
|
}
|
||||||
|
|
||||||
var rsNextPageDoc = repl.Doc{
|
var rsNextPageDoc = repl.Doc{
|
||||||
Brief: "Returns the next page of the passed in result-set",
|
Brief: "Returns the next page of the passed in result-set",
|
||||||
Usage: "RESULT_SET",
|
Usage: "RESULT_SET",
|
||||||
|
@ -207,6 +236,33 @@ func (rs *rsModule) rsUnion(ctx context.Context, args ucl.CallArgs) (_ any, err
|
||||||
return newResultSetProxy(rsProxy1.ProxyValue().MergeWith(rsProxy2.ProxyValue())), nil
|
return newResultSetProxy(rsProxy1.ProxyValue().MergeWith(rsProxy2.ProxyValue())), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rs *rsModule) rsSet(ctx context.Context, args ucl.CallArgs) (_ any, err error) {
|
||||||
|
var (
|
||||||
|
item itemProxy
|
||||||
|
expr string
|
||||||
|
val ucl.Object
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := args.Bind(&item, &expr, &val); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := queryexpr.Parse(expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMP
|
||||||
|
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 moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module {
|
func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module {
|
||||||
m := &rsModule{
|
m := &rsModule{
|
||||||
tableService: tableService,
|
tableService: tableService,
|
||||||
|
@ -219,8 +275,10 @@ func moduleRS(tableService *tables.Service, state *controllers.State) ucl.Module
|
||||||
"new": m.rsNew,
|
"new": m.rsNew,
|
||||||
"query": m.rsQuery,
|
"query": m.rsQuery,
|
||||||
"scan": m.rsScan,
|
"scan": m.rsScan,
|
||||||
|
"filter": m.rsFilter,
|
||||||
"next-page": m.rsNextPage,
|
"next-page": m.rsNextPage,
|
||||||
"union": m.rsUnion,
|
"union": m.rsUnion,
|
||||||
|
"set": m.rsSet,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
195
internal/common/ui/commandctrl/cmdpacks/modui.go
Normal file
195
internal/common/ui/commandctrl/cmdpacks/modui.go
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
package cmdpacks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/commandctrl"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/common/ui/events"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/controllers"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type uiModule struct {
|
||||||
|
tableService *tables.Service
|
||||||
|
state *controllers.State
|
||||||
|
ckb *customKeyBinding
|
||||||
|
readController *controllers.TableReadController
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiPrompt(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var prompt string
|
||||||
|
if err := args.Bind(&prompt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resChan := make(chan string)
|
||||||
|
go func() {
|
||||||
|
commandctrl.PostMsg(ctx, events.PromptForInput(prompt, nil, func(value string) tea.Msg {
|
||||||
|
resChan <- value
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case value := <-resChan:
|
||||||
|
return value, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiConfirm(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var prompt string
|
||||||
|
if err := args.Bind(&prompt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resChan := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
commandctrl.PostMsg(ctx, events.Confirm(prompt, func(value bool) tea.Msg {
|
||||||
|
resChan <- value
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case value := <-resChan:
|
||||||
|
return value, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiPromptTable(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
tables, err := m.tableService.ListTables(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resChan := make(chan string)
|
||||||
|
go func() {
|
||||||
|
commandctrl.PostMsg(ctx, controllers.PromptForTableMsg{
|
||||||
|
Tables: tables,
|
||||||
|
OnSelected: func(tableName string) tea.Msg {
|
||||||
|
resChan <- tableName
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case value := <-resChan:
|
||||||
|
return value, nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiBind(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var (
|
||||||
|
bindName string
|
||||||
|
key string
|
||||||
|
inv ucl.Invokable
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.NArgs() == 2 {
|
||||||
|
if err := args.Bind(&key, &inv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bindName = "custom." + key
|
||||||
|
} else {
|
||||||
|
if err := args.Bind(&bindName, &key, &inv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invoker := commandctrl.GetInvoker(ctx)
|
||||||
|
|
||||||
|
m.ckb.bindings[bindName] = func() tea.Msg {
|
||||||
|
return invoker.Invoke(inv, nil)
|
||||||
|
}
|
||||||
|
m.ckb.keyBindings[key] = bindName
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiQuery(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
q, tableInfo, err := parseQuery(ctx, args, m.state.ResultSet(), m.tableService)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commandctrl.PostMsg(ctx, m.readController.RunQuery(q, tableInfo))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *uiModule) uiFilter(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||||
|
var filter string
|
||||||
|
|
||||||
|
if err := args.Bind(&filter); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commandctrl.PostMsg(ctx, m.readController.Filter(filter))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func moduleUI(
|
||||||
|
tableService *tables.Service,
|
||||||
|
state *controllers.State,
|
||||||
|
readController *controllers.TableReadController,
|
||||||
|
) (ucl.Module, controllers.CustomKeyBindingSource) {
|
||||||
|
m := &uiModule{
|
||||||
|
tableService: tableService,
|
||||||
|
state: state,
|
||||||
|
readController: readController,
|
||||||
|
ckb: &customKeyBinding{
|
||||||
|
bindings: map[string]tea.Cmd{},
|
||||||
|
keyBindings: map[string]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return ucl.Module{
|
||||||
|
Name: "ui",
|
||||||
|
Builtins: map[string]ucl.BuiltinHandler{
|
||||||
|
"prompt": m.uiPrompt,
|
||||||
|
"prompt-table": m.uiPromptTable,
|
||||||
|
"confirm": m.uiConfirm,
|
||||||
|
"query": m.uiQuery,
|
||||||
|
"filter": m.uiFilter,
|
||||||
|
"bind": m.uiBind,
|
||||||
|
},
|
||||||
|
}, m.ckb
|
||||||
|
}
|
||||||
|
|
||||||
|
type customKeyBinding struct {
|
||||||
|
bindings map[string]tea.Cmd
|
||||||
|
keyBindings map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customKeyBinding) LookupBinding(theKey string) string {
|
||||||
|
return c.keyBindings[theKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customKeyBinding) CustomKeyCommand(key string) tea.Cmd {
|
||||||
|
bindingName, ok := c.keyBindings[key]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
binding, ok := c.bindings[bindingName]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customKeyBinding) UnbindKey(key string) {
|
||||||
|
delete(c.keyBindings, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *customKeyBinding) Rebind(bindingName string, newKey string) error {
|
||||||
|
c.keyBindings[newKey] = bindingName
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -19,6 +19,30 @@ type StandardCommands struct {
|
||||||
WriteController *controllers.TableWriteController
|
WriteController *controllers.TableWriteController
|
||||||
ExportController *controllers.ExportController
|
ExportController *controllers.ExportController
|
||||||
KeyBindingController *controllers.KeyBindingController
|
KeyBindingController *controllers.KeyBindingController
|
||||||
|
|
||||||
|
modUI ucl.Module
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStandardCommands(
|
||||||
|
tableService *tables.Service,
|
||||||
|
state *controllers.State,
|
||||||
|
readController *controllers.TableReadController,
|
||||||
|
writeController *controllers.TableWriteController,
|
||||||
|
exportController *controllers.ExportController,
|
||||||
|
keyBindingController *controllers.KeyBindingController,
|
||||||
|
) StandardCommands {
|
||||||
|
modUI, ckbs := moduleUI(tableService, state, readController)
|
||||||
|
keyBindingController.SetCustomKeyBindingSource(ckbs)
|
||||||
|
|
||||||
|
return StandardCommands{
|
||||||
|
TableService: tableService,
|
||||||
|
State: state,
|
||||||
|
ReadController: readController,
|
||||||
|
WriteController: writeController,
|
||||||
|
ExportController: exportController,
|
||||||
|
KeyBindingController: keyBindingController,
|
||||||
|
modUI: modUI,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdQuitDoc = repl.Doc{
|
var cmdQuitDoc = repl.Doc{
|
||||||
|
@ -364,6 +388,7 @@ func (sc StandardCommands) cmdRebind(ctx context.Context, args ucl.CallArgs) (an
|
||||||
func (sc StandardCommands) InstOptions() []ucl.InstOption {
|
func (sc StandardCommands) InstOptions() []ucl.InstOption {
|
||||||
return []ucl.InstOption{
|
return []ucl.InstOption{
|
||||||
ucl.WithModule(moduleRS(sc.TableService, sc.State)),
|
ucl.WithModule(moduleRS(sc.TableService, sc.State)),
|
||||||
|
ucl.WithModule(sc.modUI),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,10 @@ import (
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/inputhistory"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/inputhistory"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/jobs"
|
||||||
|
keybindings_service "github.com/lmika/dynamo-browse/internal/dynamo-browse/services/keybindings"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||||
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/viewsnapshot"
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/services/viewsnapshot"
|
||||||
|
"github.com/lmika/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
||||||
"github.com/lmika/dynamo-browse/test/testdynamo"
|
"github.com/lmika/dynamo-browse/test/testdynamo"
|
||||||
"github.com/lmika/dynamo-browse/test/testworkspace"
|
"github.com/lmika/dynamo-browse/test/testworkspace"
|
||||||
bus "github.com/lmika/events"
|
bus "github.com/lmika/events"
|
||||||
|
@ -132,15 +134,12 @@ func newService(t *testing.T, opts ...serviceOpt) *services {
|
||||||
columnsController := controllers.NewColumnsController(readController, eventBus)
|
columnsController := controllers.NewColumnsController(readController, eventBus)
|
||||||
exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{})
|
exportController := controllers.NewExportController(state, service, jobsController, columnsController, pasteboardprovider.NilProvider{})
|
||||||
|
|
||||||
|
keyBindingService := keybindings_service.NewService(keybindings.Default())
|
||||||
|
keyBindingController := controllers.NewKeyBindingController(keyBindingService, nil)
|
||||||
|
|
||||||
_ = settingsController
|
_ = settingsController
|
||||||
commandController := commandctrl.NewCommandController(inputHistoryService,
|
commandController := commandctrl.NewCommandController(inputHistoryService,
|
||||||
cmdpacks.StandardCommands{
|
cmdpacks.NewStandardCommands(service, state, readController, writeController, exportController, keyBindingController),
|
||||||
State: state,
|
|
||||||
TableService: service,
|
|
||||||
ReadController: readController,
|
|
||||||
WriteController: writeController,
|
|
||||||
ExportController: exportController,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
s.State = state
|
s.State = state
|
||||||
|
|
|
@ -139,8 +139,26 @@ func (c *CommandController) ExecuteAndWait(ctx context.Context, commandInput str
|
||||||
return c.uclInst.Eval(ctx, commandInput)
|
return c.uclInst.Eval(ctx, commandInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommandController) Invoke(invokable ucl.Invokable, args []any) (msg tea.Msg) {
|
||||||
|
execCtx := execContext{ctrl: c}
|
||||||
|
ctx := context.WithValue(context.Background(), commandCtlKey, &execCtx)
|
||||||
|
|
||||||
|
res, err := invokable.Invoke(ctx, args)
|
||||||
|
if err != nil {
|
||||||
|
msg = events.Error(err)
|
||||||
|
} else if res != nil {
|
||||||
|
msg = events.StatusMsg(fmt.Sprint(res))
|
||||||
|
}
|
||||||
|
if execCtx.requestRefresh {
|
||||||
|
c.postMessage(events.ResultSetUpdated{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CommandController) cmdLooper() {
|
func (c *CommandController) cmdLooper() {
|
||||||
ctx := context.WithValue(context.Background(), commandCtlKey, c)
|
execCtx := execContext{ctrl: c}
|
||||||
|
ctx := context.WithValue(context.Background(), commandCtlKey, &execCtx)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -151,6 +169,9 @@ func (c *CommandController) cmdLooper() {
|
||||||
} else if res != nil {
|
} else if res != nil {
|
||||||
c.postMessage(events.StatusMsg(fmt.Sprint(res)))
|
c.postMessage(events.StatusMsg(fmt.Sprint(res)))
|
||||||
}
|
}
|
||||||
|
if execCtx.requestRefresh {
|
||||||
|
c.postMessage(events.ResultSetUpdated{})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,33 +3,60 @@ package commandctrl
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"ucl.lmika.dev/ucl"
|
||||||
)
|
)
|
||||||
|
|
||||||
type commandCtlKeyType struct{}
|
type commandCtlKeyType struct{}
|
||||||
|
|
||||||
var commandCtlKey = commandCtlKeyType{}
|
var commandCtlKey = commandCtlKeyType{}
|
||||||
|
|
||||||
|
type execContext struct {
|
||||||
|
ctrl *CommandController
|
||||||
|
requestRefresh bool
|
||||||
|
}
|
||||||
|
|
||||||
func PostMsg(ctx context.Context, msg tea.Msg) {
|
func PostMsg(ctx context.Context, msg tea.Msg) {
|
||||||
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
|
||||||
if ok {
|
if ok {
|
||||||
cmdCtl.postMessage(msg)
|
cmdCtl.ctrl.postMessage(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectedItemIndex(ctx context.Context) (int, bool) {
|
func SelectedItemIndex(ctx context.Context) (int, bool) {
|
||||||
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmdCtl.uiStateProvider.SelectedItemIndex(), true
|
return cmdCtl.ctrl.uiStateProvider.SelectedItemIndex(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetSelectedItemIndex(ctx context.Context, newIdx int) tea.Msg {
|
func SetSelectedItemIndex(ctx context.Context, newIdx int) tea.Msg {
|
||||||
cmdCtl, ok := ctx.Value(commandCtlKey).(*CommandController)
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmdCtl.uiStateProvider.SetSelectedItemIndex(newIdx)
|
return cmdCtl.ctrl.uiStateProvider.SetSelectedItemIndex(newIdx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetInvoker(ctx context.Context) Invoker {
|
||||||
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdCtl.ctrl
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueueRefresh(ctx context.Context) {
|
||||||
|
cmdCtl, ok := ctx.Value(commandCtlKey).(*execContext)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmdCtl.requestRefresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
type Invoker interface {
|
||||||
|
Invoke(invokable ucl.Invokable, args []any) tea.Msg
|
||||||
}
|
}
|
||||||
|
|
5
internal/common/ui/events/resultset.go
Normal file
5
internal/common/ui/events/resultset.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package events
|
||||||
|
|
||||||
|
type ResultSetUpdated struct {
|
||||||
|
StatusMessage string
|
||||||
|
}
|
|
@ -81,14 +81,6 @@ type PromptForTableMsg struct {
|
||||||
OnSelected func(tableName string) tea.Msg
|
OnSelected func(tableName string) tea.Msg
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResultSetUpdated struct {
|
|
||||||
statusMessage string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs ResultSetUpdated) StatusMessage() string {
|
|
||||||
return rs.statusMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShowColumnOverlay struct{}
|
type ShowColumnOverlay struct{}
|
||||||
type HideColumnOverlay struct{}
|
type HideColumnOverlay struct{}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,10 @@ func NewKeyBindingController(service *keybindings.Service, customBindingSource C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kb *KeyBindingController) SetCustomKeyBindingSource(customBindingSource CustomKeyBindingSource) {
|
||||||
|
kb.customBindingSource = customBindingSource
|
||||||
|
}
|
||||||
|
|
||||||
func (kb *KeyBindingController) Rebind(bindingName string, newKey string, force bool) tea.Msg {
|
func (kb *KeyBindingController) Rebind(bindingName string, newKey string, force bool) tea.Msg {
|
||||||
existingBinding := kb.findExistingBinding(newKey)
|
existingBinding := kb.findExistingBinding(newKey)
|
||||||
if existingBinding == "" {
|
if existingBinding == "" {
|
||||||
|
|
|
@ -182,6 +182,10 @@ func (c *TableReadController) PromptForQuery() tea.Msg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TableReadController) RunQuery(q *queryexpr.QueryExpr, table *models.TableInfo) tea.Msg {
|
||||||
|
return c.runQuery(table, q, "", true, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *TableReadController) runQuery(
|
func (c *TableReadController) runQuery(
|
||||||
tableInfo *models.TableInfo,
|
tableInfo *models.TableInfo,
|
||||||
query *queryexpr.QueryExpr,
|
query *queryexpr.QueryExpr,
|
||||||
|
@ -339,27 +343,31 @@ func (c *TableReadController) Mark(op MarkOp, where string) tea.Msg {
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TableReadController) Filter() tea.Msg {
|
func (c *TableReadController) PromptForFilter() tea.Msg {
|
||||||
return events.PromptForInputMsg{
|
return events.PromptForInputMsg{
|
||||||
Prompt: "filter: ",
|
Prompt: "filter: ",
|
||||||
History: c.inputHistoryService.Iter(context.Background(), filterInputHistoryCategory),
|
History: c.inputHistoryService.Iter(context.Background(), filterInputHistoryCategory),
|
||||||
OnDone: func(value string) tea.Msg {
|
OnDone: func(value string) tea.Msg {
|
||||||
resultSet := c.state.ResultSet()
|
return c.Filter(value)
|
||||||
if resultSet == nil {
|
|
||||||
return events.StatusMsg("Result-set is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewJob(c.jobController, "Applying Filter…", func(ctx context.Context) (*models.ResultSet, error) {
|
|
||||||
newResultSet := c.tableService.Filter(resultSet, value)
|
|
||||||
return newResultSet, nil
|
|
||||||
}).OnEither(c.handleResultSetFromJobResult(value, true, false, resultSetUpdateFilter)).Submit()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *TableReadController) Filter(value string) tea.Msg {
|
||||||
|
resultSet := c.state.ResultSet()
|
||||||
|
if resultSet == nil {
|
||||||
|
return events.StatusMsg("Result-set is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewJob(c.jobController, "Applying Filter…", func(ctx context.Context) (*models.ResultSet, error) {
|
||||||
|
newResultSet := c.tableService.Filter(resultSet, value)
|
||||||
|
return newResultSet, nil
|
||||||
|
}).OnEither(c.handleResultSetFromJobResult(value, true, false, resultSetUpdateFilter)).Submit()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *TableReadController) handleResultSetFromJobResult(
|
func (c *TableReadController) handleResultSetFromJobResult(
|
||||||
filter string,
|
filter string,
|
||||||
pushbackStack, errIfEmpty bool,
|
pushbackStack, errIfEmpty bool,
|
||||||
|
|
|
@ -44,7 +44,7 @@ func (twc *TableWriteController) ToggleMark(idx int) tea.Msg {
|
||||||
resultSet.SetMark(idx, !resultSet.Marked(idx))
|
resultSet.SetMark(idx, !resultSet.Marked(idx))
|
||||||
})
|
})
|
||||||
|
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) NewItem() tea.Msg {
|
func (twc *TableWriteController) NewItem() tea.Msg {
|
||||||
|
@ -148,7 +148,7 @@ func (twc *TableWriteController) setStringValue(idx int, attr *queryexpr.QueryEx
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func (twc *TableWriteController) setToExpressionValue(idx int, attr *queryexpr.Q
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ func (twc *TableWriteController) setNumberValue(idx int, attr *queryexpr.QueryEx
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,7 +234,7 @@ func (twc *TableWriteController) setBoolValue(idx int, attr *queryexpr.QueryExpr
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ func (twc *TableWriteController) setNullValue(idx int, attr *queryexpr.QueryExpr
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Msg {
|
func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Msg {
|
||||||
|
@ -291,7 +291,7 @@ func (twc *TableWriteController) DeleteAttribute(idx int, key string) tea.Msg {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (twc *TableWriteController) PutItems() tea.Msg {
|
func (twc *TableWriteController) PutItems() tea.Msg {
|
||||||
|
@ -351,8 +351,8 @@ func (twc *TableWriteController) PutItems() tea.Msg {
|
||||||
}
|
}
|
||||||
return rs, nil
|
return rs, nil
|
||||||
}).OnDone(func(rs *models.ResultSet) tea.Msg {
|
}).OnDone(func(rs *models.ResultSet) tea.Msg {
|
||||||
return ResultSetUpdated{
|
return events.ResultSetUpdated{
|
||||||
statusMessage: applyToN("", len(itemsToPut), "item", "item", " put to table"),
|
StatusMessage: applyToN("", len(itemsToPut), "item", "item", " put to table"),
|
||||||
}
|
}
|
||||||
}).Submit()
|
}).Submit()
|
||||||
},
|
},
|
||||||
|
@ -379,7 +379,7 @@ func (twc *TableWriteController) TouchItem(idx int) tea.Msg {
|
||||||
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
|
if err := twc.tableService.PutItemAt(context.Background(), resultSet, idx); err != nil {
|
||||||
return events.Error(err)
|
return events.Error(err)
|
||||||
}
|
}
|
||||||
return ResultSetUpdated{}
|
return events.ResultSetUpdated{}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,10 +276,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case controllers.SetTableItemView:
|
case controllers.SetTableItemView:
|
||||||
cmd := m.setMainViewIndex(msg.ViewIndex)
|
cmd := m.setMainViewIndex(msg.ViewIndex)
|
||||||
return m, cmd
|
return m, cmd
|
||||||
case controllers.ResultSetUpdated:
|
case events.ResultSetUpdated:
|
||||||
return m, tea.Batch(
|
return m, tea.Batch(
|
||||||
m.tableView.Refresh(),
|
m.tableView.Refresh(),
|
||||||
events.SetStatus(msg.StatusMessage()),
|
events.SetStatus(msg.StatusMessage),
|
||||||
)
|
)
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
// TODO: use modes here
|
// TODO: use modes here
|
||||||
|
@ -302,7 +302,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case key.Matches(msg, m.keyMap.PromptForQuery):
|
case key.Matches(msg, m.keyMap.PromptForQuery):
|
||||||
return m, m.tableReadController.PromptForQuery
|
return m, m.tableReadController.PromptForQuery
|
||||||
case key.Matches(msg, m.keyMap.PromptForFilter):
|
case key.Matches(msg, m.keyMap.PromptForFilter):
|
||||||
return m, m.tableReadController.Filter
|
return m, m.tableReadController.PromptForFilter
|
||||||
case key.Matches(msg, m.keyMap.FetchNextPage):
|
case key.Matches(msg, m.keyMap.FetchNextPage):
|
||||||
return m, m.tableReadController.NextPage
|
return m, m.tableReadController.NextPage
|
||||||
case key.Matches(msg, m.keyMap.ViewBack):
|
case key.Matches(msg, m.keyMap.ViewBack):
|
||||||
|
|
Loading…
Reference in a new issue