diff --git a/command/issue.go b/command/issue.go index 5a34afac7..0c022939f 100644 --- a/command/issue.go +++ b/command/issue.go @@ -2,15 +2,14 @@ package command import ( "fmt" - "io/ioutil" "os" "strconv" "strings" "github.com/github/gh-cli/api" "github.com/github/gh-cli/utils" + "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/crypto/ssh/terminal" ) func init() { @@ -29,7 +28,10 @@ func init() { }, ) issueCmd.AddCommand(issueCreateCmd) - issueCreateCmd.Flags().StringArrayP("message", "m", nil, "set title and body") + issueCreateCmd.Flags().StringP("title", "t", "", + "Supply a title. Will prompt for one otherwise.") + issueCreateCmd.Flags().StringP("body", "b", "", + "Supply a body. Will prompt for one otherwise.") issueCreateCmd.Flags().BoolP("web", "w", false, "open the web browser to create an issue") issueListCmd := &cobra.Command{ @@ -205,44 +207,39 @@ func issueCreate(cmd *cobra.Command, args []string) error { return utils.OpenInBrowser(openURL) } - var title string - var body string - - message, err := cmd.Flags().GetStringArray("message") - if err != nil { - return err - } - apiClient, err := apiClientForContext(ctx) if err != nil { return err } - if len(message) > 0 { - title = message[0] - body = strings.Join(message[1:], "\n\n") - } else { - // TODO: open the text editor for issue title & body - input := os.Stdin - if terminal.IsTerminal(int(input.Fd())) { - cmd.Println("Enter the issue title and body; press Enter + Ctrl-D when done:") - } - inputBytes, err := ioutil.ReadAll(input) - if err != nil { - return err - } - - parts := strings.SplitN(string(inputBytes), "\n\n", 2) - if len(parts) > 0 { - title = parts[0] - } - if len(parts) > 1 { - body = parts[1] - } + title, err := cmd.Flags().GetString("title") + if err != nil { + return errors.Wrap(err, "could not parse title") + } + body, err := cmd.Flags().GetString("body") + if err != nil { + return errors.Wrap(err, "could not parse body") } - if title == "" { - return fmt.Errorf("aborting due to empty title") + interactive := title == "" || body == "" + + if interactive { + tb, err := titleBodySurvey(cmd, title, body) + if err != nil { + return errors.Wrap(err, "could not collect title and/or body") + } + + if tb == nil { + // editing was canceled, we can just leave + return nil + } + + if title == "" { + title = tb.Title + } + if body == "" { + body = tb.Body + } } params := map[string]interface{}{ "title": title, diff --git a/command/issue_test.go b/command/issue_test.go index 7c76859f7..37db69b10 100644 --- a/command/issue_test.go +++ b/command/issue_test.go @@ -144,7 +144,7 @@ func TestIssueCreate(t *testing.T) { out := bytes.Buffer{} issueCreateCmd.SetOut(&out) - RootCmd.SetArgs([]string{"issue", "create", "-m", "hello", "-m", "ab", "-m", "cd"}) + RootCmd.SetArgs([]string{"issue", "create", "-t", "hello", "-b", "cash rules everything around me"}) _, err := RootCmd.ExecuteC() if err != nil { t.Errorf("error running command `issue create`: %v", err) @@ -164,7 +164,7 @@ func TestIssueCreate(t *testing.T) { eq(t, reqBody.Variables.Input.RepositoryID, "REPOID") eq(t, reqBody.Variables.Input.Title, "hello") - eq(t, reqBody.Variables.Input.Body, "ab\n\ncd") + eq(t, reqBody.Variables.Input.Body, "cash rules everything around me") eq(t, out.String(), "https://github.com/OWNER/REPO/issues/12\n") } diff --git a/command/pr_create.go b/command/pr_create.go index eebd7258d..8f635e306 100644 --- a/command/pr_create.go +++ b/command/pr_create.go @@ -5,7 +5,6 @@ import ( "os" "runtime" - "github.com/AlecAivazis/survey/v2" "github.com/github/gh-cli/api" "github.com/github/gh-cli/context" "github.com/github/gh-cli/git" @@ -55,86 +54,25 @@ func prCreate(cmd *cobra.Command, _ []string) error { interactive := title == "" || body == "" - inProgress := struct { - Body string - Title string - }{} - if interactive { - confirmed := false - editor := determineEditor() + tb, err := titleBodySurvey(cmd, title, body) + if err != nil { + return errors.Wrap(err, "could not collect title and/or body") + } - for !confirmed { - titleQuestion := &survey.Question{ - Name: "title", - Prompt: &survey.Input{ - Message: "Pull request title", - Default: inProgress.Title, - }, - } - bodyQuestion := &survey.Question{ - Name: "body", - Prompt: &survey.Editor{ - Message: fmt.Sprintf("Pull request body (%s)", editor), - FileName: "*.md", - Default: inProgress.Body, - AppendDefault: true, - Editor: editor, - }, - } + if tb == nil { + // editing was canceled, we can just leave + return nil + } - qs := []*survey.Question{} - if title == "" { - qs = append(qs, titleQuestion) - } - if body == "" { - qs = append(qs, bodyQuestion) - } - - err := survey.Ask(qs, &inProgress) - if err != nil { - return errors.Wrap(err, "could not prompt") - } - confirmAnswers := struct { - Confirmation string - }{} - confirmQs := []*survey.Question{ - { - Name: "confirmation", - Prompt: &survey.Select{ - Message: "Submit?", - Options: []string{ - "Yes", - "Edit", - "Cancel", - }, - }, - }, - } - - err = survey.Ask(confirmQs, &confirmAnswers) - if err != nil { - return errors.Wrap(err, "could not prompt") - } - - switch confirmAnswers.Confirmation { - case "Yes": - confirmed = true - case "Edit": - continue - case "Cancel": - cmd.Println("Discarding pull request") - return nil - } + if title == "" { + title = tb.Title + } + if body == "" { + body = tb.Body } } - if title == "" { - title = inProgress.Title - } - if body == "" { - body = inProgress.Body - } base, err := cmd.Flags().GetString("base") if err != nil { return errors.Wrap(err, "could not parse base") diff --git a/command/title_body_survey.go b/command/title_body_survey.go new file mode 100644 index 000000000..af4dc34e1 --- /dev/null +++ b/command/title_body_survey.go @@ -0,0 +1,104 @@ +package command + +import ( + "fmt" + "github.com/AlecAivazis/survey/v2" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +type titleBody struct { + Body string + Title string +} + +const ( + _confirmed = iota + _unconfirmed = iota + _cancel = iota +) + +func confirm() (int, error) { + confirmAnswers := struct { + Confirmation int + }{} + confirmQs := []*survey.Question{ + { + Name: "confirmation", + Prompt: &survey.Select{ + Message: "Submit?", + Options: []string{ + "Yes", + "Edit", + "Cancel", + }, + }, + }, + } + + err := survey.Ask(confirmQs, &confirmAnswers) + if err != nil { + return -1, errors.Wrap(err, "could not prompt") + } + + return confirmAnswers.Confirmation, nil +} + +func titleBodySurvey(cmd *cobra.Command, providedTitle string, providedBody string) (*titleBody, error) { + inProgress := titleBody{} + + confirmed := false + editor := determineEditor() + + for !confirmed { + titleQuestion := &survey.Question{ + Name: "title", + Prompt: &survey.Input{ + Message: "Title", + Default: inProgress.Title, + }, + } + bodyQuestion := &survey.Question{ + Name: "body", + Prompt: &survey.Editor{ + Message: fmt.Sprintf("Body (%s)", editor), + FileName: "*.md", + Default: inProgress.Body, + AppendDefault: true, + Editor: editor, + }, + } + + qs := []*survey.Question{} + if providedTitle == "" { + qs = append(qs, titleQuestion) + } + if providedBody == "" { + qs = append(qs, bodyQuestion) + } + + err := survey.Ask(qs, &inProgress) + if err != nil { + return nil, errors.Wrap(err, "could not prompt") + } + + confirmA, err := confirm() + if err != nil { + return nil, errors.Wrap(err, "unable to confirm") + } + switch confirmA { + case _confirmed: + confirmed = true + case _unconfirmed: + continue + case _cancel: + cmd.Println("Discarding.") + return nil, nil + default: + panic("reached unreachable case") + } + + } + + return &inProgress, nil +}