1package cmdline23import "strings"45// maxHistory is how many lines the history keeps.6const maxHistory = 10078// History is the lines run on the command line, oldest first. It lasts9// as long as the session: each `012 serve` session has its own.10type History struct {11 lines []string12}1314// Add puts line at the end of the history, moving it there if it's15// already in it.16func (h *History) Add(line string) {17 if line == "" {18 return19 }20 for i, l := range h.lines {21 if l == line {22 h.lines = append(h.lines[:i], h.lines[i+1:]...)23 break24 }25 }26 h.lines = append(h.lines, line)27 if len(h.lines) > maxHistory {28 h.lines = h.lines[len(h.lines)-maxHistory:]29 }30}3132// Lines are the lines in the history, oldest first.33func (h *History) Lines() []string { return h.lines }3435// browse is a walk through the history with Up and Down, as vim's: only36// lines starting with what was typed before the first Up.37type browse struct {38 on bool39 at int // the line shown, len(lines) for what was typed40 prefix string // what was typed41}4243// step moves d lines through h (-1 older, 1 newer) from what's shown,44// skipping lines that don't start with the prefix, and returns the text45// to show and whether there was a line to move to.46func (b *browse) step(h *History, typed string, d int) (string, bool) {47 if !b.on {48 *b = browse{on: true, at: len(h.lines), prefix: typed}49 }50 for i := b.at + d; i >= 0 && i <= len(h.lines); i += d {51 if i == len(h.lines) {52 b.at = i53 return b.prefix, true54 }55 if strings.HasPrefix(h.lines[i], b.prefix) {56 b.at = i57 return h.lines[i], true58 }59 }60 return "", false61}