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  }