Some work, I guess

This commit is contained in:
Brian Buller 2022-11-18 13:43:52 -06:00
parent dce6e6c368
commit 22ffda8d13
4 changed files with 101 additions and 72 deletions

View File

@ -108,12 +108,12 @@ func (bd *BoltDB) GetPairFromPath(path []string) (*BoltPair, error) {
return p, err return p, err
} }
func (bd *BoltDB) GetVisibleItemCount(path []string) (int, error) { func (bd *BoltDB) GetVisibleItemCount(path []string, search string) (int, error) {
vis := 0 vis := 0
var retErr error var retErr error
if len(path) == 0 { if len(path) == 0 {
for i := range bd.buckets { for i := range bd.buckets {
n, err := bd.GetVisibleItemCount(bd.buckets[i].GetPath()) n, err := bd.GetVisibleItemCount(bd.buckets[i].GetPath(), search)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -126,13 +126,13 @@ func (bd *BoltDB) GetVisibleItemCount(path []string) (int, error) {
} }
// 1 for the bucket // 1 for the bucket
vis++ vis++
if b.expanded { if b.expanded || search != "" {
// This bucket is expanded, add up it's children // This bucket is expanded (or we're searching), add up it's children
// * 1 for each pair // * 1 for each pair
vis += len(b.pairs) vis += len(b.pairs)
// * recurse for buckets // * recurse for buckets
for i := range b.buckets { for i := range b.buckets {
n, err := bd.GetVisibleItemCount(b.buckets[i].GetPath()) n, err := bd.GetVisibleItemCount(b.buckets[i].GetPath(), search)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -143,12 +143,12 @@ func (bd *BoltDB) GetVisibleItemCount(path []string) (int, error) {
return vis, retErr return vis, retErr
} }
func (bd *BoltDB) BuildVisiblePathSlice(filter string) ([][]string, error) { func (bd *BoltDB) BuildVisiblePathSlice(search string) ([][]string, error) {
var retSlice [][]string var retSlice [][]string
var retErr error var retErr error
// The root path, recurse for root buckets // The root path, recurse for root buckets
for i := range bd.buckets { for i := range bd.buckets {
bktS, bktErr := bd.buckets[i].BuildVisiblePathSlice([]string{}, filter) bktS, bktErr := bd.buckets[i].BuildVisiblePathSlice([]string{}, search)
if bktErr == nil { if bktErr == nil {
retSlice = append(retSlice, bktS...) retSlice = append(retSlice, bktS...)
} else { } else {
@ -159,8 +159,8 @@ func (bd *BoltDB) BuildVisiblePathSlice(filter string) ([][]string, error) {
return retSlice, retErr return retSlice, retErr
} }
func (bd *BoltDB) IsVisiblePath(path []string, filter string) bool { func (bd *BoltDB) IsVisiblePath(path []string, search string) bool {
visPaths, err := bd.BuildVisiblePathSlice(filter) visPaths, err := bd.BuildVisiblePathSlice(search)
if err != nil { if err != nil {
return false return false
} }
@ -181,8 +181,8 @@ func (bd *BoltDB) IsVisiblePath(path []string, filter string) bool {
} }
return false return false
} }
func (bd *BoltDB) GetPrevVisiblePath(path []string, filter string) []string { func (bd *BoltDB) GetPrevVisiblePath(path []string, search string) []string {
visPaths, err := bd.BuildVisiblePathSlice(filter) visPaths, err := bd.BuildVisiblePathSlice(search)
if path == nil { if path == nil {
if len(visPaths) > 0 { if len(visPaths) > 0 {
return visPaths[len(visPaths)-1] return visPaths[len(visPaths)-1]
@ -205,8 +205,8 @@ func (bd *BoltDB) GetPrevVisiblePath(path []string, filter string) []string {
} }
return nil return nil
} }
func (bd *BoltDB) GetNextVisiblePath(path []string, filter string) []string { func (bd *BoltDB) GetNextVisiblePath(path []string, search string) []string {
visPaths, err := bd.BuildVisiblePathSlice(filter) visPaths, err := bd.BuildVisiblePathSlice(search)
if path == nil { if path == nil {
if len(visPaths) > 0 { if len(visPaths) > 0 {
return visPaths[0] return visPaths[0]

View File

@ -38,14 +38,16 @@ func (b *BoltBucket) GetPath() []string {
buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket buildVisiblePathSlice builds a slice of string slices containing all visible paths in this bucket
The passed prefix is the path leading to the current bucket The passed prefix is the path leading to the current bucket
*/ */
func (b *BoltBucket) BuildVisiblePathSlice(prefix []string, filter string) ([][]string, error) { func (b *BoltBucket) BuildVisiblePathSlice(prefix []string, search string) ([][]string, error) {
var retSlice [][]string var retSlice [][]string
var retErr error var retErr error
retSlice = append(retSlice, append(prefix, b.name)) if search == "" || strings.Contains(b.name, search) {
if b.expanded { retSlice = append(retSlice, append(prefix, b.name))
}
if b.expanded || search != "" {
// Add subbuckets // Add subbuckets
for i := range b.buckets { for i := range b.buckets {
bktS, bktErr := b.buckets[i].BuildVisiblePathSlice(append(prefix, b.name), filter) bktS, bktErr := b.buckets[i].BuildVisiblePathSlice(append(prefix, b.name), search)
if bktErr != nil { if bktErr != nil {
return retSlice, bktErr return retSlice, bktErr
} }
@ -53,7 +55,7 @@ func (b *BoltBucket) BuildVisiblePathSlice(prefix []string, filter string) ([][]
} }
// Add pairs // Add pairs
for i := range b.pairs { for i := range b.pairs {
if filter != "" && !strings.Contains(b.pairs[i].key, filter) { if search != "" && !strings.Contains(b.pairs[i].key, search) {
continue continue
} }
retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key)) retSlice = append(retSlice, append(append(prefix, b.name), b.pairs[i].key))

View File

@ -6,6 +6,7 @@ import (
"git.bullercodeworks.com/brian/boltbrowser/models" "git.bullercodeworks.com/brian/boltbrowser/models"
"git.bullercodeworks.com/brian/boltbrowser/util" "git.bullercodeworks.com/brian/boltbrowser/util"
"git.bullercodeworks.com/brian/wandle" "git.bullercodeworks.com/brian/wandle"
"git.bullercodeworks.com/brian/widdles"
"github.com/nsf/termbox-go" "github.com/nsf/termbox-go"
) )
@ -21,8 +22,7 @@ type BoltTreePane struct {
pathBuffer [][]string pathBuffer [][]string
currentPath []string currentPath []string
currentPathIdx int currentPathIdx int
filter string search *widdles.Field
filterMode bool
insertPairCmd func(*models.BoltBucket) wandle.Cmd insertPairCmd func(*models.BoltBucket) wandle.Cmd
insertBucketCmd func(*models.BoltBucket) wandle.Cmd insertBucketCmd func(*models.BoltBucket) wandle.Cmd
@ -46,6 +46,7 @@ func NewBoltTreePane(x, y, w, h int) *BoltTreePane {
return &BoltTreePane{ return &BoltTreePane{
x: x, y: y, x: x, y: y,
width: w, height: h, width: w, height: h,
search: widdles.NewField("Search", "", x, y+h),
} }
} }
@ -53,16 +54,18 @@ func (w *BoltTreePane) Init() wandle.Cmd {
if w.db != nil { if w.db != nil {
w.refreshDBAndBuffer() w.refreshDBAndBuffer()
} }
w.search.SetWidth(w.width)
return nil return nil
} }
func (w *BoltTreePane) SetDetailPane(pane *BoltDetailPane) { w.detailPane = pane } func (w *BoltTreePane) SetDetailPane(pane *BoltDetailPane) { w.detailPane = pane }
func (w *BoltTreePane) Measure() {} func (w *BoltTreePane) Measure() {
}
func (w *BoltTreePane) Update(msg wandle.Msg) wandle.Cmd { func (w *BoltTreePane) Update(msg wandle.Msg) wandle.Cmd {
if w.db == nil { if w.db == nil {
return nil return nil
} }
if len(w.currentPath) == 0 { if len(w.currentPath) == 0 {
w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) w.currentPath = w.db.GetNextVisiblePath(nil, w.search.GetValue())
} }
switch msg := msg.(type) { switch msg := msg.(type) {
case BrowseMsg: case BrowseMsg:
@ -88,48 +91,65 @@ func (w *BoltTreePane) Update(msg wandle.Msg) wandle.Cmd {
return nil return nil
} }
func (w *BoltTreePane) handleTermboxEvent(msg termbox.Event) wandle.Cmd { func (w *BoltTreePane) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
switch msg.Type { if w.search.IsActive() {
case termbox.EventKey: if msg.Type == termbox.EventKey {
if msg.Ch == 'g' { if msg.Key == termbox.KeyEnter {
return w.jumpToStart return func() wandle.Msg {
} else if msg.Ch == 'G' { w.search.SetActive(false)
return w.jumpToEnd return nil
} else if msg.Key == termbox.KeyCtrlF { }
return w.pageDown } else {
} else if msg.Key == termbox.KeyCtrlB { w.setStatus(w.search.Message, time.Second)
return w.pageUp return w.search.Update(msg)
} else if msg.Ch == 'j' || msg.Key == termbox.KeyArrowDown { }
return w.moveCursorDown }
} else if msg.Ch == 'k' || msg.Key == termbox.KeyArrowUp { } else {
return w.moveCursorUp switch msg.Type {
} else if msg.Key == termbox.KeyCtrlR { case termbox.EventKey:
return w.refreshDBAndBuffer if msg.Ch == 'g' {
} else if msg.Ch == 'p' { return w.jumpToStart
return w.insertPairAtCurrent } else if msg.Ch == 'G' {
} else if msg.Ch == 'P' { return w.jumpToEnd
return w.insertPairAtParent } else if msg.Key == termbox.KeyCtrlF {
} else if msg.Ch == 'b' { return w.pageDown
return w.insertBucketAtCurrent } else if msg.Key == termbox.KeyCtrlB {
} else if msg.Ch == 'B' { return w.pageUp
return w.insertBucketAtParent } else if msg.Ch == 'j' || msg.Key == termbox.KeyArrowDown {
} else if msg.Ch == 'e' { return w.moveCursorDown
return w.editPairValue } else if msg.Ch == 'k' || msg.Key == termbox.KeyArrowUp {
} else if msg.Ch == 'r' { return w.moveCursorUp
return w.renameBucketOrPair } else if msg.Key == termbox.KeyCtrlR {
} else if msg.Key == termbox.KeyEnter { return w.refreshDBAndBuffer
return w.handleEnterPressed } else if msg.Ch == 'p' {
} else if msg.Ch == 'l' || msg.Key == termbox.KeyArrowRight { return w.insertPairAtCurrent
return w.handleRightPressed } else if msg.Ch == 'P' {
} else if msg.Ch == 'h' || msg.Key == termbox.KeyArrowLeft { return w.insertPairAtParent
return w.handleLeftPressed } else if msg.Ch == 'b' {
} else if msg.Ch == 'D' { return w.insertBucketAtCurrent
return w.handleDelete } else if msg.Ch == 'B' {
} else if msg.Ch == 'x' { return w.insertBucketAtParent
// TODO: Export Value } else if msg.Ch == 'e' {
} else if msg.Ch == 'X' { return w.editPairValue
// TODO: Export Key/Value (or Bucket) as JSON } else if msg.Ch == 'r' {
} else if msg.Ch == '/' { return w.renameBucketOrPair
// TODO: Start Filtering } else if msg.Key == termbox.KeyEnter {
return w.handleEnterPressed
} else if msg.Ch == 'l' || msg.Key == termbox.KeyArrowRight {
return w.handleRightPressed
} else if msg.Ch == 'h' || msg.Key == termbox.KeyArrowLeft {
return w.handleLeftPressed
} else if msg.Ch == 'D' {
return w.handleDelete
} else if msg.Ch == 'x' {
// TODO: Export Value
} else if msg.Ch == 'X' {
// TODO: Export Key/Value (or Bucket) as JSON
} else if msg.Ch == '/' {
return func() wandle.Msg {
w.search.SetActive(true)
return nil
}
}
} }
} }
return nil return nil
@ -145,6 +165,9 @@ func (w *BoltTreePane) View(style wandle.Style) {
wandle.Print(midX, midY, style, txt) wandle.Print(midX, midY, style, txt)
return return
} }
if w.search.IsActive() || w.search.GetValue() != "" {
w.search.View(style)
}
// Find the cursor in the pane // Find the cursor in the pane
for k, v := range w.pathBuffer { for k, v := range w.pathBuffer {
if comparePaths(w.currentPath, v) { if comparePaths(w.currentPath, v) {
@ -230,9 +253,9 @@ func (w *BoltTreePane) RefreshBuffer() {
for i := range buckets { for i := range buckets {
w.buffer = append(w.buffer, buckets[i].Lines()...) w.buffer = append(w.buffer, buckets[i].Lines()...)
} }
w.pathBuffer, _ = w.db.BuildVisiblePathSlice(w.filter) w.pathBuffer, _ = w.db.BuildVisiblePathSlice(w.search.GetValue())
if len(w.currentPath) == 0 { if len(w.currentPath) == 0 {
w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) w.currentPath = w.db.GetNextVisiblePath(nil, w.search.GetValue())
} }
} }
@ -260,7 +283,7 @@ func (w *BoltTreePane) jumpCursorDown(distance int) {
} }
} }
if isCurPath { if isCurPath {
w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) w.currentPath = w.db.GetNextVisiblePath(nil, w.search.GetValue())
} }
w.UpdateDetailPane() w.UpdateDetailPane()
} }
@ -289,17 +312,17 @@ func (w *BoltTreePane) jumpCursorUp(distance int) {
} }
} }
if isCurPath { if isCurPath {
w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) w.currentPath = w.db.GetNextVisiblePath(nil, w.search.GetValue())
} }
w.UpdateDetailPane() w.UpdateDetailPane()
} }
func (w *BoltTreePane) jumpToStart() wandle.Msg { func (w *BoltTreePane) jumpToStart() wandle.Msg {
w.currentPath = w.db.GetNextVisiblePath(nil, w.filter) w.currentPath = w.db.GetNextVisiblePath(nil, w.search.GetValue())
w.UpdateDetailPane() w.UpdateDetailPane()
return nil return nil
} }
func (w *BoltTreePane) jumpToEnd() wandle.Msg { func (w *BoltTreePane) jumpToEnd() wandle.Msg {
w.currentPath = w.db.GetPrevVisiblePath(nil, w.filter) w.currentPath = w.db.GetPrevVisiblePath(nil, w.search.GetValue())
w.UpdateDetailPane() w.UpdateDetailPane()
return nil return nil
} }
@ -314,7 +337,7 @@ func (w *BoltTreePane) pageDown() wandle.Msg {
return nil return nil
} }
func (w *BoltTreePane) moveCursorUp() wandle.Msg { func (w *BoltTreePane) moveCursorUp() wandle.Msg {
p := w.db.GetPrevVisiblePath(w.currentPath, w.filter) p := w.db.GetPrevVisiblePath(w.currentPath, w.search.GetValue())
if p != nil { if p != nil {
w.currentPath = p w.currentPath = p
} }
@ -322,7 +345,7 @@ func (w *BoltTreePane) moveCursorUp() wandle.Msg {
return nil return nil
} }
func (w *BoltTreePane) moveCursorDown() wandle.Msg { func (w *BoltTreePane) moveCursorDown() wandle.Msg {
p := w.db.GetNextVisiblePath(w.currentPath, w.filter) p := w.db.GetNextVisiblePath(w.currentPath, w.search.GetValue())
if p != nil { if p != nil {
w.currentPath = p w.currentPath = p
} }

View File

@ -75,6 +75,7 @@ func (s *browseScreen) Init() wandle.Cmd {
} }
s.dbPath = dbs[dbidx] s.dbPath = dbs[dbidx]
s.treePane = NewBoltTreePane(0, 3, s.width/2, s.height-6) s.treePane = NewBoltTreePane(0, 3, s.width/2, s.height-6)
s.treePane.Init()
s.treePane.SetVisible(true) s.treePane.SetVisible(true)
s.treePane.SetInsertPairCommand(s.insertPair) s.treePane.SetInsertPairCommand(s.insertPair)
s.treePane.SetInsertBucketCommand(s.insertBucket) s.treePane.SetInsertBucketCommand(s.insertBucket)
@ -143,6 +144,9 @@ func (s *browseScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
return cmd return cmd
} }
if s.treePane.search.IsActive() {
return s.treePane.Update(msg)
}
var cmds []wandle.Cmd var cmds []wandle.Cmd
cmds = append(cmds, s.treePane.Update(msg)) cmds = append(cmds, s.treePane.Update(msg))
cmds = append(cmds, s.detailPane.Update(msg)) cmds = append(cmds, s.detailPane.Update(msg))