github.com/cli/cli@v1.14.1-0.20210902173923-1af6a669e342/pkg/cmd/pr/shared/commentable.go (about) 1 package shared 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 10 "github.com/AlecAivazis/survey/v2" 11 "github.com/cli/cli/api" 12 "github.com/cli/cli/internal/config" 13 "github.com/cli/cli/internal/ghrepo" 14 "github.com/cli/cli/pkg/cmdutil" 15 "github.com/cli/cli/pkg/iostreams" 16 "github.com/cli/cli/pkg/surveyext" 17 "github.com/cli/cli/utils" 18 "github.com/spf13/cobra" 19 ) 20 21 type InputType int 22 23 const ( 24 InputTypeEditor InputType = iota 25 InputTypeInline 26 InputTypeWeb 27 ) 28 29 type Commentable interface { 30 Link() string 31 Identifier() string 32 } 33 34 type CommentableOptions struct { 35 IO *iostreams.IOStreams 36 HttpClient func() (*http.Client, error) 37 RetrieveCommentable func() (Commentable, ghrepo.Interface, error) 38 EditSurvey func() (string, error) 39 InteractiveEditSurvey func() (string, error) 40 ConfirmSubmitSurvey func() (bool, error) 41 OpenInBrowser func(string) error 42 Interactive bool 43 InputType InputType 44 Body string 45 } 46 47 func CommentablePreRun(cmd *cobra.Command, opts *CommentableOptions) error { 48 inputFlags := 0 49 if cmd.Flags().Changed("body") { 50 opts.InputType = InputTypeInline 51 inputFlags++ 52 } 53 if cmd.Flags().Changed("body-file") { 54 opts.InputType = InputTypeInline 55 inputFlags++ 56 } 57 if web, _ := cmd.Flags().GetBool("web"); web { 58 opts.InputType = InputTypeWeb 59 inputFlags++ 60 } 61 if editor, _ := cmd.Flags().GetBool("editor"); editor { 62 opts.InputType = InputTypeEditor 63 inputFlags++ 64 } 65 66 if inputFlags == 0 { 67 if !opts.IO.CanPrompt() { 68 return &cmdutil.FlagError{Err: errors.New("`--body`, `--body-file` or `--web` required when not running interactively")} 69 } 70 opts.Interactive = true 71 } else if inputFlags == 1 { 72 if !opts.IO.CanPrompt() && opts.InputType == InputTypeEditor { 73 return &cmdutil.FlagError{Err: errors.New("`--body`, `--body-file` or `--web` required when not running interactively")} 74 } 75 } else if inputFlags > 1 { 76 return &cmdutil.FlagError{Err: fmt.Errorf("specify only one of `--body`, `--body-file`, `--editor`, or `--web`")} 77 } 78 79 return nil 80 } 81 82 func CommentableRun(opts *CommentableOptions) error { 83 commentable, repo, err := opts.RetrieveCommentable() 84 if err != nil { 85 return err 86 } 87 88 switch opts.InputType { 89 case InputTypeWeb: 90 openURL := commentable.Link() + "#issuecomment-new" 91 if opts.IO.IsStdoutTTY() { 92 fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(openURL)) 93 } 94 return opts.OpenInBrowser(openURL) 95 case InputTypeEditor: 96 var body string 97 if opts.Interactive { 98 body, err = opts.InteractiveEditSurvey() 99 } else { 100 body, err = opts.EditSurvey() 101 } 102 if err != nil { 103 return err 104 } 105 opts.Body = body 106 } 107 108 if opts.Interactive { 109 cont, err := opts.ConfirmSubmitSurvey() 110 if err != nil { 111 return err 112 } 113 if !cont { 114 return errors.New("Discarding...") 115 } 116 } 117 118 httpClient, err := opts.HttpClient() 119 if err != nil { 120 return err 121 } 122 apiClient := api.NewClientFromHTTP(httpClient) 123 params := api.CommentCreateInput{Body: opts.Body, SubjectId: commentable.Identifier()} 124 url, err := api.CommentCreate(apiClient, repo.RepoHost(), params) 125 if err != nil { 126 return err 127 } 128 fmt.Fprintln(opts.IO.Out, url) 129 return nil 130 } 131 132 func CommentableConfirmSubmitSurvey() (bool, error) { 133 var confirm bool 134 submit := &survey.Confirm{ 135 Message: "Submit?", 136 Default: true, 137 } 138 err := survey.AskOne(submit, &confirm) 139 return confirm, err 140 } 141 142 func CommentableInteractiveEditSurvey(cf func() (config.Config, error), io *iostreams.IOStreams) func() (string, error) { 143 return func() (string, error) { 144 editorCommand, err := cmdutil.DetermineEditor(cf) 145 if err != nil { 146 return "", err 147 } 148 if editorCommand == "" { 149 editorCommand = surveyext.DefaultEditorName() 150 } 151 cs := io.ColorScheme() 152 fmt.Fprintf(io.Out, "- %s to draft your comment in %s... ", cs.Bold("Press Enter"), cs.Bold(editorCommand)) 153 _ = waitForEnter(io.In) 154 return surveyext.Edit(editorCommand, "*.md", "", io.In, io.Out, io.ErrOut, nil) 155 } 156 } 157 158 func CommentableEditSurvey(cf func() (config.Config, error), io *iostreams.IOStreams) func() (string, error) { 159 return func() (string, error) { 160 editorCommand, err := cmdutil.DetermineEditor(cf) 161 if err != nil { 162 return "", err 163 } 164 return surveyext.Edit(editorCommand, "*.md", "", io.In, io.Out, io.ErrOut, nil) 165 } 166 } 167 168 func waitForEnter(r io.Reader) error { 169 scanner := bufio.NewScanner(r) 170 scanner.Scan() 171 return scanner.Err() 172 }