github.com/sercand/please@v13.4.0+incompatible/src/query/completions.go (about) 1 package query 2 3 import ( 4 "fmt" 5 "os" 6 "path" 7 "strings" 8 9 "github.com/thought-machine/please/src/core" 10 "github.com/thought-machine/please/src/utils" 11 ) 12 13 // CompletionLabels produces a set of labels that complete a given input. 14 // The second return value is a set of labels to parse for (since the original set generally won't turn out to exist). 15 // The last return value is true if one or more of the inputs are a "hidden" target 16 // (i.e. name begins with an underscore). 17 func CompletionLabels(config *core.Configuration, args []string, repoRoot string) ([]core.BuildLabel, []core.BuildLabel, bool) { 18 if len(args) == 0 { 19 queryCompletionPackages(config, ".", repoRoot) 20 } else if !strings.Contains(args[0], ":") { 21 // Haven't picked a package yet so no parsing is necessary. 22 if strings.HasPrefix(args[0], "//") { 23 queryCompletionPackages(config, args[0][2:], repoRoot) 24 } else { 25 queryCompletionPackages(config, args[0], repoRoot) 26 } 27 } 28 hidden := false 29 for _, arg := range args { 30 hidden = hidden || strings.Contains(arg, ":_") 31 } 32 // Bash completion sometimes produces \: instead of just : (see issue #18). 33 // We silently fix that here since we've not yet worked out how to fix Bash itself :( 34 args[0] = strings.Replace(args[0], "\\:", ":", -1) 35 if strings.HasSuffix(args[0], ":") { 36 // Have to special-case this because it won't be a valid label. 37 labels := core.ParseBuildLabels([]string{args[0] + "all"}) 38 return []core.BuildLabel{{PackageName: labels[0].PackageName, Name: ""}}, labels, hidden 39 } 40 labels := core.ParseBuildLabels([]string{args[0]}) 41 return labels, []core.BuildLabel{{PackageName: labels[0].PackageName, Name: "all"}}, hidden 42 } 43 44 func queryCompletionPackages(config *core.Configuration, query, repoRoot string) { 45 packages := GetAllPackages(config, query, repoRoot) 46 // If there's only one package, we know it has to be that, but we don't present 47 // only one option otherwise bash completion will assume it's that. 48 if len(packages) == 1 { 49 fmt.Printf("/%s:\n", packages[0]) 50 fmt.Printf("/%s:all\n", packages[0]) 51 } else { 52 for _, pkg := range packages { 53 fmt.Printf("/%s\n", pkg) 54 } 55 } 56 os.Exit(0) // Don't need to run a full-blown parse, get out now. 57 } 58 59 // GetAllPackages returns a string slice of all the package labels, such as "//src/core/query" 60 func GetAllPackages(config *core.Configuration, query, repoRoot string) []string { 61 root := path.Join(repoRoot, query) 62 origRoot := root 63 if !core.PathExists(root) { 64 root = path.Dir(root) 65 } 66 packages := []string{} 67 for pkg := range utils.FindAllSubpackages(config, root, origRoot) { 68 if strings.HasPrefix(pkg, origRoot) { 69 packages = append(packages, pkg[len(repoRoot):]) 70 } 71 } 72 73 return packages 74 } 75 76 // Completions queries a set of possible completions for some build labels. 77 // If 'binary' is true it will complete only targets that are runnable binaries (but not tests). 78 // If 'test' is true it will similarly complete only targets that are tests. 79 // If 'hidden' is true then hidden targets (i.e. those with names beginning with an underscore) 80 // will be included as well. 81 func Completions(graph *core.BuildGraph, labels []core.BuildLabel, binary, test, hidden bool) { 82 for _, label := range labels { 83 count := 0 84 for _, target := range graph.PackageOrDie(label).AllTargets() { 85 if !strings.HasPrefix(target.Label.Name, label.Name) { 86 continue 87 } 88 if (binary && (!target.IsBinary || target.IsTest)) || (test && !target.IsTest) { 89 continue 90 } 91 if hidden || !strings.HasPrefix(target.Label.Name, "_") { 92 fmt.Printf("%s\n", target.Label) 93 count++ 94 } 95 } 96 if !binary && ((label.Name != "" && strings.HasPrefix("all", label.Name)) || (label.Name == "" && count > 1)) { 97 fmt.Printf("//%s:all\n", label.PackageName) 98 } 99 } 100 }