github.com/trevoraustin/hub@v2.2.0-preview1.0.20141105230840-96d8bfc654cc+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/github/hub/cmd" 15 "github.com/github/hub/git" 16 ) 17 18 func NewEditor(filePrefix, topic, message string) (editor *Editor, err error) { 19 messageFile, err := getMessageFile(filePrefix) 20 if err != nil { 21 return 22 } 23 24 program, err := git.Editor() 25 if err != nil { 26 return 27 } 28 29 cs := git.CommentChar() 30 31 editor = &Editor{ 32 Program: program, 33 Topic: topic, 34 File: messageFile, 35 Message: message, 36 CS: cs, 37 openEditor: openTextEditor, 38 } 39 40 return 41 } 42 43 type Editor struct { 44 Program string 45 Topic string 46 File string 47 Message string 48 CS string 49 openEditor func(program, file string) error 50 } 51 52 func (e *Editor) DeleteFile() error { 53 return os.Remove(e.File) 54 } 55 56 func (e *Editor) EditTitleAndBody() (title, body string, err error) { 57 content, err := e.openAndEdit() 58 if err != nil { 59 return 60 } 61 62 content = bytes.TrimSpace(content) 63 reader := bytes.NewReader(content) 64 title, body, err = readTitleAndBody(reader, e.CS) 65 66 return 67 } 68 69 func (e *Editor) openAndEdit() (content []byte, err error) { 70 err = e.writeContent() 71 if err != nil { 72 return 73 } 74 75 err = e.openEditor(e.Program, e.File) 76 if err != nil { 77 err = fmt.Errorf("error using text editor for %s message", e.Topic) 78 defer e.DeleteFile() 79 return 80 } 81 82 content, err = e.readContent() 83 84 return 85 } 86 87 func (e *Editor) writeContent() (err error) { 88 // only write message if file doesn't exist 89 if !e.isFileExist() && e.Message != "" { 90 err = ioutil.WriteFile(e.File, []byte(e.Message), 0644) 91 if err != nil { 92 return 93 } 94 } 95 96 return 97 } 98 99 func (e *Editor) isFileExist() bool { 100 _, err := os.Stat(e.File) 101 return err == nil || !os.IsNotExist(err) 102 } 103 104 func (e *Editor) readContent() (content []byte, err error) { 105 return ioutil.ReadFile(e.File) 106 } 107 108 func openTextEditor(program, file string) error { 109 editCmd := cmd.New(program) 110 r := regexp.MustCompile("[mg]?vi[m]$") 111 if r.MatchString(program) { 112 editCmd.WithArg("-c") 113 editCmd.WithArg("set ft=gitcommit tw=0 wrap lbr") 114 } 115 editCmd.WithArg(file) 116 117 return editCmd.Exec() 118 } 119 120 func readTitleAndBody(reader io.Reader, cs string) (title, body string, err error) { 121 var titleParts, bodyParts []string 122 123 r := regexp.MustCompile("\\S") 124 scanner := bufio.NewScanner(reader) 125 for scanner.Scan() { 126 line := scanner.Text() 127 if strings.HasPrefix(line, cs) { 128 continue 129 } 130 131 if len(bodyParts) == 0 && r.MatchString(line) { 132 titleParts = append(titleParts, line) 133 } else { 134 bodyParts = append(bodyParts, line) 135 } 136 } 137 138 if err = scanner.Err(); err != nil { 139 return 140 } 141 142 title = strings.Join(titleParts, " ") 143 title = strings.TrimSpace(title) 144 145 body = strings.Join(bodyParts, "\n") 146 body = strings.TrimSpace(body) 147 148 return 149 } 150 151 func getMessageFile(about string) (string, error) { 152 gitDir, err := git.Dir() 153 if err != nil { 154 return "", err 155 } 156 157 return filepath.Join(gitDir, fmt.Sprintf("%s_EDITMSG", about)), nil 158 }