-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd.go
149 lines (134 loc) · 3.66 KB
/
cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package store
import (
"bytes"
"encoding/binary"
bolt "go.etcd.io/bbolt"
. "github.com/markusbkk/elvish/pkg/store/storedefs"
)
func init() {
initDB["initialize command history table"] = func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(bucketCmd))
return err
}
}
// NextCmdSeq returns the next sequence number of the command history.
func (s *dbStore) NextCmdSeq() (int, error) {
var seq uint64
err := s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
seq = b.Sequence() + 1
return nil
})
return int(seq), err
}
// AddCmd adds a new command to the command history.
func (s *dbStore) AddCmd(cmd string) (int, error) {
var (
seq uint64
err error
)
err = s.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
seq, err = b.NextSequence()
if err != nil {
return err
}
return b.Put(marshalSeq(seq), []byte(cmd))
})
return int(seq), err
}
// DelCmd deletes a command history item with the given sequence number.
func (s *dbStore) DelCmd(seq int) error {
return s.db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
return b.Delete(marshalSeq(uint64(seq)))
})
}
// Cmd queries the command history item with the specified sequence number.
func (s *dbStore) Cmd(seq int) (string, error) {
var cmd string
err := s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
v := b.Get(marshalSeq(uint64(seq)))
if v == nil {
return ErrNoMatchingCmd
}
cmd = string(v)
return nil
})
return cmd, err
}
// IterateCmds iterates all the commands in the specified range, and calls the
// callback with the content of each command sequentially.
func (s *dbStore) IterateCmds(from, upto int, f func(Cmd)) error {
return s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
c := b.Cursor()
for k, v := c.Seek(marshalSeq(uint64(from))); k != nil && unmarshalSeq(k) < uint64(upto); k, v = c.Next() {
f(Cmd{Text: string(v), Seq: int(unmarshalSeq(k))})
}
return nil
})
}
// CmdsWithSeq returns all commands within the specified range.
func (s *dbStore) CmdsWithSeq(from, upto int) ([]Cmd, error) {
var cmds []Cmd
err := s.IterateCmds(from, upto, func(cmd Cmd) {
cmds = append(cmds, cmd)
})
return cmds, err
}
// NextCmd finds the first command after the given sequence number (inclusive)
// with the given prefix.
func (s *dbStore) NextCmd(from int, prefix string) (Cmd, error) {
var cmd Cmd
err := s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
c := b.Cursor()
p := []byte(prefix)
for k, v := c.Seek(marshalSeq(uint64(from))); k != nil; k, v = c.Next() {
if bytes.HasPrefix(v, p) {
cmd = Cmd{Text: string(v), Seq: int(unmarshalSeq(k))}
return nil
}
}
return ErrNoMatchingCmd
})
return cmd, err
}
// PrevCmd finds the last command before the given sequence number (exclusive)
// with the given prefix.
func (s *dbStore) PrevCmd(upto int, prefix string) (Cmd, error) {
var cmd Cmd
err := s.db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketCmd))
c := b.Cursor()
p := []byte(prefix)
var v []byte
k, _ := c.Seek(marshalSeq(uint64(upto)))
if k == nil { // upto > LAST
k, v = c.Last()
if k == nil {
return ErrNoMatchingCmd
}
} else {
k, v = c.Prev() // upto exists, find the previous one
}
for ; k != nil; k, v = c.Prev() {
if bytes.HasPrefix(v, p) {
cmd = Cmd{Text: string(v), Seq: int(unmarshalSeq(k))}
return nil
}
}
return ErrNoMatchingCmd
})
return cmd, err
}
func marshalSeq(seq uint64) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, seq)
return b
}
func unmarshalSeq(key []byte) uint64 {
return binary.BigEndian.Uint64(key)
}