github.com/joshmeranda/wrash@v0.4.2/pkg/history.go (about) 1 package wrash 2 3 import ( 4 "fmt" 5 "io" 6 7 prompt "github.com/joshmeranda/go-prompt" 8 "gopkg.in/yaml.v3" 9 ) 10 11 type Entry struct { 12 Base string 13 Cmd string 14 15 changes string 16 } 17 18 type history struct { 19 entries []*Entry 20 21 current int 22 base string 23 w io.Writer 24 } 25 26 func (h *history) Add(inputs ...string) { 27 h.entries = h.entries[:len(h.entries)-1] 28 29 var lastEntry *Entry 30 if len(h.entries) >= 1 { 31 lastEntry = h.entries[len(h.entries)-1] 32 } 33 34 for _, s := range inputs { 35 if s == "" || lastEntry != nil && s == lastEntry.Cmd { 36 continue 37 } 38 39 base := h.base 40 if isBuiltin(s) { 41 base = "" 42 } 43 44 h.entries = append(h.entries, &Entry{ 45 Base: base, 46 Cmd: s, 47 }) 48 } 49 h.entries = append(h.entries, &Entry{ 50 Base: h.base, 51 }) 52 } 53 54 func (h *history) Clear() { 55 h.current = len(h.entries) - 1 56 for _, entry := range h.entries { 57 entry.changes = "" 58 } 59 } 60 61 func (h *history) nextOlder(text string) (*Entry, bool) { 62 if len(h.entries) == 1 || h.current == 0 { 63 return nil, false 64 } 65 66 entry := h.entries[h.current] 67 if entry.Cmd != text { 68 entry.changes = text 69 } 70 71 h.current-- 72 entry = h.entries[h.current] 73 74 return entry, true 75 } 76 77 func (h *history) Older(buf *prompt.Buffer) (*prompt.Buffer, bool) { 78 for next, ok := h.nextOlder(buf.Text()); ok; next, ok = h.nextOlder(buf.Text()) { 79 if next.Base == h.base || isBuiltin(next.Cmd) { 80 var text string 81 if next.changes != "" { 82 text = next.changes 83 } else { 84 text = next.Cmd 85 } 86 87 new := prompt.NewBuffer() 88 new.InsertText(text, false, true) 89 90 return new, true 91 } 92 } 93 94 return buf, false 95 } 96 97 func (h *history) nextNewer(text string) (*Entry, bool) { 98 if h.current == len(h.entries)-1 { 99 return nil, false 100 } 101 102 entry := h.entries[h.current] 103 if entry.Cmd != text { 104 entry.changes = text 105 } 106 107 h.current++ 108 entry = h.entries[h.current] 109 110 return entry, true 111 } 112 113 func (h *history) Newer(buf *prompt.Buffer) (*prompt.Buffer, bool) { 114 for next, ok := h.nextNewer(buf.Text()); ok; next, ok = h.nextNewer(buf.Text()) { 115 if next.Base == h.base || isBuiltin(next.Cmd) { 116 var text string 117 if next.changes != "" { 118 text = next.changes 119 } else { 120 text = next.Cmd 121 } 122 123 new := prompt.NewBuffer() 124 new.InsertText(text, false, true) 125 126 return new, true 127 } 128 } 129 fmt.Println() 130 131 return buf, false 132 } 133 134 func (h *history) Sync() error { 135 data, err := yaml.Marshal(h.entries[:len(h.entries)-1]) 136 if err != nil { 137 return fmt.Errorf("could not marshal history entries: %w", err) 138 } 139 140 if _, err := h.w.Write(data); err != nil { 141 return fmt.Errorf("could not sync history: %w", err) 142 } 143 144 return nil 145 } 146 147 func NewHistory(base string, w io.Writer, entries []*Entry) prompt.History { 148 newEntries := make([]*Entry, len(entries), len(entries)+1) 149 copy(newEntries, entries) 150 newEntries = append(newEntries, &Entry{ 151 Base: base, 152 }) 153 154 return &history{ 155 entries: newEntries, 156 current: len(newEntries) - 1, 157 base: base, 158 w: w, 159 } 160 }