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