2019 Complete!
This commit is contained in:
264
2019/day25/cli_processor.go
Normal file
264
2019/day25/cli_processor.go
Normal file
@@ -0,0 +1,264 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/nsf/termbox-go"
|
||||
)
|
||||
|
||||
type CliProc struct {
|
||||
History []string
|
||||
HistoryIdx int
|
||||
Suggestions []string // Suggestions are 'autocomplete' commands
|
||||
Proposals []string // Proposals are proposed 'next' commands
|
||||
|
||||
Buffer string
|
||||
Cursor int
|
||||
|
||||
Complete bool
|
||||
}
|
||||
|
||||
func NewCLI() *CliProc {
|
||||
c := CliProc{}
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *CliProc) handleEvent(event termbox.Event) error {
|
||||
if c.Complete {
|
||||
return nil
|
||||
}
|
||||
if event.Ch == 0 {
|
||||
switch event.Key {
|
||||
case termbox.KeyEnter:
|
||||
// Execute command
|
||||
c.Complete = true
|
||||
|
||||
case termbox.KeySpace:
|
||||
c.putInBuffer(" ")
|
||||
|
||||
case termbox.KeyBackspace, termbox.KeyBackspace2:
|
||||
if c.Cursor > 0 {
|
||||
c.Buffer = c.Buffer[:c.Cursor-1] + c.Buffer[c.Cursor:]
|
||||
c.Cursor = c.Cursor - 1
|
||||
}
|
||||
case termbox.KeyDelete:
|
||||
if c.Cursor < len(c.Buffer) {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + c.Buffer[c.Cursor+1:]
|
||||
}
|
||||
|
||||
case termbox.KeyArrowLeft:
|
||||
if c.Cursor > 0 {
|
||||
c.Cursor = c.Cursor - 1
|
||||
}
|
||||
case termbox.KeyArrowRight:
|
||||
if c.Cursor < len(c.Buffer) {
|
||||
c.Cursor = c.Cursor + 1
|
||||
} else {
|
||||
// Complete suggestion
|
||||
}
|
||||
case termbox.KeyHome:
|
||||
c.Cursor = 0
|
||||
case termbox.KeyEnd:
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyArrowUp:
|
||||
val, err := c.HistoryBack()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Buffer = val
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyArrowDown:
|
||||
val, err := c.HistoryForward()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Buffer = val
|
||||
c.Cursor = len(c.Buffer)
|
||||
|
||||
case termbox.KeyTab:
|
||||
// Complete suggestion
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
acText := strings.TrimPrefix(c.GetAutocompleteText(), args[len(args)-1])
|
||||
if len(acText) > 0 {
|
||||
c.Buffer = c.Buffer + acText
|
||||
c.Cursor = len(c.Buffer)
|
||||
}
|
||||
|
||||
case termbox.KeyCtrlU:
|
||||
// Delete everything from the cursor forward
|
||||
c.Buffer = c.Buffer[c.Cursor:]
|
||||
c.Cursor = 0
|
||||
}
|
||||
} else {
|
||||
c.putInBuffer(string(event.Ch))
|
||||
/*
|
||||
if c.Cursor == len(c.Buffer) {
|
||||
c.Buffer = c.Buffer + string(event.Ch)
|
||||
} else {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + string(event.Ch) + c.Buffer[c.Cursor:]
|
||||
}
|
||||
c.Cursor = c.Cursor + 1
|
||||
*/
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CliProc) putInBuffer(v string) {
|
||||
c.Buffer = c.Buffer[:c.Cursor] + v + c.Buffer[c.Cursor:]
|
||||
c.Cursor = c.Cursor + 1
|
||||
}
|
||||
|
||||
func (c *CliProc) ClearBuffer() {
|
||||
c.Buffer = ""
|
||||
c.Cursor = 0
|
||||
c.Complete = false
|
||||
}
|
||||
|
||||
func (c *CliProc) GetBuffer() string {
|
||||
return c.Buffer
|
||||
}
|
||||
|
||||
func (c *CliProc) HistoryBack() (string, error) {
|
||||
if len(c.History) < c.HistoryIdx+1 {
|
||||
return "", errors.New("Already at oldest command")
|
||||
}
|
||||
c.HistoryIdx = c.HistoryIdx + 1
|
||||
return c.History[len(c.History)-c.HistoryIdx], nil
|
||||
}
|
||||
|
||||
func (c *CliProc) HistoryForward() (string, error) {
|
||||
if c.HistoryIdx == 0 {
|
||||
return "", errors.New("Already at most recent command")
|
||||
}
|
||||
c.HistoryIdx = c.HistoryIdx - 1
|
||||
if c.HistoryIdx == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return c.History[len(c.History)-c.HistoryIdx], nil
|
||||
}
|
||||
|
||||
func (c *CliProc) Suggest(cmd string) string {
|
||||
allSuggestions := c.GetAllSuggestions(cmd)
|
||||
if len(allSuggestions) == 0 {
|
||||
return ""
|
||||
}
|
||||
return allSuggestions[0]
|
||||
}
|
||||
|
||||
func (c *CliProc) GetDisplaySuggestions() []string {
|
||||
var ret []string
|
||||
v := c.GetAllSuggestions(c.Buffer)
|
||||
if len(c.Buffer) > 0 {
|
||||
for k := range v {
|
||||
if strings.HasPrefix(v[k], c.Buffer) {
|
||||
ret = append(ret, v[k])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normally, filter out 'drop's
|
||||
for k := range v {
|
||||
if !strings.HasPrefix(v[k], "drop ") {
|
||||
ret = append(ret, v[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CliProc) GetAllSuggestions(cmd string) []string {
|
||||
var ret []string
|
||||
args := strings.Fields(cmd)
|
||||
if len(args) == 0 {
|
||||
// Return all commands
|
||||
for _, v := range c.Suggestions {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
return ret
|
||||
} else if len(args) == 1 && !strings.HasSuffix(cmd, " ") {
|
||||
for _, v := range c.Suggestions {
|
||||
if strings.HasPrefix(v, args[0]) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *CliProc) Draw(x, y int, bg, fg termbox.Attribute) {
|
||||
DrawString("[ "+strings.Join(c.GetDisplaySuggestions(), ", ")+" ]", x, y, bg, fg)
|
||||
y = y + 1
|
||||
prompt := '>'
|
||||
termbox.SetCell(x, y, prompt, bg, fg)
|
||||
x = x + 2
|
||||
for k := range c.Buffer {
|
||||
useFg, useBg := bg, fg
|
||||
if k == c.Cursor {
|
||||
useFg, useBg = fg, bg
|
||||
}
|
||||
termbox.SetCell(x+k, y, rune(c.Buffer[k]), useFg, useBg)
|
||||
}
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
acText := strings.TrimPrefix(c.GetAutocompleteText(), args[len(args)-1])
|
||||
if len(acText) > 0 {
|
||||
for k := range acText {
|
||||
useFg, useBg := bg|termbox.AttrBold, fg
|
||||
if k == 0 && c.Cursor == len(c.Buffer) {
|
||||
useFg, useBg = fg|termbox.AttrBold, bg
|
||||
}
|
||||
termbox.SetCell(x+len(c.Buffer)+k, y, rune(acText[k]), useFg, useBg)
|
||||
}
|
||||
} else {
|
||||
if c.Cursor == len(c.Buffer) {
|
||||
termbox.SetCell(x+len(c.Buffer), y, ' ', fg, bg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CliProc) GetAutocompleteText() string {
|
||||
suggestions := c.GetAllSuggestions(c.Buffer)
|
||||
if len(suggestions) == 1 {
|
||||
args := strings.Split(c.Buffer, " ")
|
||||
return strings.TrimPrefix(suggestions[0], args[len(args)-1])
|
||||
} else if len(suggestions) > 1 {
|
||||
// Find how many characters we _can_ autocomplete
|
||||
var ret string
|
||||
for k := range suggestions[0] {
|
||||
allHave := true
|
||||
for wrk := range suggestions {
|
||||
if len(suggestions[wrk]) <= k || suggestions[0][k] != suggestions[wrk][k] {
|
||||
allHave = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allHave {
|
||||
ret = ret + string(suggestions[0][k])
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *CliProc) ClearSuggestions() {
|
||||
c.Suggestions = nil
|
||||
}
|
||||
|
||||
func (c *CliProc) AddSuggestion(s string) {
|
||||
c.AddSuggestions([]string{s})
|
||||
}
|
||||
func (c *CliProc) AddSuggestions(s []string) {
|
||||
c.Suggestions = append(c.Suggestions, s...)
|
||||
}
|
||||
|
||||
func (c *CliProc) RemoveSuggestion(s string) {
|
||||
var i int
|
||||
for i = range c.Suggestions {
|
||||
if c.Suggestions[i] == s {
|
||||
break
|
||||
}
|
||||
}
|
||||
c.Suggestions = append(c.Suggestions[:i], c.Suggestions[i+1:]...)
|
||||
}
|
||||
Reference in New Issue
Block a user