Added ui:set-item-annotator
This commit is contained in:
parent
a733a47d5c
commit
c11560e6cd
|
|
@ -110,7 +110,7 @@ func main() {
|
|||
|
||||
tableService := tables.NewService(dynamoProvider, settingStore)
|
||||
workspaceService := viewsnapshot.NewService(resultSetSnapshotStore)
|
||||
itemRendererService := itemrenderer.NewService(nil, uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo)
|
||||
itemRendererService := itemrenderer.NewService(uiStyles.ItemView.FieldType, uiStyles.ItemView.MetaInfo)
|
||||
jobsService := jobs.NewService(eventBus)
|
||||
inputHistoryService := inputhistory.New(inputHistoryStore)
|
||||
|
||||
|
|
@ -177,6 +177,7 @@ func main() {
|
|||
keyBindingController,
|
||||
pasteboardProvider,
|
||||
settingsController,
|
||||
itemRendererService,
|
||||
)
|
||||
|
||||
commandController, err := commandctrl.NewCommandController(inputHistoryService, stdCommands)
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ package cmdpacks
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/common/ui/commandctrl"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/common/ui/events"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||
"ucl.lmika.dev/ucl"
|
||||
)
|
||||
|
|
@ -16,6 +19,7 @@ type uiModule struct {
|
|||
state *controllers.State
|
||||
ckb *customKeyBinding
|
||||
readController *controllers.TableReadController
|
||||
itemRenderer *itemrenderer.Service
|
||||
}
|
||||
|
||||
func (m *uiModule) uiCommand(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
|
|
@ -191,15 +195,35 @@ func (m *uiModule) uiFilter(ctx context.Context, args ucl.CallArgs) (any, error)
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *uiModule) uiSetItemAnnotator(ctx context.Context, args ucl.CallArgs) (any, error) {
|
||||
var inv ucl.Invokable
|
||||
if err := args.Bind(&inv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.itemRenderer.SetAnnotation(itemrenderer.AnnotationFunc(func(rs *models.ResultSet, item models.Item, path models.AttrPathNode) string {
|
||||
v, err := inv.Invoke(ctx, newResultSetProxy(rs), itemProxy{rs, 0, item}, attrPathProxy{attrPath: &path})
|
||||
if err != nil {
|
||||
return ""
|
||||
} else if v == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprint(v)
|
||||
}))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func moduleUI(
|
||||
tableService *tables.Service,
|
||||
state *controllers.State,
|
||||
readController *controllers.TableReadController,
|
||||
itemRenderer *itemrenderer.Service,
|
||||
) (ucl.Module, controllers.CustomKeyBindingSource) {
|
||||
m := &uiModule{
|
||||
tableService: tableService,
|
||||
state: state,
|
||||
readController: readController,
|
||||
itemRenderer: itemRenderer,
|
||||
ckb: &customKeyBinding{
|
||||
bindings: map[string]tea.Cmd{},
|
||||
keyBindings: map[string]string{},
|
||||
|
|
@ -217,6 +241,7 @@ func moduleUI(
|
|||
"query": m.uiQuery,
|
||||
"filter": m.uiFilter,
|
||||
"bind": m.uiBind,
|
||||
"set-item-annotator": m.uiSetItemAnnotator,
|
||||
},
|
||||
}, m.ckb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,49 @@ func (tp itemProxy) Each(fn func(k string, v ucl.Object) error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type attrPathProxy struct {
|
||||
attrPath *models.AttrPathNode
|
||||
}
|
||||
|
||||
func (ap attrPathProxy) String() string {
|
||||
return "RSItem()"
|
||||
}
|
||||
|
||||
func (ap attrPathProxy) Truthy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ap attrPathProxy) Len() (l int) {
|
||||
for p := ap.attrPath; p != nil; p = p.Parent {
|
||||
l++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ap attrPathProxy) Index(k int) ucl.Object {
|
||||
if k == -1 {
|
||||
return ucl.StringObject(ap.attrPath.Key)
|
||||
}
|
||||
|
||||
if k >= 0 {
|
||||
k = ap.Len() - k - 1
|
||||
} else {
|
||||
k = -k - 1
|
||||
}
|
||||
|
||||
if k < 0 {
|
||||
return nil
|
||||
}
|
||||
for p := ap.attrPath; p != nil; p = p.Parent {
|
||||
if k <= 0 {
|
||||
return ucl.StringObject(p.Key)
|
||||
}
|
||||
k -= 1
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type attributeValueProxy struct {
|
||||
value types.AttributeValue
|
||||
}
|
||||
|
|
|
|||
101
internal/common/ui/commandctrl/cmdpacks/proxy_test.go
Normal file
101
internal/common/ui/commandctrl/cmdpacks/proxy_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package cmdpacks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
|
||||
)
|
||||
|
||||
func TestAttrPathProxy_Index(t *testing.T) {
|
||||
tests := []struct {
|
||||
descr string
|
||||
attrPath models.AttrPathNode
|
||||
index int
|
||||
want string
|
||||
wantNil bool
|
||||
}{
|
||||
{
|
||||
descr: "return leaf 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf"},
|
||||
index: -1,
|
||||
want: "leaf",
|
||||
},
|
||||
{
|
||||
descr: "return leaf 2",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent"}},
|
||||
index: -1,
|
||||
want: "leaf",
|
||||
},
|
||||
{
|
||||
descr: "return parent 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent"}},
|
||||
index: -2,
|
||||
want: "parent",
|
||||
},
|
||||
{
|
||||
descr: "return parent 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent", Parent: &models.AttrPathNode{Key: "grandparent"}}},
|
||||
index: -2,
|
||||
want: "parent",
|
||||
},
|
||||
{
|
||||
descr: "return parent 3",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent", Parent: &models.AttrPathNode{Key: "grandparent"}}},
|
||||
index: -3,
|
||||
want: "grandparent",
|
||||
},
|
||||
{
|
||||
descr: "return root 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent", Parent: &models.AttrPathNode{Key: "grandparent"}}},
|
||||
index: 0,
|
||||
want: "grandparent",
|
||||
},
|
||||
{
|
||||
descr: "return root 2",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent"}},
|
||||
index: 0,
|
||||
want: "parent",
|
||||
},
|
||||
{
|
||||
descr: "return root 3",
|
||||
attrPath: models.AttrPathNode{Key: "leaf"},
|
||||
index: 0,
|
||||
want: "leaf",
|
||||
},
|
||||
{
|
||||
descr: "return first child 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent", Parent: &models.AttrPathNode{Key: "grandparent"}}},
|
||||
index: 1,
|
||||
want: "parent",
|
||||
},
|
||||
{
|
||||
descr: "return first child 2",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent"}},
|
||||
index: 1,
|
||||
want: "leaf",
|
||||
},
|
||||
{
|
||||
descr: "return nil 1",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent", Parent: &models.AttrPathNode{Key: "grandparent"}}},
|
||||
index: -5,
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
descr: "return nil 2",
|
||||
attrPath: models.AttrPathNode{Key: "leaf", Parent: &models.AttrPathNode{Key: "parent"}},
|
||||
index: 56,
|
||||
wantNil: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.descr, func(t *testing.T) {
|
||||
proxy := attrPathProxy{&tt.attrPath}
|
||||
if tt.wantNil {
|
||||
assert.Nil(t, proxy.Index(tt.index))
|
||||
} else {
|
||||
assert.Equal(t, tt.want, proxy.Index(tt.index).String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/controllers"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/models"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/itemrenderer"
|
||||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/services/tables"
|
||||
"ucl.lmika.dev/repl"
|
||||
"ucl.lmika.dev/ucl"
|
||||
|
|
@ -23,6 +24,7 @@ type StandardCommands struct {
|
|||
KeyBindingController *controllers.KeyBindingController
|
||||
PBProvider services.PasteboardProvider
|
||||
SettingsController *controllers.SettingsController
|
||||
ItemRenderer *itemrenderer.Service
|
||||
|
||||
modUI ucl.Module
|
||||
}
|
||||
|
|
@ -36,8 +38,9 @@ func NewStandardCommands(
|
|||
keyBindingController *controllers.KeyBindingController,
|
||||
pbProvider services.PasteboardProvider,
|
||||
settingsController *controllers.SettingsController,
|
||||
itemRenderer *itemrenderer.Service,
|
||||
) StandardCommands {
|
||||
modUI, ckbs := moduleUI(tableService, state, readController)
|
||||
modUI, ckbs := moduleUI(tableService, state, readController, itemRenderer)
|
||||
keyBindingController.SetCustomKeyBindingSource(ckbs)
|
||||
|
||||
return StandardCommands{
|
||||
|
|
@ -49,6 +52,7 @@ func NewStandardCommands(
|
|||
KeyBindingController: keyBindingController,
|
||||
PBProvider: pbProvider,
|
||||
SettingsController: settingsController,
|
||||
ItemRenderer: itemRenderer,
|
||||
modUI: modUI,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package cmdpacks_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
bus "github.com/lmika/events"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -21,7 +23,6 @@ import (
|
|||
"lmika.dev/cmd/dynamo-browse/internal/dynamo-browse/ui/keybindings"
|
||||
"lmika.dev/cmd/dynamo-browse/test/testdynamo"
|
||||
"lmika.dev/cmd/dynamo-browse/test/testworkspace"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStdCmds_Mark(t *testing.T) {
|
||||
|
|
@ -162,6 +163,7 @@ func newService(t *testing.T, opts ...serviceOpt) *services {
|
|||
keyBindingController,
|
||||
testPB,
|
||||
settingsController,
|
||||
itemRendererService,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,17 +10,22 @@ import (
|
|||
)
|
||||
|
||||
type Service struct {
|
||||
annotations Annotation
|
||||
annotation Annotation
|
||||
styles styleRenderer
|
||||
}
|
||||
|
||||
func NewService(
|
||||
annotations Annotation,
|
||||
fileTypeStyle StyleRenderer,
|
||||
metaInfoStyle StyleRenderer,
|
||||
) *Service {
|
||||
if fileTypeStyle == nil {
|
||||
fileTypeStyle = plainTextStyleRenderer{}
|
||||
}
|
||||
if metaInfoStyle == nil {
|
||||
metaInfoStyle = plainTextStyleRenderer{}
|
||||
}
|
||||
return &Service{
|
||||
annotations: testAnnotation{},
|
||||
annotation: nil,
|
||||
styles: styleRenderer{
|
||||
fileTypeRenderer: fileTypeStyle,
|
||||
metaInfoRenderer: metaInfoStyle,
|
||||
|
|
@ -28,6 +33,10 @@ func NewService(
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Service) SetAnnotation(a Annotation) {
|
||||
s.annotation = a
|
||||
}
|
||||
|
||||
func (s *Service) RenderItem(w io.Writer, item models.Item, resultSet *models.ResultSet, plainText bool) {
|
||||
styles := s.styles
|
||||
if plainText {
|
||||
|
|
@ -71,9 +80,9 @@ func (m *Service) renderItem(
|
|||
fmt.Fprint(w, "\t")
|
||||
fmt.Fprint(w, r.StringValue())
|
||||
fmt.Fprint(w, sr.metaInfoRenderer.Render(r.MetaInfo()))
|
||||
if m.annotations != nil {
|
||||
if m.annotation != nil {
|
||||
fmt.Fprint(w, " ")
|
||||
fmt.Fprint(w, sr.metaInfoRenderer.Render(m.annotations.AnnotateAttribute(resultSet, item, path)))
|
||||
fmt.Fprint(w, sr.metaInfoRenderer.Render(m.annotation.AnnotateAttribute(resultSet, item, path)))
|
||||
}
|
||||
fmt.Fprint(w, "\n")
|
||||
|
||||
|
|
@ -94,8 +103,8 @@ type Annotation interface {
|
|||
AnnotateAttribute(rs *models.ResultSet, item models.Item, path models.AttrPathNode) string
|
||||
}
|
||||
|
||||
type testAnnotation struct{}
|
||||
type AnnotationFunc func(rs *models.ResultSet, item models.Item, path models.AttrPathNode) string
|
||||
|
||||
func (t testAnnotation) AnnotateAttribute(rs *models.ResultSet, item models.Item, path models.AttrPathNode) string {
|
||||
return "( annotation of " + path.Key + " )"
|
||||
func (af AnnotationFunc) AnnotateAttribute(rs *models.ResultSet, item models.Item, path models.AttrPathNode) string {
|
||||
return af(rs, item, path)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue