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  }