From ee6ff7a3137e1094143240cf16b97df66b344434 Mon Sep 17 00:00:00 2001 From: Cyril David Date: Fri, 2 Apr 2021 11:59:20 -0700 Subject: [PATCH] Solidify editor mechanics I've looked into what github does, so they use a mix of this approach, and the survey approach. To keep it simple, we're only gonna go with the manual approach they do until we feel the full blown prompt approach is necessary. Closes #189 --- internal/cli/rules.go | 24 ++++++++++------ internal/prompt/editor.go | 58 +++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 33 deletions(-) diff --git a/internal/cli/rules.go b/internal/cli/rules.go index 6f03c0ddd..fac10b1fd 100644 --- a/internal/cli/rules.go +++ b/internal/cli/rules.go @@ -67,10 +67,10 @@ func rulesCmd(cli *cli) *cobra.Command { func listRulesCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ - Use: "list", + Use: "list", Aliases: []string{"ls"}, - Short: "List your rules", - Long: `List the rules in your current tenant.`, + Short: "List your rules", + Long: `List the rules in your current tenant.`, Example: `auth0 rules list auth0 rules ls`, RunE: func(cmd *cobra.Command, args []string) error { @@ -106,9 +106,9 @@ func createRuleCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "create", Short: "Create a new rule", - Long: `Create a new rule:`, - Example: `auth0 rules create -auth0 rules create --name "My Rule" + Long: `Create a new rule:`, + Example: `auth0 rules create +auth0 rules create --name "My Rule" auth0 rules create -n "My Rule" --template "Empty rule" auth0 rules create -n "My Rule" -t "Empty rule" --enabled=false`, PreRun: func(cmd *cobra.Command, args []string) { @@ -129,6 +129,9 @@ auth0 rules create -n "My Rule" -t "Empty rule" --enabled=false`, script, err := prompt.CaptureInputViaEditor( ruleTemplateOptions.getValue(inputs.Template), inputs.Name+".*.js", + func() { + cli.renderer.Infof("%s once you close the editor, the rule will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:")) + }, ) if err != nil { return fmt.Errorf("Failed to capture input from the editor: %w", err) @@ -169,7 +172,7 @@ func showRuleCmd(cli *cli) *cobra.Command { Use: "show", Args: cobra.MaximumNArgs(1), Short: "Show a rule", - Long: `Show a rule:`, + Long: `Show a rule:`, Example: `auth0 rules show auth0 rules show `, PreRun: func(cmd *cobra.Command, args []string) { @@ -213,7 +216,7 @@ func deleteRuleCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "delete", Short: "Delete a rule", - Long: `Delete a rule`, + Long: `Delete a rule`, Example: `auth0 rules delete auth0 rules delete `, PreRun: func(cmd *cobra.Command, args []string) { @@ -254,7 +257,7 @@ func updateRuleCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "update", Short: "Update a rule", - Long: `Update a rule`, + Long: `Update a rule`, Example: `auth0 rules update auth0 rules update --name "My Updated Rule" auth0 rules update -n "My Updated Rule" --enabled=false`, @@ -291,6 +294,9 @@ auth0 rules update -n "My Updated Rule" --enabled=false`, script, err := prompt.CaptureInputViaEditor( rule.GetScript(), rule.GetName()+".*.js", + func() { + cli.renderer.Infof("%s once you close the editor, the rule will be saved. To cancel, CTRL+C.", ansi.Faint("Hint:")) + }, ) if err != nil { return fmt.Errorf("Failed to capture input from the editor: %w", err) diff --git a/internal/prompt/editor.go b/internal/prompt/editor.go index 0ce6c1f3f..65cd9d381 100644 --- a/internal/prompt/editor.go +++ b/internal/prompt/editor.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "os/exec" + "runtime" "github.com/kballard/go-shellquote" ) @@ -14,48 +15,58 @@ const ( ) var ( - bom = []byte{0xef, 0xbb, 0xbf} + bom = []byte{0xef, 0xbb, 0xbf} + cliEditors = []string{"vi", "vim", "nano"} ) -var defaultEditorPrompt = &editorPrompt{defaultEditor: defaultEditor} +var defaultEditorPrompt = &editorPrompt{editorCmd: getDefaultEditor()} // CaptureInputViaEditor is the high level function to use in this package in // order to capture input from an editor. // // The arguments have been tailored for our use of strings mostly in the rest // of the CLI even though internally we're using []byte. -func CaptureInputViaEditor(contents, pattern string) (result string, err error) { - v, err := defaultEditorPrompt.captureInput([]byte(contents), pattern) +func CaptureInputViaEditor(contents, pattern string, infoFn func()) (result string, err error) { + v, err := defaultEditorPrompt.captureInput([]byte(contents), pattern, infoFn) return string(v), err } type editorPrompt struct { - defaultEditor string + editorCmd string } -// GetPreferredEditorFromEnvironment returns the user's editor as defined by the -// `$EDITOR` environment variable, or the `defaultEditor` if it is not set. -func (p *editorPrompt) getPreferredEditor() string { - editor := os.Getenv("EDITOR") - - if editor == "" { - return p.defaultEditor +// getDefaultEditor is taken from https://github.com/cli/cli/blob/trunk/pkg/surveyext/editor_manual.go +// and tries to infer the editor from different heuristics. +func getDefaultEditor() string { + if runtime.GOOS == "windows" { + return "notepad" + } else if g := os.Getenv("GIT_EDITOR"); g != "" { + return g + } else if v := os.Getenv("VISUAL"); v != "" { + return v + } else if e := os.Getenv("EDITOR"); e != "" { + return e } - return editor + return defaultEditor } // openFile opens filename in the preferred text editor, resolving the // arguments with editor specific logic. -func (p *editorPrompt) openFile(filename string) error { - editorCommand := p.getPreferredEditor() - - args, err := shellquote.Split(editorCommand) +func (p *editorPrompt) openFile(filename string, infoFn func()) error { + args, err := shellquote.Split(p.editorCmd) if err != nil { return err } args = append(args, filename) + isCLIEditor := false + for _, e := range cliEditors { + if e == args[0] { + isCLIEditor = true + } + } + editorExe, err := exec.LookPath(args[0]) if err != nil { return err @@ -66,6 +77,10 @@ func (p *editorPrompt) openFile(filename string) error { cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr + if !isCLIEditor && infoFn != nil { + infoFn() + } + return cmd.Run() } @@ -75,7 +90,7 @@ func (p *editorPrompt) openFile(filename string) error { // // If given default contents, it will write that to the file before popping // open the editor. -func (p *editorPrompt) captureInput(contents []byte, pattern string) ([]byte, error) { +func (p *editorPrompt) captureInput(contents []byte, pattern string, infoFn func()) ([]byte, error) { file, err := os.CreateTemp(os.TempDir(), pattern) if err != nil { return []byte{}, err @@ -102,16 +117,11 @@ func (p *editorPrompt) captureInput(contents []byte, pattern string) ([]byte, er } } - // close the fd to allow the editor to save file - if err := file.Close(); err != nil { - return nil, err - } - if err = file.Close(); err != nil { return nil, err } - if err = p.openFile(filename); err != nil { + if err = p.openFile(filename, infoFn); err != nil { return nil, err }