From 4830d37e1b655b1eeadc0b6793d94f97cfae09a7 Mon Sep 17 00:00:00 2001 From: rsteube Date: Mon, 11 Sep 2023 11:37:11 +0200 Subject: [PATCH] expose custom macros --- action.go | 45 +++++++++++++++++-- cmd/carapace-spec/cmd/root.go | 4 ++ go.mod | 2 +- go.sum | 4 +- macro.go | 2 +- register.go | 82 +++++++++++++++++++++++++++++++++++ 6 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 register.go diff --git a/action.go b/action.go index 28c5207..0a4725e 100644 --- a/action.go +++ b/action.go @@ -3,6 +3,7 @@ package spec import ( "fmt" "os" + "path/filepath" "regexp" "sort" "strings" @@ -47,14 +48,50 @@ func (value) JSONSchema() *jsonschema.Schema { } } +func executable() string { + s, err := os.Executable() + if err != nil { + panic(err.Error()) // TODO handle error, eval symlink, how to handle "go test" + } + + return filepath.Base(s) +} + // ActionMacro completes given macro func ActionMacro(s string) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { - m, err := macros.Lookup(s) - if err != nil { - return carapace.ActionMessage(err.Error()) + r := regexp.MustCompile(`^\$(?P[^(]*)(\((?P.*)\))?$`) + matches := r.FindStringSubmatch(s) + if matches == nil { + return carapace.ActionMessage("malformed macro: %#v", s) + } + if strings.HasPrefix(matches[1], "_") && !strings.HasPrefix(matches[1], "_.") { + return carapace.ActionMessage(`"$_" deprecated: replace %#v with %#v`, "$"+matches[1], "$carapace."+strings.TrimPrefix(matches[1], "_")) + } + prefix := fmt.Sprintf("$%v.", executable()) + + switch { + case !strings.HasPrefix(matches[1], "_.") && strings.Contains(matches[1], ".") && !strings.HasPrefix(s, prefix): + splitted := strings.SplitN(strings.TrimPrefix(s, "$"), ".", 2) + args := []string{"_carapace", "macro"} + args = append(args, splitted[1]) + args = append(args, c.Args...) + args = append(args, c.Value) + carapace.LOG.Printf("%#v", args) + return carapace.ActionExecCommand(splitted[0], args...)(func(output []byte) carapace.Action { + return carapace.ActionImport(output) + }) + + default: + if strings.HasPrefix(s, prefix) { + s = "$_." + strings.TrimPrefix(s, prefix) + } + m, err := macros.Lookup(s) + if err != nil { + return carapace.ActionMessage(err.Error()) + } + return m.Parse(s) } - return m.Parse(s) }) } diff --git a/cmd/carapace-spec/cmd/root.go b/cmd/carapace-spec/cmd/root.go index 850a635..2717c1a 100644 --- a/cmd/carapace-spec/cmd/root.go +++ b/cmd/carapace-spec/cmd/root.go @@ -136,7 +136,11 @@ func init() { default: cmd.Flags().Parse(args[:1]) // TODO unnecessary } + }) + + spec.AddMacro("Spec", spec.MacroI(spec.ActionSpec)) + spec.Register(rootCmd) } func bridgeCompletion(cmd *cobra.Command, spec string, args ...string) { diff --git a/go.mod b/go.mod index 841cd03..21f1724 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.18 require ( github.com/invopop/jsonschema v0.12.0 - github.com/rsteube/carapace v0.47.0 + github.com/rsteube/carapace v0.47.3 github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index a005395..a79347e 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/rsteube/carapace v0.47.0 h1:2mt1sGsNcqvh+XzG070FGSG8EXMIRGdgYt0SOo/DaWQ= -github.com/rsteube/carapace v0.47.0/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o= +github.com/rsteube/carapace v0.47.3 h1:g+R4mKPzuV6aPcztQR+kpZlLAqRFvUSG2aM/9JaOIUw= +github.com/rsteube/carapace v0.47.3/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o= github.com/rsteube/carapace-shlex v0.1.1 h1:fRQEBBKyYKm4TXUabm4tzH904iFWSmXJl3UZhMfQNYU= github.com/rsteube/carapace-shlex v0.1.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/macro.go b/macro.go index b0c904b..3074eb3 100644 --- a/macro.go +++ b/macro.go @@ -32,7 +32,7 @@ func addCoreMacro(s string, m Macro) { // AddMacro adds a custom macro func AddMacro(s string, m Macro) { - macros["_"+s] = m + macros["_."+s] = m } func MacroN(f func() carapace.Action) Macro { diff --git a/register.go b/register.go new file mode 100644 index 0000000..1db49d1 --- /dev/null +++ b/register.go @@ -0,0 +1,82 @@ +package spec + +import ( + "fmt" + "sort" + "strings" + + "github.com/rsteube/carapace" + "github.com/spf13/cobra" +) + +func Register(cmd *cobra.Command) { + carapace.Gen(cmd) + + carapaceCmd, _, err := cmd.Find([]string{"_carapace"}) // TODO provide access to it using `carapace.Gen` + if err != nil { + carapace.LOG.Println(err.Error()) + return // should never happen + } + + macroCmd := &cobra.Command{ + Use: "macro", + RunE: func(cmd *cobra.Command, args []string) error { + switch len(args) { + case 0: + keys := make([]string, 0, len(macros)) + for k := range macros { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, key := range keys { + if strings.HasPrefix(key, "_") { + fmt.Fprintln(cmd.OutOrStdout(), strings.TrimPrefix(key, "_")) + } + } + case 1: + m, ok := macros["_."+args[0]] + if !ok { + return fmt.Errorf("unknown macro: %v", args[0]) + } + fmt.Fprintln(cmd.OutOrStdout(), m.Signature()) + default: + mCmd := &cobra.Command{ + DisableFlagParsing: true, + } + carapace.Gen(mCmd).Standalone() + carapace.Gen(mCmd).PositionalAnyCompletion( + ActionMacro("$_." + args[0]), + ) + carapace.LOG.Printf("%#v", args) + mCmd.SetArgs(append([]string{"_carapace", "export", ""}, args[1:]...)) + mCmd.SetOut(cmd.OutOrStdout()) + mCmd.SetErr(cmd.ErrOrStderr()) + return mCmd.Execute() + } + return nil + }, + } + + macroCmd.Flags().SetInterspersed(false) + + carapaceCmd.AddCommand(macroCmd) + + carapace.Gen(macroCmd).PositionalCompletion( + carapace.ActionCallback(func(c carapace.Context) carapace.Action { + vals := make([]string, 0, len(macros)) + for key := range macros { + if strings.HasPrefix(key, "_.") { + vals = append(vals, strings.TrimPrefix(key, "_.")) + } + } + return carapace.ActionValues(vals...).MultiParts(".") + }), + ) + + carapace.Gen(macroCmd).PositionalAnyCompletion( + carapace.ActionCallback(func(c carapace.Context) carapace.Action { + return ActionMacro("$_." + c.Args[0]).Shift(1) + }), + ) +}