Merge pull request #97 from github/issue-create-editor

Use survey when creating issues
This commit is contained in:
Nate Smith 2019-11-25 11:34:11 -06:00 committed by GitHub
commit 89700216c6
4 changed files with 150 additions and 111 deletions

View file

@ -2,15 +2,14 @@ package command
import ( import (
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strconv" "strconv"
"strings" "strings"
"github.com/github/gh-cli/api" "github.com/github/gh-cli/api"
"github.com/github/gh-cli/utils" "github.com/github/gh-cli/utils"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
) )
func init() { func init() {
@ -29,7 +28,10 @@ func init() {
}, },
) )
issueCmd.AddCommand(issueCreateCmd) 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") issueCreateCmd.Flags().BoolP("web", "w", false, "open the web browser to create an issue")
issueListCmd := &cobra.Command{ issueListCmd := &cobra.Command{
@ -205,44 +207,39 @@ func issueCreate(cmd *cobra.Command, args []string) error {
return utils.OpenInBrowser(openURL) 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) apiClient, err := apiClientForContext(ctx)
if err != nil { if err != nil {
return err return err
} }
if len(message) > 0 { title, err := cmd.Flags().GetString("title")
title = message[0] if err != nil {
body = strings.Join(message[1:], "\n\n") return errors.Wrap(err, "could not parse title")
} else { }
// TODO: open the text editor for issue title & body body, err := cmd.Flags().GetString("body")
input := os.Stdin if err != nil {
if terminal.IsTerminal(int(input.Fd())) { return errors.Wrap(err, "could not parse body")
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]
}
} }
if title == "" { interactive := title == "" || body == ""
return fmt.Errorf("aborting due to empty title")
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{}{ params := map[string]interface{}{
"title": title, "title": title,

View file

@ -144,7 +144,7 @@ func TestIssueCreate(t *testing.T) {
out := bytes.Buffer{} out := bytes.Buffer{}
issueCreateCmd.SetOut(&out) 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() _, err := RootCmd.ExecuteC()
if err != nil { if err != nil {
t.Errorf("error running command `issue create`: %v", err) 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.RepositoryID, "REPOID")
eq(t, reqBody.Variables.Input.Title, "hello") 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") eq(t, out.String(), "https://github.com/OWNER/REPO/issues/12\n")
} }

View file

@ -5,7 +5,6 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/AlecAivazis/survey/v2"
"github.com/github/gh-cli/api" "github.com/github/gh-cli/api"
"github.com/github/gh-cli/context" "github.com/github/gh-cli/context"
"github.com/github/gh-cli/git" "github.com/github/gh-cli/git"
@ -55,86 +54,25 @@ func prCreate(cmd *cobra.Command, _ []string) error {
interactive := title == "" || body == "" interactive := title == "" || body == ""
inProgress := struct {
Body string
Title string
}{}
if interactive { if interactive {
confirmed := false tb, err := titleBodySurvey(cmd, title, body)
editor := determineEditor() if err != nil {
return errors.Wrap(err, "could not collect title and/or body")
}
for !confirmed { if tb == nil {
titleQuestion := &survey.Question{ // editing was canceled, we can just leave
Name: "title", return nil
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,
},
}
qs := []*survey.Question{} if title == "" {
if title == "" { title = tb.Title
qs = append(qs, titleQuestion) }
} if body == "" {
if body == "" { body = tb.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 = inProgress.Title
}
if body == "" {
body = inProgress.Body
}
base, err := cmd.Flags().GetString("base") base, err := cmd.Flags().GetString("base")
if err != nil { if err != nil {
return errors.Wrap(err, "could not parse base") return errors.Wrap(err, "could not parse base")

View file

@ -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
}