github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/cmds/core/elvish/store/cmd.go (about) 1 package store 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "strings" 7 "sync" 8 9 "github.com/u-root/u-root/cmds/core/elvish/store/storedefs" 10 ) 11 12 // The numbering is 1-based, not 0-based. Oh well. 13 type CmdHistory struct { 14 sync.Mutex 15 cmds map[int]string 16 cur int 17 max int 18 } 19 20 func (history *CmdHistory) Seek(i int) (int, error) { 21 history.Lock() 22 defer history.Unlock() 23 history.cur = history.cur + i 24 switch { 25 case history.cur < 1: 26 history.cur = 1 27 } 28 if history.cur > history.max { 29 history.max = history.cur 30 } 31 return history.cur, nil 32 } 33 34 // convenience functions 35 func (history *CmdHistory) Cur() (int, error) { 36 return history.Seek(0) 37 } 38 39 func (history *CmdHistory) Prev() (int, error) { 40 return history.Seek(-1) 41 } 42 43 func (history *CmdHistory) Next() (int, error) { 44 return history.Seek(1) 45 } 46 47 // AddCmd adds a new command to the command history. 48 func (history *CmdHistory) Add(cmd string) (int, error) { 49 history.Lock() 50 defer history.Unlock() 51 history.cur = len(history.cmds) + 1 52 history.cmds[history.cur] = cmd 53 if history.cur > history.max { 54 history.max = history.cur 55 } 56 return history.cur, nil 57 } 58 59 // Remove removes a command from command history referenced by 60 // sequence. 61 func (history *CmdHistory) Remove(seq int) error { 62 history.Lock() 63 defer history.Unlock() 64 delete(history.cmds, seq) 65 return nil 66 } 67 68 // Cmd queries the command history item with the specified sequence number. 69 func (history *CmdHistory) One(seq int) (string, error) { 70 history.Lock() 71 defer history.Unlock() 72 c, ok := history.cmds[seq] 73 if !ok { 74 return "", storedefs.ErrNoMatchingCmd 75 } 76 return c, nil 77 } 78 79 // IterateCmds iterates all the commands in the specified range, and calls the 80 // callback with the content of each command sequentially. 81 func (history *CmdHistory) Walk(from, upto int, f func(string) bool) error { 82 history.Lock() 83 defer history.Unlock() 84 for i := from; i < upto; i++ { 85 v, ok := history.cmds[i] 86 if !ok { 87 continue 88 } 89 if !f(v) { 90 return fmt.Errorf("%s fails", v) 91 } 92 } 93 94 return nil 95 } 96 97 // Cmds returns the contents of all commands within the specified range. 98 func (history *CmdHistory) List(from, upto int) ([]string, error) { 99 history.Lock() 100 defer history.Unlock() 101 var list []string 102 for i := from; i < upto; i++ { 103 v, ok := history.cmds[i] 104 if !ok { 105 continue 106 } 107 list = append(list, v) 108 } 109 return list, nil 110 } 111 112 // Search finds the first command after the given sequence number (inclusive) 113 // with the given prefix. 114 func (history *CmdHistory) Search(from int, prefix string) (int, string, error) { 115 l, _ := history.List(from, history.max) 116 for i, v := range l { 117 if strings.HasPrefix(v, prefix) { 118 return i + from, v, nil 119 } 120 } 121 122 return 0, "", storedefs.ErrNoMatchingCmd 123 } 124 125 // PrevCmd finds the last command before the given sequence number (exclusive) 126 // with the given prefix. 127 func (history *CmdHistory) RSearch(upto int, prefix string) (int, string, error) { 128 l, _ := history.List(1, upto) 129 for i := range l { 130 cmd := l[len(l)-i-1] 131 if strings.HasPrefix(cmd, prefix) { 132 return upto - i - 1, cmd, nil 133 } 134 } 135 return 0, "", storedefs.ErrNoMatchingCmd 136 } 137 138 func marshalSeq(seq uint64) []byte { 139 b := make([]byte, 8) 140 binary.BigEndian.PutUint64(b, uint64(seq)) 141 return b 142 } 143 144 func unmarshalSeq(key []byte) uint64 { 145 return binary.BigEndian.Uint64(key) 146 } 147 148 func NewCmdHistory(s ...string) storedefs.Store { 149 c := &CmdHistory{cmds: make(map[int]string)} 150 for _, v := range s { 151 c.Add(v) 152 } 153 return c 154 }