github.com/pengwynn/gh@v1.0.1-0.20140118055701-14327ca3942e/github/editor.go (about)

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