github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/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" 13 ) 14 15 var errStoreOffline = errors.New("store offline") 16 17 //elvdoc:fn command-history 18 // 19 // ```elvish 20 // edit:command-history &cmd-only=$false &dedup=$false &newest-first 21 // ``` 22 // 23 // Outputs the command history. 24 // 25 // By default, each entry is represented as a map, with an `id` key key for the 26 // sequence number of the command, and a `cmd` key for the text of the command. 27 // If `&cmd-only` is `$true`, only the text of each command is output. 28 // 29 // All entries are output by default. If `&dedup` is `$true`, only the most 30 // recent instance of each command (when comparing just the `cmd` key) is 31 // output. 32 // 33 // Commands are are output in oldest to newest order by default. If 34 // `&newest-first` is `$true` the output is in newest to oldest order instead. 35 // 36 // As an example, either of the following extracts the text of the most recent 37 // command: 38 // 39 // ```elvish 40 // edit:command-history | put [(all)][-1][cmd] 41 // edit:command-history &cmd-only &newest-first | take 1 42 // ``` 43 // 44 // @cf builtin:dir-history 45 46 type cmdhistOpt struct{ CmdOnly, Dedup, NewestFirst bool } 47 48 func (o *cmdhistOpt) SetDefaultOptions() {} 49 50 func commandHistory(opts cmdhistOpt, fuser histutil.Store, ch chan<- interface{}) error { 51 if fuser == nil { 52 return errStoreOffline 53 } 54 cmds, err := fuser.AllCmds() 55 if err != nil { 56 return err 57 } 58 if opts.Dedup { 59 cmds = dedupCmds(cmds, opts.NewestFirst) 60 } else if opts.NewestFirst { 61 reverseCmds(cmds) 62 } 63 if opts.CmdOnly { 64 for _, cmd := range cmds { 65 ch <- cmd.Text 66 } 67 } else { 68 for _, cmd := range cmds { 69 ch <- vals.MakeMap("id", cmd.Seq, "cmd", cmd.Text) 70 } 71 } 72 return nil 73 } 74 75 func dedupCmds(allCmds []store.Cmd, newestFirst bool) []store.Cmd { 76 // Capacity allocation below is based on some personal empirical observation. 77 uniqCmds := make([]store.Cmd, 0, len(allCmds)/4) 78 seenCmds := make(map[string]bool, len(allCmds)/4) 79 for i := len(allCmds) - 1; i >= 0; i-- { 80 if !seenCmds[allCmds[i].Text] { 81 seenCmds[allCmds[i].Text] = true 82 uniqCmds = append(uniqCmds, allCmds[i]) 83 } 84 } 85 if !newestFirst { 86 reverseCmds(uniqCmds) 87 } 88 return uniqCmds 89 } 90 91 // Reverse the order of commands, in place, in the slice. This reorders the 92 // command history between oldest or newest command being first in the slice. 93 func reverseCmds(cmds []store.Cmd) { 94 for i, j := 0, len(cmds)-1; i < j; i, j = i+1, j-1 { 95 cmds[i], cmds[j] = cmds[j], cmds[i] 96 } 97 } 98 99 //elvdoc:fn insert-last-word 100 // 101 // Inserts the last word of the last command. 102 103 func insertLastWord(app cli.App, histStore histutil.Store) error { 104 c := histStore.Cursor("") 105 c.Prev() 106 cmd, err := c.Get() 107 if err != nil { 108 return err 109 } 110 words := parseutil.Wordify(cmd.Text) 111 if len(words) > 0 { 112 app.CodeArea().MutateState(func(s *tk.CodeAreaState) { 113 s.Buffer.InsertAtDot(words[len(words)-1]) 114 }) 115 } 116 return nil 117 } 118 119 func initStoreAPI(app cli.App, nb eval.NsBuilder, fuser histutil.Store) { 120 nb.AddGoFns("<edit>", map[string]interface{}{ 121 "command-history": func(fm *eval.Frame, opts cmdhistOpt) error { 122 return commandHistory(opts, fuser, fm.OutputChan()) 123 }, 124 "insert-last-word": func() { insertLastWord(app, fuser) }, 125 }) 126 }