github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/surveyext/editor.go (about) 1 package surveyext 2 3 // This file extends survey.Editor to give it more flexible behavior. For more context, read 4 // https://github.com/ungtb10d/cli/issues/70 5 // To see what we extended, search through for EXTENDED comments. 6 7 import ( 8 "os" 9 "path/filepath" 10 "runtime" 11 12 "github.com/AlecAivazis/survey/v2" 13 "github.com/AlecAivazis/survey/v2/terminal" 14 shellquote "github.com/kballard/go-shellquote" 15 ) 16 17 var ( 18 bom = []byte{0xef, 0xbb, 0xbf} 19 defaultEditor = "nano" // EXTENDED to switch from vim as a default editor 20 ) 21 22 func init() { 23 if g := os.Getenv("GIT_EDITOR"); g != "" { 24 defaultEditor = g 25 } else if v := os.Getenv("VISUAL"); v != "" { 26 defaultEditor = v 27 } else if e := os.Getenv("EDITOR"); e != "" { 28 defaultEditor = e 29 } else if runtime.GOOS == "windows" { 30 defaultEditor = "notepad" 31 } 32 } 33 34 // EXTENDED to enable different prompting behavior 35 type GhEditor struct { 36 *survey.Editor 37 EditorCommand string 38 BlankAllowed bool 39 40 lookPath func(string) ([]string, []string, error) 41 } 42 43 // EXTENDED to change prompt text 44 var EditorQuestionTemplate = ` 45 {{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}} 46 {{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}} 47 {{- color "default+hb"}}{{ .Message }} {{color "reset"}} 48 {{- if .ShowAnswer}} 49 {{- color "cyan"}}{{.Answer}}{{color "reset"}}{{"\n"}} 50 {{- else }} 51 {{- if and .Help (not .ShowHelp)}}{{color "cyan"}}[{{ .Config.HelpInput }} for help]{{color "reset"}} {{end}} 52 {{- if and .Default (not .HideDefault)}}{{color "white"}}({{.Default}}) {{color "reset"}}{{end}} 53 {{- color "cyan"}}[(e) to launch {{ .EditorCommand }}{{- if .BlankAllowed }}, enter to skip{{ end }}] {{color "reset"}} 54 {{- end}}` 55 56 // EXTENDED to pass editor name (to use in prompt) 57 type EditorTemplateData struct { 58 survey.Editor 59 EditorCommand string 60 BlankAllowed bool 61 Answer string 62 ShowAnswer bool 63 ShowHelp bool 64 Config *survey.PromptConfig 65 } 66 67 // EXTENDED to augment prompt text and keypress handling 68 func (e *GhEditor) prompt(initialValue string, config *survey.PromptConfig) (interface{}, error) { 69 err := e.Render( 70 EditorQuestionTemplate, 71 // EXTENDED to support printing editor in prompt and BlankAllowed 72 EditorTemplateData{ 73 Editor: *e.Editor, 74 BlankAllowed: e.BlankAllowed, 75 EditorCommand: EditorName(e.EditorCommand), 76 Config: config, 77 }, 78 ) 79 if err != nil { 80 return "", err 81 } 82 83 // start reading runes from the standard in 84 rr := e.NewRuneReader() 85 _ = rr.SetTermMode() 86 defer func() { _ = rr.RestoreTermMode() }() 87 88 cursor := e.NewCursor() 89 _ = cursor.Hide() 90 defer func() { 91 _ = cursor.Show() 92 }() 93 94 for { 95 // EXTENDED to handle the e to edit / enter to skip behavior + BlankAllowed 96 r, _, err := rr.ReadRune() 97 if err != nil { 98 return "", err 99 } 100 if r == 'e' { 101 break 102 } 103 if r == '\r' || r == '\n' { 104 if e.BlankAllowed { 105 return initialValue, nil 106 } else { 107 continue 108 } 109 } 110 if r == terminal.KeyInterrupt { 111 return "", terminal.InterruptErr 112 } 113 if r == terminal.KeyEndTransmission { 114 break 115 } 116 if string(r) == config.HelpInput && e.Help != "" { 117 err = e.Render( 118 EditorQuestionTemplate, 119 EditorTemplateData{ 120 // EXTENDED to support printing editor in prompt, BlankAllowed 121 Editor: *e.Editor, 122 BlankAllowed: e.BlankAllowed, 123 EditorCommand: EditorName(e.EditorCommand), 124 ShowHelp: true, 125 Config: config, 126 }, 127 ) 128 if err != nil { 129 return "", err 130 } 131 } 132 continue 133 } 134 135 stdio := e.Stdio() 136 lookPath := e.lookPath 137 if lookPath == nil { 138 lookPath = defaultLookPath 139 } 140 text, err := edit(e.EditorCommand, e.FileName, initialValue, stdio.In, stdio.Out, stdio.Err, cursor, lookPath) 141 if err != nil { 142 return "", err 143 } 144 145 // check length, return default value on empty 146 if len(text) == 0 && !e.AppendDefault { 147 return e.Default, nil 148 } 149 150 return text, nil 151 } 152 153 // EXTENDED This is straight copypasta from survey to get our overridden prompt called.; 154 func (e *GhEditor) Prompt(config *survey.PromptConfig) (interface{}, error) { 155 initialValue := "" 156 if e.Default != "" && e.AppendDefault { 157 initialValue = e.Default 158 } 159 return e.prompt(initialValue, config) 160 } 161 162 func EditorName(editorCommand string) string { 163 if editorCommand == "" { 164 editorCommand = defaultEditor 165 } 166 if args, err := shellquote.Split(editorCommand); err == nil { 167 editorCommand = args[0] 168 } 169 return filepath.Base(editorCommand) 170 }