github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/store/cmd.go (about)

     1  package store
     2  
     3  import (
     4  	"database/sql"
     5  
     6  	"github.com/elves/elvish/store/storedefs"
     7  )
     8  
     9  func init() {
    10  	initDB["initialize command history table"] = func(db *sql.DB) error {
    11  		_, err := db.Exec(`CREATE TABLE IF NOT EXISTS cmd (content text)`)
    12  		return err
    13  	}
    14  }
    15  
    16  // NextCmdSeq returns the next sequence number of the command history.
    17  func (s *Store) NextCmdSeq() (int, error) {
    18  	row := s.db.QueryRow(`SELECT ifnull(max(rowid), 0) + 1 FROM cmd`)
    19  	var seq int
    20  	err := row.Scan(&seq)
    21  	return seq, err
    22  }
    23  
    24  // AddCmd adds a new command to the command history.
    25  func (s *Store) AddCmd(cmd string) (int, error) {
    26  	r, err := s.db.Exec(`INSERT INTO cmd (content) VALUES(?)`, cmd)
    27  	if err != nil {
    28  		return -1, err
    29  	}
    30  	i, err := r.LastInsertId()
    31  	return int(i), err
    32  }
    33  
    34  // Cmd queries the command history item with the specified sequence number.
    35  func (s *Store) Cmd(seq int) (string, error) {
    36  	row := s.db.QueryRow(`SELECT content FROM cmd WHERE rowid = ?`, seq)
    37  	var cmd string
    38  	err := row.Scan(&cmd)
    39  	return cmd, err
    40  }
    41  
    42  // IterateCmds iterates all the commands in the specified range, and calls the
    43  // callback with the content of each command sequentially.
    44  func (s *Store) IterateCmds(from, upto int, f func(string) bool) error {
    45  	rows, err := s.db.Query(`SELECT content FROM cmd WHERE rowid >= ? AND rowid < ?`, from, upto)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer rows.Close()
    50  	for rows.Next() {
    51  		var cmd string
    52  		err = rows.Scan(&cmd)
    53  		if err != nil {
    54  			break
    55  		}
    56  		if !f(cmd) {
    57  			break
    58  		}
    59  	}
    60  	return err
    61  }
    62  
    63  // Cmds returns the contents of all commands within the specified range.
    64  func (s *Store) Cmds(from, upto int) ([]string, error) {
    65  	var cmds []string
    66  	err := s.IterateCmds(from, upto, func(cmd string) bool {
    67  		cmds = append(cmds, cmd)
    68  		return true
    69  	})
    70  	return cmds, err
    71  }
    72  
    73  // NextCmd finds the first command after the given sequence number (inclusive)
    74  // with the given prefix.
    75  func (s *Store) NextCmd(from int, prefix string) (int, string, error) {
    76  	row := s.db.QueryRow(`SELECT rowid, content FROM cmd WHERE rowid >= ? AND substr(content, 1, ?) = ? ORDER BY rowid asc LIMIT 1`, from, len(prefix), prefix)
    77  	return convertCmd(row)
    78  }
    79  
    80  // PrevCmd finds the last command before the given sequence number (exclusive)
    81  // with the given prefix.
    82  func (s *Store) PrevCmd(upto int, prefix string) (int, string, error) {
    83  	var upto64 = int64(upto)
    84  	if upto < 0 {
    85  		upto64 = 0x7FFFFFFFFFFFFFFF
    86  	}
    87  	row := s.db.QueryRow(`SELECT rowid, content FROM cmd WHERE rowid < ? AND substr(content, 1, ?) = ? ORDER BY rowid DESC LIMIT 1`, upto64, len(prefix), prefix)
    88  	return convertCmd(row)
    89  }
    90  
    91  func convertCmd(row *sql.Row) (seq int, cmd string, err error) {
    92  	err = row.Scan(&seq, &cmd)
    93  	if err == sql.ErrNoRows {
    94  		err = storedefs.ErrNoMatchingCmd
    95  	}
    96  	return
    97  }