From 78a95f1ca43eb4ae60b32c60f4ad6ba9ed86a127 Mon Sep 17 00:00:00 2001 From: Norwin Date: Thu, 14 Oct 2021 22:36:08 +0800 Subject: [PATCH] Allow editing multiline prompts with external text editor (#429) - Adds a new `Preferences` struct to the config, initially only containing `Editor: bool (default false)`. This struct will be serialized to configs once there is a first tea induced change to the config (eg `tea login default ` or `tea login add`). - Use external editor for all multiline prompts if preferred. We already had a function for starting a texteditor for diff reviews; it does not really make sense to replace it with `survey.Editor`, as there is a big interface mismatch: survey expects strings as inputs, while our diff functions operate on files, fixes #424 Co-authored-by: Norwin Reviewed-on: https://gitea.com/gitea/tea/pulls/429 Reviewed-by: Andrew Thornton Reviewed-by: Lunny Xiao Co-authored-by: Norwin Co-committed-by: Norwin --- cmd/comment.go | 14 ++++++++++---- modules/config/config.go | 14 +++++++++++++- modules/interact/comments.go | 1 + modules/interact/issue_create.go | 7 ++++++- modules/interact/milestone_create.go | 6 +++++- modules/interact/prompts.go | 23 ++++++++++++++++++++--- modules/interact/pull_review.go | 8 +++++++- 7 files changed, 62 insertions(+), 11 deletions(-) diff --git a/cmd/comment.go b/cmd/comment.go index 169aaa3..67b8e08 100644 --- a/cmd/comment.go +++ b/cmd/comment.go @@ -9,13 +9,15 @@ import ( "io/ioutil" "strings" - "code.gitea.io/tea/modules/interact" - - "code.gitea.io/sdk/gitea" "code.gitea.io/tea/cmd/flags" + "code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/context" + "code.gitea.io/tea/modules/interact" "code.gitea.io/tea/modules/print" "code.gitea.io/tea/modules/utils" + + "code.gitea.io/sdk/gitea" + "github.com/AlecAivazis/survey/v2" "github.com/urfave/cli/v2" ) @@ -54,7 +56,11 @@ func runAddComment(cmd *cli.Context) error { body = strings.Join([]string{body, string(bodyStdin)}, "\n\n") } } else if len(body) == 0 { - if body, err = interact.PromptMultiline("Content"); err != nil { + if err = survey.AskOne(interact.NewMultiline(interact.Multiline{ + Message: "Comment:", + Syntax: "md", + UseEditor: config.GetPreferences().Editor, + }), &body); err != nil { return err } } diff --git a/modules/config/config.go b/modules/config/config.go index 74c7b5a..de6f25a 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -17,9 +17,16 @@ import ( "gopkg.in/yaml.v2" ) +// Preferences that are stored in and read from the config file +type Preferences struct { + // Prefer using an external text editor over inline multiline prompts + Editor bool `yaml:"editor"` +} + // LocalConfig represents local configurations type LocalConfig struct { - Logins []Login `yaml:"logins"` + Logins []Login `yaml:"logins"` + Prefs Preferences `yaml:"preferences"` } var ( @@ -55,6 +62,11 @@ func GetConfigPath() string { return configFilePath } +// GetPreferences returns preferences based on the config file +func GetPreferences() Preferences { + return config.Prefs +} + // loadConfig load config from file func loadConfig() (err error) { loadConfigOnce.Do(func() { diff --git a/modules/interact/comments.go b/modules/interact/comments.go index aebe4bd..a1adcef 100644 --- a/modules/interact/comments.go +++ b/modules/interact/comments.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/sdk/gitea" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/print" + "github.com/AlecAivazis/survey/v2" "golang.org/x/crypto/ssh/terminal" ) diff --git a/modules/interact/issue_create.go b/modules/interact/issue_create.go index e9b79a8..4cdab29 100644 --- a/modules/interact/issue_create.go +++ b/modules/interact/issue_create.go @@ -43,7 +43,12 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre } // description - promptD := &survey.Multiline{Message: "Issue description:", Default: o.Body} + promptD := NewMultiline(Multiline{ + Message: "Issue description:", + Default: o.Body, + Syntax: "md", + UseEditor: config.GetPreferences().Editor, + }) if err = survey.AskOne(promptD, &o.Body); err != nil { return err } diff --git a/modules/interact/milestone_create.go b/modules/interact/milestone_create.go index 8e8b2b1..07846b5 100644 --- a/modules/interact/milestone_create.go +++ b/modules/interact/milestone_create.go @@ -33,7 +33,11 @@ func CreateMilestone(login *config.Login, owner, repo string) error { } // description - promptM := &survey.Multiline{Message: "Milestone description:"} + promptM := NewMultiline(Multiline{ + Message: "Milestone description:", + Syntax: "md", + UseEditor: config.GetPreferences().Editor, + }) if err := survey.AskOne(promptM, &description); err != nil { return err } diff --git a/modules/interact/prompts.go b/modules/interact/prompts.go index debe944..4775f39 100644 --- a/modules/interact/prompts.go +++ b/modules/interact/prompts.go @@ -14,9 +14,26 @@ import ( "github.com/araddon/dateparse" ) -// PromptMultiline runs a textfield-style prompt and blocks until input was made. -func PromptMultiline(message string) (content string, err error) { - err = survey.AskOne(&survey.Multiline{Message: message}, &content) +// Multiline represents options for a prompt that expects multiline input +type Multiline struct { + Message string + Default string + Syntax string + UseEditor bool +} + +// NewMultiline creates a prompt that switches between the inline multiline text +// and a texteditor based prompt +func NewMultiline(opts Multiline) (prompt survey.Prompt) { + if opts.UseEditor { + prompt = &survey.Editor{ + Message: opts.Message, + Default: opts.Default, + FileName: "*." + opts.Syntax, + } + } else { + prompt = &survey.Multiline{Message: opts.Message, Default: opts.Default} + } return } diff --git a/modules/interact/pull_review.go b/modules/interact/pull_review.go index ea766d7..3b0affd 100644 --- a/modules/interact/pull_review.go +++ b/modules/interact/pull_review.go @@ -8,6 +8,7 @@ import ( "fmt" "os" + "code.gitea.io/tea/modules/config" "code.gitea.io/tea/modules/context" "code.gitea.io/tea/modules/task" @@ -55,7 +56,11 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error { if (state == gitea.ReviewStateComment && len(codeComments) == 0) || state == gitea.ReviewStateRequestChanges { promptOpts = survey.WithValidator(survey.Required) } - err = survey.AskOne(&survey.Multiline{Message: "Concluding comment:"}, &comment, promptOpts) + err = survey.AskOne(NewMultiline(Multiline{ + Message: "Concluding comment:", + Syntax: "md", + UseEditor: config.GetPreferences().Editor, + }), &comment, promptOpts) if err != nil { return err } @@ -65,6 +70,7 @@ func ReviewPull(ctx *context.TeaContext, idx int64) error { // DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff, // (3) parses resulting file into code comments. +// It doesn't really make sense to use survey.Editor() here, as we'd read the file content at least twice. func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) { tmpFile, err := task.SavePullDiff(ctx, idx) if err != nil {