github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/cli/mode/histlist.go (about) 1 package mode 2 3 import ( 4 "fmt" 5 6 "src.elv.sh/pkg/cli" 7 "src.elv.sh/pkg/cli/tk" 8 "src.elv.sh/pkg/store" 9 "src.elv.sh/pkg/ui" 10 ) 11 12 // Histlist is a mode for browsing history and selecting entries to insert. It 13 // is based on the ComboBox widget. 14 type Histlist interface { 15 tk.ComboBox 16 } 17 18 // HistlistSpec specifies the configuration for the histlist mode. 19 type HistlistSpec struct { 20 // Key bindings. 21 Bindings tk.Bindings 22 // AllCmds is called to retrieve all commands. 23 AllCmds func() ([]store.Cmd, error) 24 // Dedup is called to determine whether deduplication should be done. 25 // Defaults to true if unset. 26 Dedup func() bool 27 // Configuration for the filter. 28 Filter FilterSpec 29 } 30 31 // NewHistlist creates a new histlist mode. 32 func NewHistlist(app cli.App, spec HistlistSpec) (Histlist, error) { 33 if spec.AllCmds == nil { 34 return nil, errNoHistoryStore 35 } 36 if spec.Dedup == nil { 37 spec.Dedup = func() bool { return true } 38 } 39 40 cmds, err := spec.AllCmds() 41 if err != nil { 42 return nil, fmt.Errorf("db error: %v", err.Error()) 43 } 44 last := map[string]int{} 45 for i, cmd := range cmds { 46 last[cmd.Text] = i 47 } 48 cmdItems := histlistItems{cmds, last} 49 50 w := tk.NewComboBox(tk.ComboBoxSpec{ 51 CodeArea: tk.CodeAreaSpec{ 52 Prompt: func() ui.Text { 53 content := " HISTORY " 54 if spec.Dedup() { 55 content += "(dedup on) " 56 } 57 return modeLine(content, true) 58 }, 59 Highlighter: spec.Filter.Highlighter, 60 }, 61 ListBox: tk.ListBoxSpec{ 62 Bindings: spec.Bindings, 63 OnAccept: func(it tk.Items, i int) { 64 text := it.(histlistItems).entries[i].Text 65 app.CodeArea().MutateState(func(s *tk.CodeAreaState) { 66 buf := &s.Buffer 67 if buf.Content == "" { 68 buf.InsertAtDot(text) 69 } else { 70 buf.InsertAtDot("\n" + text) 71 } 72 }) 73 app.SetAddon(nil, false) 74 }, 75 }, 76 OnFilter: func(w tk.ComboBox, p string) { 77 it := cmdItems.filter(spec.Filter.makePredicate(p), spec.Dedup()) 78 w.ListBox().Reset(it, it.Len()-1) 79 }, 80 }) 81 return w, nil 82 } 83 84 type histlistItems struct { 85 entries []store.Cmd 86 last map[string]int 87 } 88 89 func (it histlistItems) filter(p func(string) bool, dedup bool) histlistItems { 90 var filtered []store.Cmd 91 for i, entry := range it.entries { 92 text := entry.Text 93 if dedup && it.last[text] != i { 94 continue 95 } 96 if p(text) { 97 filtered = append(filtered, entry) 98 } 99 } 100 return histlistItems{filtered, nil} 101 } 102 103 func (it histlistItems) Show(i int) ui.Text { 104 entry := it.entries[i] 105 // TODO: The alignment of the index works up to 10000 entries. 106 return ui.T(fmt.Sprintf("%4d %s", entry.Seq, entry.Text)) 107 } 108 109 func (it histlistItems) Len() int { return len(it.entries) }