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  }