Fixed Paths

No need to track paths, just recurse over parents.
Also fixed an issue with some mode tracking.

Preparing to add ability to 'rename' buckets
This commit is contained in:
Brian Buller 2015-05-15 16:07:05 -05:00
parent f951ce9b64
commit be134e0632
2 changed files with 137 additions and 113 deletions

View File

@ -15,7 +15,6 @@ type BoltDB struct {
type BoltBucket struct { type BoltBucket struct {
name string name string
path []string
pairs []BoltPair pairs []BoltPair
buckets []BoltBucket buckets []BoltBucket
parent *BoltBucket parent *BoltBucket
@ -24,7 +23,6 @@ type BoltBucket struct {
} }
type BoltPair struct { type BoltPair struct {
path []string
parent *BoltBucket parent *BoltBucket
key string key string
val string val string
@ -86,7 +84,7 @@ func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
var ret_err error var ret_err 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].path) n, err := bd.getVisibleItemCount(bd.buckets[i].GetPath())
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -105,7 +103,7 @@ func (bd *BoltDB) getVisibleItemCount(path []string) (int, error) {
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].path) n, err := bd.getVisibleItemCount(b.buckets[i].GetPath())
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -132,45 +130,6 @@ func (bd *BoltDB) buildVisiblePathSlice() ([]string, error) {
return ret_slice, ret_err return ret_slice, ret_err
} }
/*
func (bd *BoltDB) buildVisiblePathSlice_old(path []string) ([]string, error) {
var ret_slice []string
var ret_err error
if len(path) == 0 {
for i := range bd.buckets {
n, err := bd.buildVisiblePathSlice(bd.buckets[i].path)
if err != nil {
return nil, err
}
ret_slice = append(ret_slice, n...)
}
} else {
b, err := bd.getBucketFromPath(path)
if err != nil {
return nil, err
}
// Add the bucket's path
ret_slice = append(ret_slice, strings.Join(b.path, "/"))
if b.expanded {
// This bucket is expanded, include it's children
// * recurse for buckets
for i := range b.buckets {
n, err := bd.buildVisiblePathSlice(b.buckets[i].path)
if err != nil {
return nil, err
}
ret_slice = append(ret_slice, n...)
}
// * one path for each pair
for i := range b.pairs {
ret_slice = append(ret_slice, strings.Join(b.pairs[i].path, "/"))
}
}
}
return ret_slice, ret_err
}
*/
func (bd *BoltDB) getPrevVisiblePath(path []string) []string { func (bd *BoltDB) getPrevVisiblePath(path []string) []string {
vis_paths, err := bd.buildVisiblePathSlice() vis_paths, err := bd.buildVisiblePathSlice()
if path == nil { if path == nil {
@ -265,10 +224,8 @@ func (bd *BoltDB) refreshDatabase() *BoltDB {
bb, err := readBucket(b) bb, err := readBucket(b)
if err == nil { if err == nil {
bb.name = string(nm) bb.name = string(nm)
bb.setPath([]string{bb.name})
bb.expanded = false bb.expanded = false
memBolt.buckets = append(memBolt.buckets, *bb) memBolt.buckets = append(memBolt.buckets, *bb)
//updatePaths(bb)
return nil return nil
} }
return err return err
@ -278,8 +235,11 @@ func (bd *BoltDB) refreshDatabase() *BoltDB {
} }
func (b *BoltBucket) GetPath() []string { func (b *BoltBucket) GetPath() []string {
b.path[len(b.path)-1] = b.name if b.parent != nil {
return b.path return append(b.parent.GetPath(), b.name)
} else {
return []string{b.name}
}
} }
func (b *BoltBucket) buildVisiblePathSlice(prefix []string) ([]string, error) { func (b *BoltBucket) buildVisiblePathSlice(prefix []string) ([]string, error) {
@ -337,19 +297,14 @@ func (b *BoltBucket) getPair(k string) (*BoltPair, error) {
return nil, errors.New("Pair Not Found") return nil, errors.New("Pair Not Found")
} }
func (b *BoltBucket) setPath(p []string) { func (p *BoltPair) GetPath() []string {
b.path = p return append(p.parent.GetPath(), p.key)
for i := range b.buckets {
b.buckets[i].setPath(append(p, b.buckets[i].name))
}
for i := range b.pairs {
b.pairs[i].path = append(p, b.pairs[i].key)
}
} }
func deleteKey(path []string) error { func deleteKey(path []string) error {
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key // len(b.path)-1 is the key we need to delete,
// the rest are buckets leading to that key
if len(path) == 1 { if len(path) == 1 {
// Deleting a root bucket // Deleting a root bucket
return tx.DeleteBucket([]byte(path[0])) return tx.DeleteBucket([]byte(path[0]))
@ -401,17 +356,6 @@ func readBucket(b *bolt.Bucket) (*BoltBucket, error) {
return bb, nil return bb, nil
} }
func updatePaths(b *BoltBucket) {
for i := range b.buckets {
b.buckets[i].path = append(b.path, b.buckets[i].name)
updatePaths(&b.buckets[i])
}
for i := range b.pairs {
b.pairs[i].path = append(b.path, b.pairs[i].key)
}
}
/* /*
func renameBucket(path []string, name string) error { func renameBucket(path []string, name string) error {
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
@ -437,9 +381,41 @@ func renameBucket(path []string, name string) error {
return err return err
} }
*/ */
func updatePairKey(path []string, k string) error {
err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key for the pair we're updating,
// the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0]))
if b != nil {
if len(path) > 0 {
for i := range path[1 : len(path)-1] {
b = b.Bucket([]byte(path[i+1]))
if b == nil {
return errors.New("updatePairValue: Invalid Path")
}
}
}
bk := []byte(path[len(path)-1])
v := b.Get(bk)
err := b.Delete(bk)
if err == nil {
// Old pair has been deleted, now add the new one
err = b.Put([]byte(k), v)
}
// Now update the last key in the path
return err
} else {
return errors.New("updatePairValue: Invalid Path")
}
})
return err
}
func updatePairValue(path []string, v string) error { func updatePairValue(path []string, v string) error {
err := db.Update(func(tx *bolt.Tx) error { err := db.Update(func(tx *bolt.Tx) error {
// len(b.path)-1 is the key we need to delete, the rest are buckets leading to that key // len(b.GetPath())-1 is the key for the pair we're updating,
// the rest are buckets leading to that key
b := tx.Bucket([]byte(path[0])) b := tx.Bucket([]byte(path[0]))
if b != nil { if b != nil {
if len(path) > 0 { if len(path) > 0 {
@ -528,10 +504,19 @@ func insertPair(path []string, k string, v string) error {
var f *os.File var f *os.File
func logToFile(s string) { func logToFile(s string) error {
var err error
if f == nil { if f == nil {
f, _ = os.OpenFile("bolt-log", os.O_RDWR|os.O_APPEND, 0660) f, err = os.OpenFile("bolt-log", os.O_RDWR|os.O_APPEND, 0660)
} }
f.WriteString(s + "\n") if err != nil {
f.Sync() return err
}
if _, err = f.WriteString(s + "\n"); err != nil {
return err
}
if err = f.Sync(); err != nil {
return err
}
return nil
} }

View File

@ -32,14 +32,17 @@ type BrowserScreen struct {
type BrowserMode int type BrowserMode int
const ( const (
MODE_BROWSE = 16 // 0001 0000 MODE_BROWSE = 16 // 0000 0001 0000
MODE_CHANGE_VAL = 32 // 0010 0000 MODE_CHANGE = 32 // 0000 0010 0000
MODE_INSERT_BUCKET = 48 // 0011 0000 MODE_CHANGE_KEY = 33 // 0000 0010 0001
MODE_INSERT_PAIR = 64 // 0100 0000 MODE_CHANGE_VAL = 34 // 0000 0010 0010
MODE_INSERT_PAIR_KEY = 65 // 0100 0001 MODE_INSERT = 64 // 0000 0100 0000
MODE_INSERT_PAIR_VAL = 66 // 0100 0010 MODE_INSERT_BUCKET = 65 // 0000 0100 0001
MODE_DELETE = 128 // 1000 0000 MODE_INSERT_PAIR = 68 // 0000 0100 0100
MODE_MOD_TO_PARENT = 8 // 0000 1000 MODE_INSERT_PAIR_KEY = 69 // 0000 0100 0101
MODE_INSERT_PAIR_VAL = 70 // 0000 0100 0110
MODE_DELETE = 256 // 0001 0000 0000
MODE_MOD_TO_PARENT = 8 // 0000 0000 1000
) )
type BoltType int type BoltType int
@ -55,9 +58,9 @@ func (screen *BrowserScreen) handleKeyEvent(event termbox.Event) int {
} }
if screen.mode == MODE_BROWSE { if screen.mode == MODE_BROWSE {
return screen.handleBrowseKeyEvent(event) return screen.handleBrowseKeyEvent(event)
} else if screen.mode == MODE_CHANGE_VAL { } else if screen.mode&MODE_CHANGE == MODE_CHANGE {
return screen.handleInputKeyEvent(event) return screen.handleInputKeyEvent(event)
} else if screen.mode&MODE_INSERT_BUCKET == MODE_INSERT_BUCKET || screen.mode&MODE_INSERT_PAIR == MODE_INSERT_PAIR { } else if screen.mode&MODE_INSERT == MODE_INSERT {
return screen.handleInsertKeyEvent(event) return screen.handleInsertKeyEvent(event)
} else if screen.mode == MODE_DELETE { } else if screen.mode == MODE_DELETE {
return screen.handleDeleteKeyEvent(event) return screen.handleDeleteKeyEvent(event)
@ -119,11 +122,14 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
} else if event.Ch == 'e' { } else if event.Ch == 'e' {
b, p, _ := screen.db.getGenericFromPath(screen.current_path) b, p, _ := screen.db.getGenericFromPath(screen.current_path)
if b != nil { if b != nil {
screen.setMessage("Cannot edit a bucket") screen.setMessage("Cannot edit a bucket, did you mean to (r)ename?")
} else if p != nil { } else if p != nil {
screen.startEditItem() screen.startEditItem()
} }
} else if event.Ch == 'r' {
screen.startRenameItem()
} else if event.Key == termbox.KeyEnter { } else if event.Key == termbox.KeyEnter {
b, p, _ := screen.db.getGenericFromPath(screen.current_path) b, p, _ := screen.db.getGenericFromPath(screen.current_path)
if b != nil { if b != nil {
@ -152,9 +158,9 @@ func (screen *BrowserScreen) handleBrowseKeyEvent(event termbox.Event) int {
if len(screen.current_path) > 1 { if len(screen.current_path) > 1 {
parent_bucket, err := screen.db.getBucketFromPath(screen.current_path[:len(screen.current_path)-1]) parent_bucket, err := screen.db.getBucketFromPath(screen.current_path[:len(screen.current_path)-1])
if err == nil { if err == nil {
screen.db.closeBucket(parent_bucket.path) screen.db.closeBucket(parent_bucket.GetPath())
// Figure out how far up we need to move the cursor // Figure out how far up we need to move the cursor
screen.current_path = parent_bucket.path screen.current_path = parent_bucket.GetPath()
} }
} else { } else {
screen.db.closeBucket(screen.current_path) screen.db.closeBucket(screen.current_path)
@ -174,15 +180,30 @@ func (screen *BrowserScreen) handleInputKeyEvent(event termbox.Event) int {
} else { } else {
screen.input_modal.HandleKeyPress(event) screen.input_modal.HandleKeyPress(event)
if screen.input_modal.IsDone() { if screen.input_modal.IsDone() {
new_val := screen.input_modal.GetValue() if screen.mode == MODE_CHANGE_KEY {
_, p, _ := screen.db.getGenericFromPath(screen.current_path) new_key := screen.input_modal.GetValue()
if p != nil { _, p, _ := screen.db.getGenericFromPath(screen.current_path)
if updatePairValue(screen.current_path, new_val) != nil { if p != nil {
screen.setMessage("Error occurred updating Pair.") if updatePairKey(screen.current_path, new_key) != nil {
} else { screen.setMessage("Error occurred updating Pair.")
p.val = new_val } else {
screen.setMessage("Pair updated!") p.key = new_key
screen.refreshDatabase() screen.current_path[len(screen.current_path)-1] = p.key
screen.setMessage("Pair updated!")
screen.refreshDatabase()
}
}
} else if screen.mode == MODE_CHANGE_VAL {
new_val := screen.input_modal.GetValue()
_, p, _ := screen.db.getGenericFromPath(screen.current_path)
if p != nil {
if updatePairValue(screen.current_path, new_val) != nil {
screen.setMessage("Error occurred updating Pair.")
} else {
p.val = new_val
screen.setMessage("Pair updated!")
screen.refreshDatabase()
}
} }
} }
screen.mode = MODE_BROWSE screen.mode = MODE_BROWSE
@ -239,6 +260,7 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int {
} }
} else { } else {
screen.input_modal.HandleKeyPress(event) screen.input_modal.HandleKeyPress(event)
logToFile("> Insert Key Event")
if screen.input_modal.IsDone() { if screen.input_modal.IsDone() {
new_val := screen.input_modal.GetValue() new_val := screen.input_modal.GetValue()
screen.input_modal.Clear() screen.input_modal.Clear()
@ -264,18 +286,22 @@ func (screen *BrowserScreen) handleInsertKeyEvent(event termbox.Event) int {
} }
if screen.mode&MODE_INSERT_BUCKET == MODE_INSERT_BUCKET { if screen.mode&MODE_INSERT_BUCKET == MODE_INSERT_BUCKET {
logToFile(">> Inserting Bucket")
screen.setMessage(strings.Join(insert_path, "/")) screen.setMessage(strings.Join(insert_path, "/"))
err := insertBucket(insert_path, new_val) err := insertBucket(insert_path, new_val)
if err != nil { if err != nil {
screen.setMessage(fmt.Sprintf("%s => %s", err, insert_path)) screen.setMessage(fmt.Sprintf("%s => %s", err, insert_path))
logToFile(fmt.Sprintf(">> >> Error: %s", err))
} }
screen.refreshDatabase() screen.refreshDatabase()
screen.mode = MODE_BROWSE screen.mode = MODE_BROWSE
screen.input_modal.Clear() screen.input_modal.Clear()
} else if screen.mode&MODE_INSERT_PAIR == MODE_INSERT_PAIR { } else if screen.mode&MODE_INSERT_PAIR == MODE_INSERT_PAIR {
logToFile(">> Inserting Pair")
err := insertPair(insert_path, new_val, "") err := insertPair(insert_path, new_val, "")
if err != nil { if err != nil {
screen.setMessage(fmt.Sprintf("%s => %s", err, insert_path)) screen.setMessage(fmt.Sprintf("%s => %s", err, insert_path))
logToFile(fmt.Sprintf(">> >> Error: %s", err))
screen.refreshDatabase() screen.refreshDatabase()
screen.mode = MODE_BROWSE screen.mode = MODE_BROWSE
screen.input_modal.Clear() screen.input_modal.Clear()
@ -387,7 +413,6 @@ func (screen *BrowserScreen) drawFooter(style Style) {
_, height := termbox.Size() _, height := termbox.Size()
termbox_util.DrawStringAtPoint(screen.message, 0, height-1, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(screen.message, 0, height-1, style.default_fg, style.default_bg)
} }
func (screen *BrowserScreen) drawLeftPane(style Style) { func (screen *BrowserScreen) drawLeftPane(style Style) {
w, h := termbox.Size() w, h := termbox.Size()
if w >= 80 { if w >= 80 {
@ -426,7 +451,6 @@ func (screen *BrowserScreen) drawLeftPane(style Style) {
y += bkt_h y += bkt_h
} }
} }
func (screen *BrowserScreen) drawRightPane(style Style) { func (screen *BrowserScreen) drawRightPane(style Style) {
w, h := termbox.Size() w, h := termbox.Size()
if w >= 80 { if w >= 80 {
@ -443,7 +467,7 @@ func (screen *BrowserScreen) drawRightPane(style Style) {
termbox_util.DrawStringAtPoint(fmt.Sprintf("Buckets: %d", len(b.buckets)), start_x, start_y+1, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(fmt.Sprintf("Buckets: %d", len(b.buckets)), start_x, start_y+1, style.default_fg, style.default_bg)
termbox_util.DrawStringAtPoint(fmt.Sprintf("Pairs: %d", len(b.pairs)), start_x, start_y+2, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(fmt.Sprintf("Pairs: %d", len(b.pairs)), start_x, start_y+2, style.default_fg, style.default_bg)
} else if p != nil { } else if p != nil {
termbox_util.DrawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(p.path, "/")), start_x, start_y, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(fmt.Sprintf("Path: %s", strings.Join(p.GetPath(), "/")), start_x, start_y, style.default_fg, style.default_bg)
termbox_util.DrawStringAtPoint(fmt.Sprintf("Key: %s", p.key), start_x, start_y+1, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(fmt.Sprintf("Key: %s", p.key), start_x, start_y+1, style.default_fg, style.default_bg)
termbox_util.DrawStringAtPoint(fmt.Sprintf("Value: %s", p.val), start_x, start_y+2, style.default_fg, style.default_bg) termbox_util.DrawStringAtPoint(fmt.Sprintf("Value: %s", p.val), start_x, start_y+2, style.default_fg, style.default_bg)
} }
@ -471,16 +495,7 @@ func (screen *BrowserScreen) drawBucket(bkt *BoltBucket, style Style, y int) int
bucket_bg = style.cursor_bg bucket_bg = style.cursor_bg
} }
if len(bkt.path) == 0 { bkt_string := strings.Repeat(" ", len(bkt.GetPath())*2) //screen.db.getDepthFromPath(bkt.GetPath())*2)
// No bucket should have a 0 length path...
if bkt.parent == nil {
bkt.path = []string{bkt.name}
} else {
bkt.path = append(bkt.parent.path, bkt.name)
}
}
bkt_string := strings.Repeat(" ", len(bkt.path)*2) //screen.db.getDepthFromPath(bkt.path)*2)
if bkt.expanded { if bkt.expanded {
bkt_string = bkt_string + "- " + bkt.name + " " bkt_string = bkt_string + "- " + bkt.name + " "
bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string)))) bkt_string = fmt.Sprintf("%s%s", bkt_string, strings.Repeat(" ", (w-len(bkt_string))))
@ -510,14 +525,16 @@ func (screen *BrowserScreen) drawPair(bp *BoltPair, style Style, y int) int {
} }
bucket_fg := style.default_fg bucket_fg := style.default_fg
bucket_bg := style.default_bg bucket_bg := style.default_bg
if comparePaths(screen.current_path, bp.path) { if comparePaths(screen.current_path, bp.GetPath()) {
bucket_fg = style.cursor_fg bucket_fg = style.cursor_fg
bucket_bg = style.cursor_bg bucket_bg = style.cursor_bg
} }
pair_string := strings.Repeat(" ", len(bp.path)*2) //screen.db.getDepthFromPath(bp.path)*2) pair_string := strings.Repeat(" ", len(bp.GetPath())*2) //screen.db.getDepthFromPath(bp.GetPath())*2)
pair_string = fmt.Sprintf("%s%s: %s", pair_string, bp.key, bp.val) pair_string = fmt.Sprintf("%s%s: %s", pair_string, bp.key, bp.val)
pair_string = fmt.Sprintf("%s%s", pair_string, strings.Repeat(" ", (w-len(pair_string)))) if w-len(pair_string) > 0 {
pair_string = fmt.Sprintf("%s%s", pair_string, strings.Repeat(" ", (w-len(pair_string))))
}
termbox_util.DrawStringAtPoint(pair_string, 0, y, bucket_fg, bucket_bg) termbox_util.DrawStringAtPoint(pair_string, 0, y, bucket_fg, bucket_bg)
return 1 return 1
} }
@ -544,6 +561,25 @@ func (screen *BrowserScreen) startDeleteItem() bool {
} }
func (screen *BrowserScreen) startEditItem() bool { func (screen *BrowserScreen) startEditItem() bool {
_, p, e := screen.db.getGenericFromPath(screen.current_path)
if e == nil {
w, h := termbox.Size()
inp_w, inp_h := (w / 2), 6
inp_x, inp_y := ((w / 2) - (inp_w / 2)), ((h / 2) - inp_h)
mod := termbox_util.CreateInputModal("", inp_x, inp_y, inp_w, inp_h, termbox.ColorWhite, termbox.ColorBlack)
if p != nil {
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Input new value for '%s'", p.key), inp_w, termbox_util.ALIGN_CENTER))
mod.SetValue(p.val)
}
mod.Show()
screen.input_modal = mod
screen.mode = MODE_CHANGE_VAL
return true
}
return false
}
func (screen *BrowserScreen) startRenameItem() bool {
b, p, e := screen.db.getGenericFromPath(screen.current_path) b, p, e := screen.db.getGenericFromPath(screen.current_path)
if e == nil { if e == nil {
w, h := termbox.Size() w, h := termbox.Size()
@ -554,12 +590,12 @@ func (screen *BrowserScreen) startEditItem() bool {
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Rename Bucket '%s' to:", b.name), inp_w, termbox_util.ALIGN_CENTER)) mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Rename Bucket '%s' to:", b.name), inp_w, termbox_util.ALIGN_CENTER))
mod.SetValue(b.name) mod.SetValue(b.name)
} else if p != nil { } else if p != nil {
mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Input new value for '%s'", p.key), inp_w, termbox_util.ALIGN_CENTER)) mod.SetTitle(termbox_util.AlignText(fmt.Sprintf("Rename Key '%s' to:", p.key), inp_w, termbox_util.ALIGN_CENTER))
mod.SetValue(p.val) mod.SetValue(p.key)
} }
mod.Show() mod.Show()
screen.input_modal = mod screen.input_modal = mod
screen.mode = MODE_CHANGE_VAL screen.mode = MODE_CHANGE_KEY
return true return true
} }
return false return false
@ -615,15 +651,18 @@ func (screen *BrowserScreen) startInsertItem(tp BoltType) bool {
} else { } else {
ins_path = strings.Join(screen.current_path, "/") + "/" ins_path = strings.Join(screen.current_path, "/") + "/"
} }
logToFile(" startInsertItem")
if tp == TYPE_BUCKET { if tp == TYPE_BUCKET {
mod.SetTitle(termbox_util.AlignText("Create Bucket at "+ins_path, inp_w, termbox_util.ALIGN_CENTER)) mod.SetTitle(termbox_util.AlignText("Create Bucket at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
screen.mode = MODE_INSERT_BUCKET screen.mode = MODE_INSERT_BUCKET
logToFile(" MODE_INSERT_BUCKET")
mod.Show() mod.Show()
return true return true
} else if tp == TYPE_PAIR { } else if tp == TYPE_PAIR {
mod.SetTitle(termbox_util.AlignText("Create Pair at "+ins_path, inp_w, termbox_util.ALIGN_CENTER)) mod.SetTitle(termbox_util.AlignText("Create Pair at "+ins_path, inp_w, termbox_util.ALIGN_CENTER))
mod.Show() mod.Show()
screen.mode = MODE_INSERT_PAIR screen.mode = MODE_INSERT_PAIR
logToFile(" MODE_INSERT_PAIR")
return true return true
} }
return false return false