elvish/pkg/store/cmd.go
Kurtis Rader eb2a792301 Fix some lint warnings
In addition to an uncontroversial spelling fix this addresses several,
related, warnings produced by the `golint` tool. In general I agree with
golint that unnecessary "else" blocks should be avoided. So this change
refactors those cases.

Note: I recognize that `golint` is deprecated (see
https://github.com/golang/go/issues/38968) since it is no longer being
maintained and there is controversy about its set of warnings. Nonetheless,
it appears that the warnings it emits for this project are all reasonable
and actionable with one potential exception: the naming of the `map_`
method in pkg/eval/compile_value.go.
2020-08-01 23:07:50 +01:00

160 lines
3.9 KiB
Go

package store
import (
"bytes"
"encoding/binary"
bolt "go.etcd.io/bbolt"
)
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
})
}
// Cmds returns the contents of all commands within the specified range.
//
// NOTE: Deprecated as of 0.13. Delete after release of 0.14.
func (s *dbStore) Cmds(from, upto int) ([]string, error) {
var cmds []string
err := s.IterateCmds(from, upto, func(cmd Cmd) {
cmds = append(cmds, cmd.Text)
})
return cmds, err
}
// 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)
}