Skip to content

Commit

Permalink
Dynamically generate list of completions (#1595)
Browse files Browse the repository at this point in the history
* Dynamically generate list of completions

* Update contribution guide

* Add unit tests
  • Loading branch information
joelim-work authored Feb 11, 2024
1 parent ac32468 commit ab54329
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 138 deletions.
1 change: 0 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ Adding a new option usually requires the following steps:
- Add default option value to `init` function in `opts.go`
- Add option evaluation logic to `setExpr.eval` in `eval.go`
- Implement the option somewhere in the code
- Add option name to `gOptWords` in `complete.go` for tab completion
- Add option name and its default value to `Quick Reference` and `Options` sections in `doc.md`
- Run `gen/doc-with-docker.sh` to update the documentation
- Commit your changes and send a pull request
Expand Down
174 changes: 37 additions & 137 deletions complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
)
Expand Down Expand Up @@ -109,146 +110,45 @@ var (
"cmd-lowercase-word",
}

gOptWords = []string{
"anchorfind",
"noanchorfind",
"anchorfind!",
"autoquit",
"noautoquit",
"autoquit!",
"borderfmt",
"copyfmt",
"cursoractivefmt",
"cursorparentfmt",
"cursorpreviewfmt",
"cutfmt",
"hidecursorinactive",
"nohidecursorinactive",
"hidecursorinactive!",
"dircache",
"nodircache",
"dircache!",
"dircounts",
"nodircounts",
"dircounts!",
"dirfirst",
"nodirfirst",
"dirfirst!",
"dironly",
"nodironly",
"dironly!",
"dirpreviews",
"nodirpreviews",
"dirpreviews!",
"drawbox",
"nodrawbox",
"drawbox!",
"dupfilefmt",
"globsearch",
"noglobsearch",
"globsearch!",
"hidden",
"nohidden",
"hidden!",
"history",
"nohistory",
"history!",
"icons",
"noicons",
"icons!",
"ignorecase",
"noignorecase",
"ignorecase!",
"ignoredia",
"noignoredia",
"ignoredia!",
"incsearch",
"noincsearch",
"incsearch!",
"incfilter",
"noincfilter",
"incfilter!",
"mouse",
"nomouse",
"mouse!",
"number",
"nonumber",
"number!",
"preview",
"nopreview",
"preview!",
"relativenumber",
"norelativenumber",
"relativenumber!",
"reverse",
"noreverse",
"reverse!",
"ruler",
"rulerfmt",
"preserve",
"selectfmt",
"sixel",
"nosixel",
"sixel!",
"smartcase",
"nosmartcase",
"smartcase!",
"smartdia",
"nosmartdia",
"smartdia!",
"waitmsg",
"wrapscan",
"nowrapscan",
"wrapscan!",
"wrapscroll",
"nowrapscroll",
"wrapscroll!",
"findlen",
"period",
"scrolloff",
"tabstop",
"errorfmt",
"filesep",
"hiddenfiles",
"ifs",
"info",
"numberfmt",
"previewer",
"cleaner",
"promptfmt",
"ratios",
"selmode",
"shell",
"shellflag",
"shellopts",
"sortby",
"statfmt",
"timefmt",
"tempmarks",
"tagfmt",
"infotimefmtnew",
"infotimefmtold",
"truncatechar",
"truncatepct",
gOptWords = getOptWords(gOpts)
gLocalOptWords = getLocalOptWords(gLocalOpts)
)

func getOptWords(opts any) (optWords []string) {
t := reflect.TypeOf(opts)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
switch field.Type.Kind() {
case reflect.Map:
continue
case reflect.Bool:
name := field.Name
optWords = append(optWords, name, "no"+name, name+"!")
default:
optWords = append(optWords, field.Name)
}
}
sort.Strings(optWords)
return
}

gLocalOptWords = []string{
"dirfirst",
"nodirfirst",
"dirfirst!",
"dironly",
"nodironly",
"dironly!",
"hidden",
"nohidden",
"hidden!",
"info",
"reverse",
"noreverse",
"reverse!",
"sortby",
func getLocalOptWords(localOpts any) (localOptWords []string) {
t := reflect.TypeOf(localOpts)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
name := strings.TrimSuffix(field.Name, "s")
if field.Type.Kind() != reflect.Map {
continue
}
if field.Type.Elem().Kind() == reflect.Bool {
localOptWords = append(localOptWords, name, "no"+name, name+"!")
} else {
localOptWords = append(localOptWords, name)
}
}
)
sort.Strings(localOptWords)
return
}

func matchLongest(s1, s2 []rune) []rune {
i := 0
Expand Down
38 changes: 38 additions & 0 deletions complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,41 @@ func TestMatchWord(t *testing.T) {
}
}
}

func TestGetOptWords(t *testing.T) {
tests := []struct {
opts any
exp []string
}{
{struct{ feature bool }{}, []string{"feature", "feature!", "nofeature"}},
{struct{ feature int }{}, []string{"feature"}},
{struct{ feature string }{}, []string{"feature"}},
{struct{ feature []string }{}, []string{"feature"}},
}

for _, test := range tests {
result := getOptWords(test.opts)
if !reflect.DeepEqual(result, test.exp) {
t.Errorf("at input '%#v' expected '%s' but got '%s'", test.opts, test.exp, result)
}
}
}

func TestGetLocalOptWords(t *testing.T) {
tests := []struct {
localOpts any
exp []string
}{
{struct{ features map[string]bool }{}, []string{"feature", "feature!", "nofeature"}},
{struct{ features map[string]int }{}, []string{"feature"}},
{struct{ features map[string]string }{}, []string{"feature"}},
{struct{ features map[string][]string }{}, []string{"feature"}},
}

for _, test := range tests {
result := getLocalOptWords(test.localOpts)
if !reflect.DeepEqual(result, test.exp) {
t.Errorf("at input '%#v' expected '%s' but got '%s'", test.localOpts, test.exp, result)
}
}
}

0 comments on commit ab54329

Please sign in to comment.