src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/store_api.go (about) 1 package edit 2 3 import ( 4 "errors" 5 6 "src.elv.sh/pkg/cli" 7 "src.elv.sh/pkg/cli/histutil" 8 "src.elv.sh/pkg/cli/tk" 9 "src.elv.sh/pkg/eval" 10 "src.elv.sh/pkg/eval/vals" 11 "src.elv.sh/pkg/parse/parseutil" 12 "src.elv.sh/pkg/store/storedefs" 13 ) 14 15 var errStoreOffline = errors.New("store offline") 16 17 type cmdhistOpt struct{ CmdOnly, Dedup, NewestFirst bool } 18 19 func (o *cmdhistOpt) SetDefaultOptions() {} 20 21 func commandHistory(opts cmdhistOpt, fuser histutil.Store, out eval.ValueOutput) error { 22 if fuser == nil { 23 return errStoreOffline 24 } 25 cmds, err := fuser.AllCmds() 26 if err != nil { 27 return err 28 } 29 if opts.Dedup { 30 cmds = dedupCmds(cmds, opts.NewestFirst) 31 } else if opts.NewestFirst { 32 reverseCmds(cmds) 33 } 34 if opts.CmdOnly { 35 for _, cmd := range cmds { 36 err := out.Put(cmd.Text) 37 if err != nil { 38 return err 39 } 40 } 41 } else { 42 for _, cmd := range cmds { 43 err := out.Put(vals.MakeMap("id", cmd.Seq, "cmd", cmd.Text)) 44 if err != nil { 45 return err 46 } 47 } 48 } 49 return nil 50 } 51 52 func dedupCmds(allCmds []storedefs.Cmd, newestFirst bool) []storedefs.Cmd { 53 // Capacity allocation below is based on some personal empirical observation. 54 uniqCmds := make([]storedefs.Cmd, 0, len(allCmds)/4) 55 seenCmds := make(map[string]bool, len(allCmds)/4) 56 for i := len(allCmds) - 1; i >= 0; i-- { 57 if !seenCmds[allCmds[i].Text] { 58 seenCmds[allCmds[i].Text] = true 59 uniqCmds = append(uniqCmds, allCmds[i]) 60 } 61 } 62 if !newestFirst { 63 reverseCmds(uniqCmds) 64 } 65 return uniqCmds 66 } 67 68 // Reverse the order of commands, in place, in the slice. This reorders the 69 // command history between oldest or newest command being first in the slice. 70 func reverseCmds(cmds []storedefs.Cmd) { 71 for i, j := 0, len(cmds)-1; i < j; i, j = i+1, j-1 { 72 cmds[i], cmds[j] = cmds[j], cmds[i] 73 } 74 } 75 76 func insertLastWord(app cli.App, histStore histutil.Store) error { 77 codeArea, ok := focusedCodeArea(app) 78 if !ok { 79 return nil 80 } 81 c := histStore.Cursor("") 82 c.Prev() 83 cmd, err := c.Get() 84 if err != nil { 85 return err 86 } 87 words := parseutil.Wordify(cmd.Text) 88 if len(words) > 0 { 89 codeArea.MutateState(func(s *tk.CodeAreaState) { 90 s.Buffer.InsertAtDot(words[len(words)-1]) 91 }) 92 } 93 return nil 94 } 95 96 func initStoreAPI(app cli.App, nb eval.NsBuilder, fuser histutil.Store) { 97 nb.AddGoFns(map[string]any{ 98 "command-history": func(fm *eval.Frame, opts cmdhistOpt) error { 99 return commandHistory(opts, fuser, fm.ValueOutput()) 100 }, 101 "insert-last-word": func() { insertLastWord(app, fuser) }, 102 }) 103 }