github.com/oweisse/u-root@v0.0.0-20181109060735-d005ad25fef1/cmds/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/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  }