Skip to content

Commit 8dfba6e

Browse files
committed
carapace-generate: improve completers performance
still got unneccessary invocations and repetitive steps
1 parent be118d9 commit 8dfba6e

File tree

2 files changed

+90
-74
lines changed

2 files changed

+90
-74
lines changed

cmd/carapace-generate/cmd/completers.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
var completersCmd = &cobra.Command{
15-
Use: "completers DIR [GOOS]",
15+
Use: "completers DIR target",
1616
Short: "",
1717
Args: cobra.ExactArgs(2),
1818
RunE: func(cmd *cobra.Command, args []string) error {
@@ -58,6 +58,6 @@ func init() {
5858

5959
carapace.Gen(completersCmd).PositionalCompletion(
6060
carapace.ActionDirectories(),
61-
carapace.ActionValues("linux", "darwin", "windows", "force_all"), // TODO others, termux as well?
61+
carapace.ActionValues("android", "linux", "darwin", "windows", "force_all"), // TODO others, termux as well?
6262
)
6363
}

cmd/carapace-generate/pkg/completer/completers.go

Lines changed: 88 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package completer
22

33
import (
4+
"bufio"
45
"encoding/json"
5-
"fmt"
6+
"io/fs"
67
"os"
78
"os/exec"
89
"path/filepath"
@@ -11,6 +12,7 @@ import (
1112

1213
"github.com/carapace-sh/carapace"
1314
"github.com/carapace-sh/carapace-bin/pkg/completer"
15+
"github.com/carapace-sh/carapace-bridge/pkg/choices"
1416
"github.com/carapace-sh/carapace/pkg/traverse"
1517
)
1618

@@ -29,103 +31,117 @@ func ReadCompleters(dir, goos string) (completer.CompleterMap, error) {
2931
"force_all": {"common", "unix", "linux", "bsd", "darwin", "android", "windows", "freebsd", "netbsd", "openbsd"},
3032
}
3133

32-
completers := make(completer.CompleterMap)
34+
completers, err := readCompleters(dir)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
filtered := make(completer.CompleterMap)
3340
for _, group := range groups[goos] {
34-
groupCompleters, err := readCompleters(dir, group)
35-
if err != nil {
36-
return nil, err
37-
}
38-
completers.Merge(groupCompleters)
41+
filtered.Merge(completers.Filter(choices.Choice{Group: group}))
3942
}
40-
return completers, nil
43+
return filtered, nil
4144
}
4245

43-
func readCompleters(dir, group string) (completer.CompleterMap, error) {
44-
prefix, err := packagePrefix(dir)
46+
func readCompleters(dir string) (completer.CompleterMap, error) {
47+
abs, err := filepath.Abs(dir)
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
prefix, err := packagePrefix(abs)
4553
if err != nil {
4654
return nil, err
4755
}
4856

4957
completers := make(completer.CompleterMap)
50-
if files, err := os.ReadDir(filepath.Join(dir, group)); err == nil {
51-
for _, file := range files {
52-
if file.IsDir() && strings.HasSuffix(file.Name(), "_completer") {
53-
name := strings.TrimSuffix(file.Name(), "_completer")
54-
if _, err := os.Stat(filepath.Join(dir, group, file.Name(), "cmd/root.go")); err == nil {
55-
description, err := readDescription(dir, group, file.Name())
56-
if err != nil {
57-
return nil, err
58-
}
59-
url, err := readUrl(dir, group, file.Name())
60-
if err != nil {
61-
return nil, err
62-
}
63-
completers[name] = append(completers[name], completer.Completer{
64-
Name: name,
65-
Description: description,
66-
Group: group,
67-
Package: filepath.Join(prefix, group, file.Name(), "cmd"),
68-
Url: url,
69-
})
58+
59+
err = filepath.WalkDir(abs, func(path string, d fs.DirEntry, err error) error {
60+
if name, ok := strings.CutSuffix(d.Name(), "_completer"); ok {
61+
group := filepath.Base(filepath.Dir(path))
62+
63+
rootPath := filepath.Join(path, "cmd", "root.go")
64+
if _, err := os.Stat(rootPath); err == nil {
65+
description, url, err := readRoot(rootPath)
66+
if err != nil {
67+
return err
7068
}
69+
completers[name] = append(completers[name], completer.Completer{
70+
Name: name,
71+
Description: description,
72+
Group: group,
73+
Package: filepath.Join(prefix, group, d.Name(), "cmd"),
74+
Url: url,
75+
})
76+
77+
return filepath.SkipDir
78+
}
7179

72-
if variants, err := os.ReadDir(filepath.Join(dir, group, file.Name())); err == nil {
73-
for _, variant := range variants {
74-
if _, err := os.Stat(filepath.Join(dir, group, file.Name(), variant.Name(), "cmd/root.go")); err == nil {
75-
// TODO path for variants not correct (description/url)
76-
description, err := readDescription(dir, group, file.Name()+"/"+variant.Name()) // TODO just pass the root.go
77-
if err != nil {
78-
return nil, err
79-
}
80-
url, err := readUrl(dir, group, file.Name()+"/"+variant.Name())
81-
if err != nil {
82-
return nil, err
83-
}
84-
completers[name] = append(completers[name], completer.Completer{
85-
Name: name,
86-
Description: description,
87-
Group: group,
88-
Package: filepath.Join(prefix, group, file.Name(), variant.Name(), "cmd"),
89-
Url: url,
90-
Variant: variant.Name(),
91-
})
80+
if variants, err := os.ReadDir(path); err == nil {
81+
for _, variant := range variants {
82+
if !variant.IsDir() {
83+
continue
84+
}
85+
86+
rootPath = filepath.Join(path, variant.Name(), "cmd", "root.go")
87+
if _, err := os.Stat(path); err == nil {
88+
// TODO path for variants not correct (description/url)
89+
description, url, err := readRoot(rootPath)
90+
if err != nil {
91+
return err
9292
}
93+
completers[name] = append(completers[name], completer.Completer{
94+
Name: name,
95+
Description: description,
96+
Group: group,
97+
Package: filepath.Join(prefix, group, d.Name(), variant.Name(), "cmd"),
98+
Url: url,
99+
Variant: variant.Name(),
100+
})
93101
}
94102
}
95103
}
104+
105+
return filepath.SkipDir
96106
}
107+
return nil
108+
})
109+
if err != nil {
110+
return nil, err
97111
}
98112
return completers, nil
99113
}
100114

101-
func readDescription(root, goos, completer string) (string, error) {
102-
content, err := os.ReadFile(fmt.Sprintf("%v/%v/%v/cmd/root.go", root, goos, completer))
115+
func readRoot(path string) (string, string, error) {
116+
f, err := os.Open(path)
103117
if err != nil {
104-
return "", err
105-
}
106-
107-
re := regexp.MustCompile("^\tShort: +\"(?P<description>.*)\",$")
108-
for _, line := range strings.Split(string(content), "\n") {
109-
if re.MatchString(line) {
110-
return re.FindStringSubmatch(line)[1], nil
111-
}
118+
return "", "", err
112119
}
113-
return "", nil // TODO error?
114-
}
120+
defer f.Close()
115121

116-
func readUrl(root, goos, completer string) (string, error) {
117-
content, err := os.ReadFile(fmt.Sprintf("%v/%v/%v/cmd/root.go", root, goos, completer))
118-
if err != nil {
119-
return "", err
120-
}
122+
rDescription := regexp.MustCompile("^\tShort: +\"(?P<description>.*)\",$")
123+
rUrl := regexp.MustCompile("^\tLong: +\"(?P<url>.*)\",$")
121124

122-
re := regexp.MustCompile("^\tLong: +\"(?P<url>.*)\",$")
123-
for _, line := range strings.Split(string(content), "\n") {
124-
if re.MatchString(line) {
125-
return re.FindStringSubmatch(line)[1], nil
125+
var description, url string
126+
scanner := bufio.NewScanner(f)
127+
for scanner.Scan() {
128+
if description != "" && url != "" {
129+
break
130+
}
131+
if description == "" {
132+
if matches := rDescription.FindStringSubmatch(scanner.Text()); matches != nil {
133+
description = matches[1]
134+
continue
135+
}
136+
}
137+
if url == "" {
138+
if matches := rUrl.FindStringSubmatch(scanner.Text()); matches != nil {
139+
url = matches[1]
140+
continue
141+
}
126142
}
127143
}
128-
return "", nil // TODO error?
144+
return description, url, nil
129145
}
130146

131147
func packagePrefix(dir string) (string, error) {

0 commit comments

Comments
 (0)