Skip to content

Commit

Permalink
Merge pull request #903 from rsteube/action-invoke-completer
Browse files Browse the repository at this point in the history
added ActionInvoke
  • Loading branch information
rsteube authored Feb 9, 2022
2 parents 98a6892 + bd450f3 commit 1e047d5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 16 deletions.
19 changes: 3 additions & 16 deletions completers/docker_completer/cmd/compose.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cmd

import (
"os"

"github.com/rsteube/carapace"
compose "github.com/rsteube/carapace-bin/completers/docker-compose_completer/cmd"
"github.com/rsteube/carapace-bin/pkg/actions/invoke"
"github.com/spf13/cobra"
)

Expand All @@ -20,19 +20,6 @@ func init() {
rootCmd.AddCommand(composeCmd)

carapace.Gen(composeCmd).PositionalAnyCompletion(
carapace.ActionCallback(func(c carapace.Context) carapace.Action {
// TODO experimental - this prevents building the completer separately
// maybe replace with invocation of cmd.Execute() and catch stdout like in the root command of carapace-bin
executable, err := os.Executable()
if err != nil {
return carapace.ActionMessage(err.Error())
}
arg := []string{"docker-compose", "export", "_", "docker-compose"}
arg = append(arg, c.Args...)
arg = append(arg, c.CallbackValue)
return carapace.ActionExecCommand(executable, arg...)(func(output []byte) carapace.Action {
return carapace.ActionImport(output)
})
}),
invoke.ActionInvoke(compose.Execute),
)
}
67 changes: 67 additions & 0 deletions pkg/actions/invoke/invoke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// package invoke contains carapace-bin internal actions
package invoke

import (
"bytes"
"io"
"os"

"github.com/rsteube/carapace"
)

// ActionInvoke invokes an internal carapace-bin completer
// var composeCmd = &cobra.Command{
// Use: "compose",
// Short: "Define and run multi-container applications with Docker",
// Run: func(cmd *cobra.Command, args []string) {},
// DisableFlagParsing: true,
// }
//
// func init() {
// carapace.Gen(composeCmd).Standalone()
//
// rootCmd.AddCommand(composeCmd)
//
// carapace.Gen(composeCmd).PositionalAnyCompletion(
// invoke.ActionInvokeCompleter(compose.Execute),
// )
// }
func ActionInvoke(f func() error) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
// TODO experimental
// TODO beware of carapace.Batch goroutines - is this safe? might need locking
old := os.Args
args := []string{"", "_carapace", "export", "_", ""}
args = append(args, c.Args...)
args = append(args, c.CallbackValue)
os.Args = args
output, err := captureStdout(f)
os.Args = old
if err != nil {
return carapace.ActionMessage(err.Error())
}
return carapace.ActionImport([]byte(output))
})
}

func captureStdout(f func() error) (string, error) {
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

outC := make(chan string)
// copy the output in a separate goroutine so printing can't block indefinitely
go func() {
var buf bytes.Buffer
io.Copy(&buf, r)
outC <- buf.String()
}()

err := f()

w.Close()
out := <-outC
os.Stdout = old

return out, err
}

0 comments on commit 1e047d5

Please sign in to comment.