github.com/jingweno/gh@v2.1.1-0.20221007190738-04a7985fa9a1+incompatible/github/editor.go (about) 1 package github 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "regexp" 12 "strings" 13 14 "github.com/jingweno/gh/cmd" 15 "github.com/jingweno/gh/git" 16 ) 17 18 func NewEditor(topic, message string) (editor *Editor, err error) { 19 messageFile, err := getMessageFile(topic) 20 if err != nil { 21 return 22 } 23 24 program, err := git.Editor() 25 if err != nil { 26 return 27 } 28 29 editor = &Editor{ 30 Program: program, 31 File: messageFile, 32 Message: message, 33 doEdit: doTextEditorEdit, 34 } 35 36 return 37 } 38 39 type Editor struct { 40 Program string 41 File string 42 Message string 43 doEdit func(program, file string) error 44 } 45 46 func (e *Editor) Edit() (content []byte, err error) { 47 if e.Message != "" { 48 err = ioutil.WriteFile(e.File, []byte(e.Message), 0644) 49 if err != nil { 50 return 51 } 52 } 53 defer os.Remove(e.File) 54 55 err = e.doEdit(e.Program, e.File) 56 if err != nil { 57 err = fmt.Errorf("error using text editor for editing message") 58 return 59 } 60 61 content, err = ioutil.ReadFile(e.File) 62 63 return 64 } 65 66 func (e *Editor) EditTitleAndBody() (title, body string, err error) { 67 content, err := e.Edit() 68 if err != nil { 69 return 70 } 71 72 content = bytes.TrimSpace(content) 73 reader := bufio.NewReader(bytes.NewReader(content)) 74 title, body, err = readTitleAndBody(reader) 75 76 return 77 } 78 79 func doTextEditorEdit(program, file string) error { 80 editCmd := cmd.New(program) 81 r := regexp.MustCompile("[mg]?vi[m]$") 82 if r.MatchString(program) { 83 editCmd.WithArg("-c") 84 editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr") 85 } 86 editCmd.WithArg(file) 87 88 return editCmd.Exec() 89 } 90 91 func readTitleAndBody(reader *bufio.Reader) (title, body string, err error) { 92 r := regexp.MustCompile("\\S") 93 var titleParts, bodyParts []string 94 95 line, err := readLine(reader) 96 for err == nil { 97 if strings.HasPrefix(line, "#") { 98 break 99 } 100 101 if len(bodyParts) == 0 && r.MatchString(line) { 102 titleParts = append(titleParts, line) 103 } else { 104 bodyParts = append(bodyParts, line) 105 } 106 107 line, err = readLine(reader) 108 } 109 110 if err == io.EOF { 111 err = nil 112 } 113 114 title = strings.Join(titleParts, " ") 115 title = strings.TrimSpace(title) 116 117 body = strings.Join(bodyParts, "\n") 118 body = strings.TrimSpace(body) 119 120 return 121 } 122 123 func readLine(r *bufio.Reader) (string, error) { 124 var ( 125 isPrefix = true 126 err error 127 line, ln []byte 128 ) 129 130 for isPrefix && err == nil { 131 line, isPrefix, err = r.ReadLine() 132 ln = append(ln, line...) 133 } 134 135 return string(ln), err 136 } 137 138 func getMessageFile(about string) (string, error) { 139 gitDir, err := git.Dir() 140 if err != nil { 141 return "", err 142 } 143 144 return filepath.Join(gitDir, fmt.Sprintf("%s_EDITMSG", about)), nil 145 }