gitlab.com/prarit/lab@v0.14.0/internal/git/edit.go (about) 1 package git 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "regexp" 10 "strings" 11 ) 12 13 // Edit opens a file in the users editor and returns the title and body. It 14 // store a temporary file in your .git directory or /tmp if accessed outside of 15 // a git repo. 16 func Edit(filePrefix, message string) (string, string, error) { 17 var ( 18 dir string 19 err error 20 ) 21 if InsideGitRepo() { 22 dir, err = GitDir() 23 if err != nil { 24 return "", "", err 25 } 26 } else { 27 dir = "/tmp" 28 } 29 filePath := filepath.Join(dir, fmt.Sprintf("%s_EDITMSG", filePrefix)) 30 editorPath, err := editorPath() 31 if err != nil { 32 return "", "", err 33 } 34 defer os.Remove(filePath) 35 36 // Write generated/template message to file 37 if _, err := os.Stat(filePath); os.IsNotExist(err) && message != "" { 38 err = ioutil.WriteFile(filePath, []byte(message), 0644) 39 if err != nil { 40 return "", "", err 41 } 42 } 43 44 cmd := editorCMD(editorPath, filePath) 45 err = cmd.Run() 46 if err != nil { 47 return "", "", err 48 } 49 50 contents, err := ioutil.ReadFile(filePath) 51 if err != nil { 52 return "", "", err 53 } 54 55 return parseTitleBody(strings.TrimSpace(string(contents))) 56 } 57 58 func editorPath() (string, error) { 59 cmd := New("var", "GIT_EDITOR") 60 cmd.Stdout = nil 61 e, err := cmd.Output() 62 if err != nil { 63 return "", err 64 } 65 return strings.TrimSpace(string(e)), nil 66 } 67 68 func editorCMD(editorPath, filePath string) *exec.Cmd { 69 parts := strings.Split(editorPath, " ") 70 r := regexp.MustCompile("[nmg]?vi[m]?$") 71 args := make([]string, 0, 3) 72 if r.MatchString(editorPath) { 73 args = append(args, "--cmd", "set ft=gitcommit tw=0 wrap lbr") 74 } 75 args = append(args, parts[1:]...) 76 args = append(args, filePath) 77 cmd := exec.Command(parts[0], args...) 78 cmd.Stdin = os.Stdin 79 cmd.Stdout = os.Stdout 80 cmd.Stderr = os.Stderr 81 return cmd 82 } 83 84 func parseTitleBody(message string) (string, string, error) { 85 // Grab all the lines that don't start with the comment char 86 cc := CommentChar() 87 r := regexp.MustCompile(`(?m:^)[^` + cc + `].*(?m:$)`) 88 cr := regexp.MustCompile(`(?m:^)\s*` + cc) 89 parts := r.FindAllString(message, -1) 90 noComments := make([]string, 0) 91 for _, p := range parts { 92 if !cr.MatchString(p) { 93 noComments = append(noComments, p) 94 } 95 } 96 msg := strings.Join(noComments, "\n") 97 if strings.TrimSpace(msg) == "" { 98 return "", "", nil 99 } 100 101 r = regexp.MustCompile(`\n\s*\n`) 102 msg = strings.Replace(msg, "\\#", "#", -1) 103 parts = r.Split(msg, 2) 104 105 if strings.Contains(parts[0], "\n") { 106 return "\n", parts[0], nil 107 } 108 if len(parts) < 2 { 109 return parts[0], "", nil 110 } 111 return parts[0], parts[1], nil 112 }