| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- package main
- import (
- "sort"
- "strconv"
- "strings"
- "github.com/spf13/cobra"
- intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
- "netdome.biz/paul/qmk-rgb/internal/via"
- )
- // Shell completion. The tool knows how to resolve a zone name and which profiles
- // exist, and a shell only offers what the binary tells it about, so the binary has
- // to say it. None of these open the keyboard: a shell asks while nothing is
- // plugged in, and an error printed into a prompt is worse than an empty list.
- // completeZoneNames offers the QMK subsystem names, which follow from the channel
- // number and work on any board, plus the names the definition files give their
- // channels, which are what a board calls them in VIA.
- func completeZoneNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- var names []string
- for _, ch := range via.LightingChannels {
- names = append(names, ch.Subsystem())
- }
- if defs, err := intrgb.LoadDefinitionsDir(definitionsPath()); err == nil {
- for _, def := range defs {
- for _, label := range def.Labels {
- names = append(names, label)
- }
- }
- }
- return narrow(names, toComplete), cobra.ShellCompDirectiveNoFileComp
- }
- // completeDeviceNumbers offers the keyboard numbers keyboard info prints, which
- // come from enumeration and need no open keyboard.
- func completeDeviceNumbers(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- devices, err := discoverAll()
- if err != nil {
- return nil, cobra.ShellCompDirectiveNoFileComp
- }
- var numbers []string
- for _, d := range devices {
- numbers = append(numbers, strconv.Itoa(d.Index))
- }
- return narrow(numbers, toComplete), cobra.ShellCompDirectiveNoFileComp
- }
- // completeProfileNames offers the profiles that can be read, because load and
- // delete take a name that is a file in the profiles directory.
- func completeProfileNames(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
- names, err := ListProfiles()
- if err != nil {
- return nil, cobra.ShellCompDirectiveNoFileComp
- }
- return narrow(names, toComplete), cobra.ShellCompDirectiveNoFileComp
- }
- // narrow keeps the candidates a prefix matches, in order and without repeats: a
- // completion list with duplicates reads as a bug, and one that ignores what has
- // been typed makes the shell do the work it came here for.
- func narrow(names []string, prefix string) []string {
- seen := make(map[string]bool, len(names))
- var out []string
- for _, name := range names {
- if prefix != "" && !strings.HasPrefix(name, prefix) {
- continue
- }
- if seen[name] {
- continue
- }
- seen[name] = true
- out = append(out, name)
- }
- sort.Strings(out)
- return out
- }
- // registerFlagCompletions teaches the shell what the persistent flags accept. The
- // definition file is a path, so the shell completes paths for it on its own.
- func registerFlagCompletions(root *cobra.Command) {
- _ = root.RegisterFlagCompletionFunc("zone", completeZoneNames)
- _ = root.RegisterFlagCompletionFunc("device", completeDeviceNumbers)
- _ = root.MarkFlagFilename("definition", "*.json")
- }
|