Skip to content

Commit ca04655

Browse files
committed
merge master/better-file-opening into master
2 parents 54408a9 + 4281cc2 commit ca04655

File tree

9 files changed

+273
-27
lines changed

9 files changed

+273
-27
lines changed

Gopkg.lock

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

files_panel.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ func handleFilePress(g *gocui.Gui, v *gocui.View) error {
6363
return handleFileSelect(g, v)
6464
}
6565

66+
func handleAddPatch(g *gocui.Gui, v *gocui.View) error {
67+
file, err := getSelectedFile(g)
68+
if err != nil {
69+
if err == ErrNoFiles {
70+
return nil
71+
}
72+
return err
73+
}
74+
if !file.HasUnstagedChanges {
75+
return createErrorPanel(g, "File has no unstaged changes to add")
76+
}
77+
gitAddPatch(g, file.Name)
78+
return err
79+
}
80+
6681
func getSelectedFile(g *gocui.Gui) (GitFile, error) {
6782
if len(state.GitFiles) == 0 {
6883
return GitFile{}, ErrNoFiles
@@ -163,7 +178,7 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
163178
if message == "" {
164179
return createErrorPanel(g, "You cannot commit without a commit message")
165180
}
166-
if output, err := gitCommit(message); err != nil {
181+
if output, err := gitCommit(g, message); err != nil {
167182
return createErrorPanel(g, output)
168183
}
169184
refreshFiles(g)
@@ -172,26 +187,32 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
172187
return nil
173188
}
174189

175-
func genericFileOpen(g *gocui.Gui, v *gocui.View, open func(string) (string, error)) error {
190+
func genericFileOpen(g *gocui.Gui, v *gocui.View, open func(*gocui.Gui, string) (string, error)) error {
176191
file, err := getSelectedFile(g)
177192
if err != nil {
178193
if err != ErrNoFiles {
179194
return err
180195
}
181196
return nil
182197
}
183-
if output, err := open(file.Name); err != nil {
184-
return createErrorPanel(g, output)
198+
if _, err := open(g, file.Name); err != nil {
199+
return createErrorPanel(g, err.Error())
185200
}
186201
return nil
187202
}
188203

204+
func handleFileEdit(g *gocui.Gui, v *gocui.View) error {
205+
return genericFileOpen(g, v, editFile)
206+
}
207+
189208
func handleFileOpen(g *gocui.Gui, v *gocui.View) error {
190209
return genericFileOpen(g, v, openFile)
191210
}
211+
192212
func handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
193213
return genericFileOpen(g, v, sublimeOpenFile)
194214
}
215+
195216
func handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
196217
return genericFileOpen(g, v, vsCodeOpenFile)
197218
}

gitcommands.go

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ import (
1313
"time"
1414

1515
"github.com/fatih/color"
16+
"github.com/jesseduffield/gocui"
17+
gitconfig "github.com/tcnksm/go-gitconfig"
1618
)
1719

1820
var (
1921
// ErrNoCheckedOutBranch : When we have no checked out branch
2022
ErrNoCheckedOutBranch = errors.New("No currently checked out branch")
23+
24+
// ErrNoOpenCommand : When we don't know which command to use to open a file
25+
ErrNoOpenCommand = errors.New("Unsure what command to use to open this file")
2126
)
2227

2328
// GitFile : A staged/unstaged file
@@ -263,23 +268,73 @@ func runCommand(command string) (string, error) {
263268
commandStartTime := time.Now()
264269
commandLog(command)
265270
splitCmd := strings.Split(command, " ")
271+
devLog(splitCmd)
266272
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).CombinedOutput()
267273
devLog("run command time: ", time.Now().Sub(commandStartTime))
268274
return sanitisedCommandOutput(cmdOut, err)
269275
}
270276

271-
func openFile(filename string) (string, error) {
272-
return runCommand("open " + filename)
273-
}
274-
275-
func vsCodeOpenFile(filename string) (string, error) {
277+
func vsCodeOpenFile(g *gocui.Gui, filename string) (string, error) {
276278
return runCommand("code -r " + filename)
277279
}
278280

279-
func sublimeOpenFile(filename string) (string, error) {
281+
func sublimeOpenFile(g *gocui.Gui, filename string) (string, error) {
280282
return runCommand("subl " + filename)
281283
}
282284

285+
func openFile(g *gocui.Gui, filename string) (string, error) {
286+
cmdName, cmdTrail, err := getOpenCommand()
287+
if err != nil {
288+
return "", err
289+
}
290+
return runCommand(cmdName + " " + filename + cmdTrail)
291+
}
292+
293+
func getOpenCommand() (string, string, error) {
294+
//NextStep open equivalents: xdg-open (linux), cygstart (cygwin), open (OSX)
295+
trailMap := map[string]string{
296+
"xdg-open": " &>/dev/null &",
297+
"cygstart": "",
298+
"open": "",
299+
}
300+
for name, trail := range trailMap {
301+
if out, _ := runCommand("which " + name); out != "exit status 1" {
302+
return name, trail, nil
303+
}
304+
}
305+
return "", "", ErrNoOpenCommand
306+
}
307+
308+
func gitAddPatch(g *gocui.Gui, filename string) {
309+
runSubProcess(g, "git", "add", "-p", filename)
310+
}
311+
312+
func editFile(g *gocui.Gui, filename string) (string, error) {
313+
editor, _ := gitconfig.Global("core.editor")
314+
if editor == "" {
315+
editor = os.Getenv("VISUAL")
316+
}
317+
if editor == "" {
318+
editor = os.Getenv("EDITOR")
319+
}
320+
if editor == "" {
321+
return "", createErrorPanel(g, "No editor defined in $VISUAL, $EDITOR, or git config.")
322+
}
323+
runSubProcess(g, editor, filename)
324+
return "", nil
325+
}
326+
327+
func runSubProcess(g *gocui.Gui, cmdName string, commandArgs ...string) {
328+
subprocess = exec.Command(cmdName, commandArgs...)
329+
subprocess.Stdin = os.Stdin
330+
subprocess.Stdout = os.Stdout
331+
subprocess.Stderr = os.Stderr
332+
333+
g.Update(func(g *gocui.Gui) error {
334+
return ErrSubprocess
335+
})
336+
}
337+
283338
func getBranchGraph(branch string, baseBranch string) (string, error) {
284339
return runCommand("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branch)
285340

@@ -403,7 +458,12 @@ func removeFile(file GitFile) error {
403458
return err
404459
}
405460

406-
func gitCommit(message string) (string, error) {
461+
func gitCommit(g *gocui.Gui, message string) (string, error) {
462+
gpgsign, _ := gitconfig.Global("commit.gpgsign")
463+
if gpgsign != "" {
464+
runSubProcess(g, "bash", "-c", "git commit -m \""+message+"\"")
465+
return "", nil
466+
}
407467
return runDirectCommand("git commit -m \"" + message + "\"")
408468
}
409469

gui.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
// "io"
66
// "io/ioutil"
77

8-
"log"
98
"runtime"
109
"strings"
1110
"time"
@@ -207,10 +206,10 @@ func updateLoader(g *gocui.Gui) {
207206
}
208207
}
209208

210-
func run() {
209+
func run() (err error) {
211210
g, err := gocui.NewGui(gocui.OutputNormal, OverlappingEdges)
212211
if err != nil {
213-
log.Panicln(err)
212+
return
214213
}
215214
defer g.Close()
216215

@@ -229,13 +228,12 @@ func run() {
229228

230229
g.SetManagerFunc(layout)
231230

232-
if err := keybindings(g); err != nil {
233-
log.Panicln(err)
231+
if err = keybindings(g); err != nil {
232+
return
234233
}
235234

236-
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
237-
log.Panicln(err)
238-
}
235+
err = g.MainLoop()
236+
return
239237
}
240238

241239
func quit(g *gocui.Gui, v *gocui.View) error {

keybindings.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ func keybindings(g *gocui.Gui) error {
3030
Binding{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleFilePress},
3131
Binding{ViewName: "files", Key: 'd', Modifier: gocui.ModNone, Handler: handleFileRemove},
3232
Binding{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: handleSwitchToMerge},
33+
Binding{ViewName: "files", Key: 'e', Modifier: gocui.ModNone, Handler: handleFileEdit},
3334
Binding{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: handleFileOpen},
3435
Binding{ViewName: "files", Key: 's', Modifier: gocui.ModNone, Handler: handleSublimeFileOpen},
3536
Binding{ViewName: "files", Key: 'v', Modifier: gocui.ModNone, Handler: handleVsCodeFileOpen},
3637
Binding{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: handleIgnoreFile},
3738
Binding{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: handleRefreshFiles},
3839
Binding{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: handleStashSave},
3940
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
41+
Binding{ViewName: "files", Key: 't', Modifier: gocui.ModNone, Handler: handleAddPatch},
4042
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
4143
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
4244
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},

main.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,34 @@
11
package main
22

33
import (
4+
"errors"
45
"flag"
56
"fmt"
67
"log"
78
"os"
9+
"os/exec"
810
"os/user"
911
"time"
1012

1113
"github.com/fatih/color"
14+
"github.com/jesseduffield/gocui"
1215
)
1316

17+
// ErrSubProcess is raised when we are running a subprocess
1418
var (
15-
startTime time.Time
16-
debugging bool
19+
ErrSubprocess = errors.New("running subprocess")
20+
subprocess *exec.Cmd
21+
startTime time.Time
1722

1823
// Rev - Git Revision
1924
Rev string
2025

2126
// Version - Version number
2227
Version = "unversioned"
2328

24-
builddate string
25-
debuggingPointer = flag.Bool("debug", false, "a boolean")
26-
versionFlag = flag.Bool("v", false, "Print the current version")
29+
builddate string
30+
debuggingFlag = flag.Bool("debug", false, "a boolean")
31+
versionFlag = flag.Bool("v", false, "Print the current version")
2732
)
2833

2934
func homeDirectory() string {
@@ -47,7 +52,7 @@ func commandLog(objects ...interface{}) {
4752
}
4853

4954
func localLog(colour color.Attribute, path string, objects ...interface{}) {
50-
if !debugging {
55+
if !*debuggingFlag {
5156
return
5257
}
5358
f, _ := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
@@ -69,7 +74,6 @@ func navigateToRepoRootDirectory() {
6974

7075
func main() {
7176
startTime = time.Now()
72-
debugging = *debuggingPointer
7377
devLog("\n\n\n\n\n\n\n\n\n\n")
7478
flag.Parse()
7579
if *versionFlag {
@@ -78,5 +82,15 @@ func main() {
7882
}
7983
verifyInGitRepo()
8084
navigateToRepoRootDirectory()
81-
run()
85+
for {
86+
if err := run(); err != nil {
87+
if err == gocui.ErrQuit {
88+
break
89+
} else if err == ErrSubprocess {
90+
subprocess.Run()
91+
} else {
92+
log.Panicln(err)
93+
}
94+
}
95+
}
8296
}

test/shell_script_input_prompt.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
# For testing subprocesses that require input
3+
# Ask the user for login details
4+
read -p 'Username: ' user
5+
read -sp 'Password: ' pass
6+
echo
7+
echo Hello $user

vendor/github.com/tcnksm/go-gitconfig/LICENSE

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)