diff --git a/completers/dagger_completer/cmd/call.go b/completers/dagger_completer/cmd/call.go index 21100e4843..c47b4f83a0 100644 --- a/completers/dagger_completer/cmd/call.go +++ b/completers/dagger_completer/cmd/call.go @@ -15,6 +15,7 @@ var callCmd = &cobra.Command{ func init() { carapace.Gen(callCmd).Standalone() + callCmd.Flags().SetInterspersed(false) callCmd.PersistentFlags().Bool("focus", false, "Only show output for focused commands") callCmd.PersistentFlags().Bool("json", false, "Present result as JSON") diff --git a/completers/dagger_completer/cmd/functions.go b/completers/dagger_completer/cmd/functions.go index d4f114d085..09a5d58759 100644 --- a/completers/dagger_completer/cmd/functions.go +++ b/completers/dagger_completer/cmd/functions.go @@ -17,6 +17,7 @@ func init() { carapace.Gen(functionsCmd).Standalone() functionsCmd.PersistentFlags().Bool("focus", false, "Only show output for focused commands") + functionsCmd.PersistentFlags().Bool("json", false, "Output as json") functionsCmd.PersistentFlags().StringP("mod", "m", "", "Path to dagger.json config file for the module or a directory containing that file. Either local path (e.g. \"/path/to/some/dir\") or a github repo (e.g. \"github.com/dagger/dagger/path/to/some/subdir\")") rootCmd.AddCommand(functionsCmd) diff --git a/completers/dagger_completer/cmd/run.go b/completers/dagger_completer/cmd/run.go index 54ee2e7935..1d21721f52 100644 --- a/completers/dagger_completer/cmd/run.go +++ b/completers/dagger_completer/cmd/run.go @@ -2,6 +2,7 @@ package cmd import ( "github.com/rsteube/carapace" + "github.com/rsteube/carapace-bridge/pkg/actions/bridge" "github.com/spf13/cobra" ) @@ -15,8 +16,13 @@ var runCmd = &cobra.Command{ func init() { carapace.Gen(runCmd).Standalone() + runCmd.Flags().SetInterspersed(false) runCmd.Flags().String("cleanup-timeout", "", "max duration to wait between SIGTERM and SIGKILL on interrupt") runCmd.Flags().Bool("focus", false, "Only show output for focused commands.") rootCmd.AddCommand(runCmd) + + carapace.Gen(runCmd).PositionalAnyCompletion( + bridge.ActionCarapaceBin(), + ) } diff --git a/pkg/actions/tools/dagger/function.go b/pkg/actions/tools/dagger/function.go index 6fc38d322a..b4c2354714 100644 --- a/pkg/actions/tools/dagger/function.go +++ b/pkg/actions/tools/dagger/function.go @@ -1,27 +1,132 @@ package dagger import ( - "regexp" - "strings" + "encoding/json" + "os" + "time" + "unicode" "github.com/rsteube/carapace" + "github.com/rsteube/carapace/pkg/cache" + "github.com/rsteube/carapace/pkg/cache/key" + "github.com/spf13/cobra" ) -// ActionFunctions completes functions +type function struct { + Name string `json:"Name"` + Description string `json:"Description"` + Args []struct { + Name string `json:"Name"` + Description string `json:"Description"` + TypeDef struct { + Kind string `json:"Kind"` + Optional bool `json:"Optional"` + AsObject *struct { + Name string `json:"Name"` + Functions any `json:"Functions"` + Fields any `json:"Fields"` + Constructor any `json:"Constructor"` + SourceModuleName string `json:"SourceModuleName"` + } `json:"AsObject"` + AsInterface any `json:"AsInterface"` + AsInput any `json:"AsInput"` + AsList any `json:"AsList"` + } `json:"TypeDef"` + DefaultValue string `json:"DefaultValue"` + } `json:"Args"` +} + +type daggerFile struct { + Name string + Sdk string + Source string +} + +// ActionFunctions completes functions and their arguments // -// container-echo (dagger call container-echo --string-arg yo stdout) // grep-dir (dagger call grep-dir --directory-arg . --pattern GrepDir) +// --directory-arg func ActionFunctions() carapace.Action { - return carapace.ActionExecCommand("dagger", "functions", "--silent")(func(output []byte) carapace.Action { - lines := strings.Split(string(output), "\n") - r := regexp.MustCompile(`^(?P[^ ]+) +example usage: "(?P.*)"$`) - - vals := make([]string, 0) - for _, line := range lines[1:] { - if matches := r.FindStringSubmatch(line); matches != nil { - vals = append(vals, matches[1], matches[2]) + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + content, err := os.ReadFile("dagger.json") + if err != nil { + return carapace.ActionMessage(err.Error()) + } + + var d daggerFile + if err := json.Unmarshal(content, &d); err != nil { + return carapace.ActionMessage(err.Error()) + } + + output, err := cache.Cache(24*time.Hour, key.FolderStats(d.Source))(func() ([]byte, error) { + return c.Command("dagger", "functions", "--json").Output() + }) + if err != nil { + return carapace.ActionMessage(err.Error()) + } + + var functions []function + if err := json.Unmarshal(output, &functions); err != nil { + return carapace.ActionMessage(err.Error()) + } + + cmd := &cobra.Command{} + carapace.Gen(cmd).Standalone() + for _, f := range functions { + f.Name = toKebab(f.Name) + subCmd := &cobra.Command{ + Use: f.Name, + Short: f.Description, + Run: func(cmd *cobra.Command, args []string) {}, } + carapace.Gen(subCmd).Standalone() + + for _, arg := range f.Args { + arg.Name = toKebab(arg.Name) + + // TODO transform camelcase to kebab + switch arg.TypeDef.Kind { // TODO more types + case "STRING_KIND", "OBJECT_KIND": + subCmd.Flags().String(arg.Name, arg.DefaultValue, arg.Description) + default: + return carapace.ActionMessage("unknown kind %s", arg.TypeDef.Kind) + } + + if arg.TypeDef.Optional { + subCmd.Flag(arg.Name).NoOptDefVal = " " + } + + localArg := arg + carapace.Gen(subCmd).FlagCompletion(carapace.ActionMap{ + arg.Name: carapace.ActionCallback(func(c carapace.Context) carapace.Action { + if localArg.TypeDef.AsObject == nil { + return carapace.ActionValues() + } + + switch localArg.TypeDef.AsObject.Name { // TODO more + case "Directory": + return carapace.ActionDirectories() + default: + return carapace.ActionValues() + } + }), + }) + } + + cmd.AddCommand(subCmd) } - return carapace.ActionValuesDescribed(vals...) + + return carapace.ActionExecute(cmd) }) } + +func toKebab(s string) string { + runes := make([]rune, 0) + for _, r := range []rune(s) { + if unicode.IsUpper(r) { + runes = append(runes, '-') + } + runes = append(runes, unicode.ToLower(r)) + } + return string(runes) +}