github.com/elves/elvish@v0.15.0/pkg/cli/addons/completion/completion.go (about) 1 // Package completion implements the UI for showing, filtering and inserting 2 // completion candidates. 3 package completion 4 5 import ( 6 "strings" 7 8 "github.com/elves/elvish/pkg/cli" 9 "github.com/elves/elvish/pkg/diag" 10 "github.com/elves/elvish/pkg/ui" 11 ) 12 13 // Item represents a completion item, also known as a candidate. 14 type Item struct { 15 // Used in the UI and for filtering. 16 ToShow string 17 // Style to use in the UI. 18 ShowStyle ui.Style 19 // Used when inserting a candidate. 20 ToInsert string 21 } 22 23 // Config keeps the configuration for the completion UI. 24 type Config struct { 25 Binding cli.Handler 26 Name string 27 Replace diag.Ranging 28 Items []Item 29 } 30 31 // Start starts the completion UI. 32 func Start(app cli.App, cfg Config) { 33 if len(cfg.Items) == 0 { 34 app.Notify("no candidates") 35 return 36 } 37 w := cli.NewComboBox(cli.ComboBoxSpec{ 38 CodeArea: cli.CodeAreaSpec{ 39 Prompt: cli.ModePrompt(" COMPLETING "+cfg.Name+" ", true), 40 }, 41 ListBox: cli.ListBoxSpec{ 42 Horizontal: true, 43 OverlayHandler: cfg.Binding, 44 OnSelect: func(it cli.Items, i int) { 45 text := it.(items)[i].ToInsert 46 app.CodeArea().MutateState(func(s *cli.CodeAreaState) { 47 s.Pending = cli.PendingCode{ 48 From: cfg.Replace.From, To: cfg.Replace.To, Content: text} 49 }) 50 }, 51 OnAccept: func(it cli.Items, i int) { 52 app.CodeArea().MutateState(func(s *cli.CodeAreaState) { 53 s.ApplyPending() 54 }) 55 app.MutateState(func(s *cli.State) { s.Addon = nil }) 56 }, 57 ExtendStyle: true, 58 }, 59 OnFilter: func(w cli.ComboBox, p string) { 60 w.ListBox().Reset(filter(cfg.Items, p), 0) 61 }, 62 }) 63 app.MutateState(func(s *cli.State) { s.Addon = w }) 64 app.Redraw() 65 } 66 67 // Close closes the completion UI. 68 func Close(app cli.App) { 69 app.CodeArea().MutateState( 70 func(s *cli.CodeAreaState) { s.Pending = cli.PendingCode{} }) 71 app.MutateState(func(s *cli.State) { s.Addon = nil }) 72 app.Redraw() 73 } 74 75 type items []Item 76 77 func filter(all []Item, p string) items { 78 var filtered []Item 79 for _, candidate := range all { 80 if strings.Contains(candidate.ToShow, p) { 81 filtered = append(filtered, candidate) 82 } 83 } 84 return filtered 85 } 86 87 func (it items) Show(i int) ui.Text { 88 return ui.Text{&ui.Segment{Style: it[i].ShowStyle, Text: it[i].ToShow}} 89 } 90 91 func (it items) Len() int { return len(it) }