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 <name>` 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 <git@nroo.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/429 Reviewed-by: Andrew Thornton <art27@cantab.net> Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Norwin <noerw@noreply.gitea.io> Co-committed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
parent
5b77345b03
commit
78a95f1ca4
|
@ -9,13 +9,15 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/tea/modules/interact"
|
|
||||||
|
|
||||||
"code.gitea.io/sdk/gitea"
|
|
||||||
"code.gitea.io/tea/cmd/flags"
|
"code.gitea.io/tea/cmd/flags"
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
|
"code.gitea.io/tea/modules/interact"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
"code.gitea.io/tea/modules/utils"
|
"code.gitea.io/tea/modules/utils"
|
||||||
|
|
||||||
|
"code.gitea.io/sdk/gitea"
|
||||||
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"github.com/urfave/cli/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")
|
body = strings.Join([]string{body, string(bodyStdin)}, "\n\n")
|
||||||
}
|
}
|
||||||
} else if len(body) == 0 {
|
} 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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,16 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"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
|
// LocalConfig represents local configurations
|
||||||
type LocalConfig struct {
|
type LocalConfig struct {
|
||||||
Logins []Login `yaml:"logins"`
|
Logins []Login `yaml:"logins"`
|
||||||
|
Prefs Preferences `yaml:"preferences"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -55,6 +62,11 @@ func GetConfigPath() string {
|
||||||
return configFilePath
|
return configFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPreferences returns preferences based on the config file
|
||||||
|
func GetPreferences() Preferences {
|
||||||
|
return config.Prefs
|
||||||
|
}
|
||||||
|
|
||||||
// loadConfig load config from file
|
// loadConfig load config from file
|
||||||
func loadConfig() (err error) {
|
func loadConfig() (err error) {
|
||||||
loadConfigOnce.Do(func() {
|
loadConfigOnce.Do(func() {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"code.gitea.io/sdk/gitea"
|
"code.gitea.io/sdk/gitea"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/print"
|
"code.gitea.io/tea/modules/print"
|
||||||
|
|
||||||
"github.com/AlecAivazis/survey/v2"
|
"github.com/AlecAivazis/survey/v2"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
|
@ -43,7 +43,12 @@ func promptIssueProperties(login *config.Login, owner, repo string, o *gitea.Cre
|
||||||
}
|
}
|
||||||
|
|
||||||
// description
|
// 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 {
|
if err = survey.AskOne(promptD, &o.Body); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,11 @@ func CreateMilestone(login *config.Login, owner, repo string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// description
|
// 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 {
|
if err := survey.AskOne(promptM, &description); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,26 @@ import (
|
||||||
"github.com/araddon/dateparse"
|
"github.com/araddon/dateparse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PromptMultiline runs a textfield-style prompt and blocks until input was made.
|
// Multiline represents options for a prompt that expects multiline input
|
||||||
func PromptMultiline(message string) (content string, err error) {
|
type Multiline struct {
|
||||||
err = survey.AskOne(&survey.Multiline{Message: message}, &content)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"code.gitea.io/tea/modules/config"
|
||||||
"code.gitea.io/tea/modules/context"
|
"code.gitea.io/tea/modules/context"
|
||||||
"code.gitea.io/tea/modules/task"
|
"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 {
|
if (state == gitea.ReviewStateComment && len(codeComments) == 0) || state == gitea.ReviewStateRequestChanges {
|
||||||
promptOpts = survey.WithValidator(survey.Required)
|
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 {
|
if err != nil {
|
||||||
return err
|
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,
|
// DoDiffReview (1) fetches & saves diff in tempfile, (2) starts $VISUAL or $EDITOR to comment on diff,
|
||||||
// (3) parses resulting file into code comments.
|
// (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) {
|
func DoDiffReview(ctx *context.TeaContext, idx int64) ([]gitea.CreatePullReviewComment, error) {
|
||||||
tmpFile, err := task.SavePullDiff(ctx, idx)
|
tmpFile, err := task.SavePullDiff(ctx, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue