github.com/abhinav/git-pr@v0.6.1-0.20171029234004-54218d68c11b/editor/vi_like.go (about) 1 package editor 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 ) 10 11 // ErrFileUnsaved is returned if the user exited a vi-like editor without 12 // saving the file. 13 var ErrFileUnsaved = errors.New("file was not saved") 14 15 const _viLikeplaceholder = "#### git-stack placeholder ####" 16 17 // ViLike is an editor backed by vi/vim/neovim/etc. 18 // 19 // If the user exits the editor without saving the file, no changes are 20 // recorded. 21 type ViLike struct { 22 name string 23 path string 24 } 25 26 // NewViLike builds a new vi-like editor. 27 func NewViLike(name string) (*ViLike, error) { 28 path, err := exec.LookPath(name) 29 if err != nil { 30 return nil, fmt.Errorf("could not resolve editor %q: %v", name, err) 31 } 32 33 return &ViLike{name: name, path: path}, nil 34 } 35 36 // Name of the editor. 37 func (vi *ViLike) Name() string { 38 return vi.name 39 } 40 41 // EditString asks the user to edit the given string inside a vi-like editor. 42 // ErrFileUnsaved is returned if the user exits the editor without saving the 43 // file. 44 func (vi *ViLike) EditString(in string) (string, error) { 45 sourceFile, err := tempFileWithContents(in) 46 if err != nil { 47 return "", err 48 } 49 defer os.Remove(sourceFile) 50 51 destFile, err := tempFileWithContents(_viLikeplaceholder) 52 if err != nil { 53 return "", err 54 } 55 defer os.Remove(destFile) 56 57 // To detect this, we create a file with some placeholder contents, load 58 // it up in vim and replace the placeholder contents with the actual 59 // contents we want to edit. If the user doesn't save it, the placeholder 60 // will be retained. 61 cmd := exec.Command( 62 vi.path, 63 "-c", "%d", // delete placeholder 64 "-c", "0read "+sourceFile, // read the source file 65 "-c", "$d", // delete trailing newline 66 "-c", "set ft=gitcommit | 0", // set filetype and go to start of file 67 destFile) 68 cmd.Stdout = os.Stdout 69 cmd.Stdin = os.Stdin 70 cmd.Stderr = os.Stderr 71 if err := cmd.Run(); err != nil { 72 return "", fmt.Errorf("editor %q failed: %v", vi.name, err) 73 } 74 75 contents, err := ioutil.ReadFile(destFile) 76 if err != nil { 77 return "", fmt.Errorf("could not read temporary file: %v", err) 78 } 79 80 out := string(contents) 81 if out == _viLikeplaceholder { 82 return "", ErrFileUnsaved 83 } 84 85 return out, nil 86 }