diff --git a/dclish/completer.go b/dclish/completer.go new file mode 100644 index 0000000000000000000000000000000000000000..82d7f674573a6f90b89b9497dcbd4d9cedc55cce --- /dev/null +++ b/dclish/completer.go @@ -0,0 +1,81 @@ +package dclish + +import ( + "strings" + "unicode" +) + +// Completer command completer type. +type Completer struct { + commands []string + flags map[string][]string +} + +// NewCompleter creates a new completer. +func NewCompleter(commands Commands) Completer { + comps := []string{} + flags := map[string][]string{} + for c := range commands { + comps = append(comps, c) + if len(commands[c].Commands) > 0 { + subcommands := commands[c].Commands + for subc := range subcommands { + fullc := c + " " + subc + comps = append(comps, fullc) + flags[fullc] = []string{} + if subcommands[subc].Flags != nil { + for f := range subcommands[subc].Flags { + flags[fullc] = append(flags[fullc], f) + } + } + } + } + flags[c] = []string{} + if commands[c].Flags != nil { + for f := range commands[c].Flags { + flags[c] = append(flags[c], f) + } + } + } + return Completer{ + commands: comps, + flags: flags, + } +} + +// Do return a list of possible completions. +func (c Completer) Do(line []rune, pos int) ([][]rune, int) { + // Nothing typed in. + if pos == 0 { + newline := make([][]rune, len(c.commands)) + for i := range c.commands { + newline[i] = []rune(c.commands[i]) + } + return newline, pos + } + + // Command partially typed in. + newline := [][]rune{} + cmd := strings.ToUpper(string(line[0:pos])) + lower := false + if unicode.IsLower(line[0]) { + lower = true + } + for i := range c.commands { + if strings.HasPrefix(c.commands[i], cmd) { + rest := strings.Replace(c.commands[i], cmd, "", 1) + if lower { + newline = append(newline, []rune(strings.ToLower(rest))) + } else { + newline = append(newline, []rune(rest)) + } + } + } + if len(newline) > 0 { + return newline, pos + } + + // Command completely typed in. + // TODO: figure out flags. + return newline, pos +} diff --git a/dclish/dclish.go b/dclish/dclish.go index 7736360d7616b5ebbf3465acd6d80425b7ef2707..c0cb79be4e8269919e00afcb85e4cec0228a45e9 100644 --- a/dclish/dclish.go +++ b/dclish/dclish.go @@ -10,6 +10,10 @@ import ( // ActionFunc is the function that a command runs. type ActionFunc func(*Command) error +// CompleterFunc is a function to provide completions for arguments for +// a given command. +type CompleterFunc func() []string + // Flag is a flag for a command. type Flag struct { OptArg bool @@ -30,6 +34,7 @@ type Command struct { MinArgs int Commands Commands Action ActionFunc + Completer CompleterFunc Description string } diff --git a/repl/repl.go b/repl/repl.go index 7dba0206789274073830df090039c7cdeadcbc12..1f69333aaf10bbfc0f6dfe257c4a00cd4579436d 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -8,6 +8,7 @@ import ( "strings" "unicode" + "git.lyda.ie/kevin/bulletin/dclish" "git.lyda.ie/kevin/bulletin/this" "github.com/adrg/xdg" "github.com/chzyer/readline" @@ -15,14 +16,15 @@ import ( // Loop is the main event loop. func Loop() error { + completer := dclish.NewCompleter(commands) histdir := path.Join(xdg.ConfigHome, "BULLETIN") os.MkdirAll(histdir, 0700) histfile := path.Join(histdir, fmt.Sprintf("%s.history", this.User.Login)) rl, err := readline.NewEx( &readline.Config{ - Prompt: "BULLETIN> ", - HistoryFile: histfile, - // TODO: AutoComplete: completer, + Prompt: "BULLETIN> ", + HistoryFile: histfile, + AutoComplete: completer, InterruptPrompt: "^C", EOFPrompt: "EXIT", HistorySearchFold: true,