Skip to content

Commit

Permalink
Add missing comments and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
mkchoi212 committed Jun 1, 2018
1 parent 02bd481 commit 6b6fb16
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 76 deletions.
74 changes: 52 additions & 22 deletions layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,43 @@ import (
"github.com/mkchoi212/fac/conflict"
)

// Following constants define the string literal names of 5 views
// that are instantiated via gocui
const (
Current = "current"
Foreign = "foreign"
Panel = "panel"
Prompt = "prompt"
Input = "input prompt"
Input = "input"
)

Up = 3
Down = 4
// `Up` and `Down` represent scrolling directions
// `Horizontal` and `Vertical` represent current code view orientation
// Notice how both pairs of directionality are `not`s of each other
const (
Up = 1
Down = ^1

Horizontal = 5
Vertical = -6
Horizontal = 2
Vertical = ^2
)

var (
ViewOrientation = Vertical
inputHeight = 2
// Following constants define input panel's dimensions
const (
inputHeight = 2
inputCursorPos = 17
promptWidth = 21
)

func printLines(v *gocui.View, lines []string) {
for _, line := range lines {
fmt.Fprint(v, line)
}
}

// layout is used as fac's main gocui.Gui manager
func layout(g *gocui.Gui) (err error) {
if err = makePanels(g); err != nil {
if err = makeCodePanels(g); err != nil {
return
}

Expand All @@ -43,7 +59,10 @@ func layout(g *gocui.Gui) (err error) {
return
}

func makePanels(g *gocui.Gui) error {
// makeCodePanels draws the two panels representing "local" and "incoming" lines of code
// `viewOrientation` is taken into consideration as the panels can either be
// `Vertical` or `Horizontal`
func makeCodePanels(g *gocui.Gui) error {
maxX, maxY := g.Size()
viewHeight := maxY - inputHeight
branchViewWidth := (maxX / 5) * 2
Expand All @@ -52,7 +71,7 @@ func makePanels(g *gocui.Gui) error {
var x0, x1, y0, y1 int
var x2, x3, y2, y3 int

if ViewOrientation == Horizontal {
if viewOrientation == Horizontal {
x0, x1 = 0, branchViewWidth
y0, y1 = 0, viewHeight
x2, x3 = branchViewWidth, branchViewWidth*2
Expand Down Expand Up @@ -87,6 +106,8 @@ func makePanels(g *gocui.Gui) error {
return nil
}

// makeOverviewPanel draws the panel on the right-side of the CUI
// listing all the conflicts that need to be resolved
func makeOverviewPanel(g *gocui.Gui) error {
maxX, maxY := g.Size()
viewHeight := maxY - inputHeight
Expand All @@ -101,13 +122,15 @@ func makeOverviewPanel(g *gocui.Gui) error {
return nil
}

// makePrompt draws two panels on the bottom of the CUI
// A "prompt view" which prompts the user for available keybindings and
// a "user input view" which is an area where the user can type in queries
func makePrompt(g *gocui.Gui) error {
maxX, maxY := g.Size()
inputHeight := 2
viewHeight := maxY - inputHeight

// Prompt view
if v, err := g.SetView(Prompt, 0, viewHeight, 21, viewHeight+inputHeight); err != nil {
if v, err := g.SetView(Prompt, 0, viewHeight, promptWidth, viewHeight+inputHeight); err != nil {
if err != gocui.ErrUnknownView {
return err
}
Expand All @@ -116,7 +139,7 @@ func makePrompt(g *gocui.Gui) error {
}

// User input view
if v, err := g.SetView(Input, 17, viewHeight, maxX, viewHeight+inputHeight); err != nil {
if v, err := g.SetView(Input, inputCursorPos, viewHeight, maxX, viewHeight+inputHeight); err != nil {
if err != gocui.ErrUnknownView {
return err
}
Expand All @@ -130,7 +153,9 @@ func makePrompt(g *gocui.Gui) error {
return nil
}

func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
// Select selects conflict `c` as the current conflict displayed on the screen
// When selecting a conflict, it updates the side panel, and the code view
func Select(g *gocui.Gui, c *conflict.Conflict, showHelp bool) error {
// Update side panel
g.Update(func(g *gocui.Gui) error {
v, err := g.View(Panel)
Expand All @@ -155,7 +180,7 @@ func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
}

if showHelp {
printHelp(v, &binding)
PrintHelp(v, &binding)
}
return nil
})
Expand Down Expand Up @@ -197,20 +222,23 @@ func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
return nil
}

func Resolve(c *conflict.Conflict, g *gocui.Gui, v *gocui.View, version int) error {
// Resolve resolves the provided conflict and moves to the next conflict
// in the queue
func Resolve(g *gocui.Gui, v *gocui.View, c *conflict.Conflict, version int) error {
g.Update(func(g *gocui.Gui) error {
c.Choice = version
MoveToItem(Down, g, v)
Move(g, v, Down)
return nil
})
return nil
}

func MoveToItem(dir int, g *gocui.Gui, v *gocui.View) error {
// Move goes to the next conflict in the list in the provided `direction`
func Move(g *gocui.Gui, v *gocui.View, direction int) error {
originalCur := cur

for {
if dir == Up {
if direction == Up {
cur--
} else {
cur++
Expand All @@ -227,14 +255,16 @@ func MoveToItem(dir int, g *gocui.Gui, v *gocui.View) error {
}
}

// Quit application if all items are resolved
if originalCur == cur && conflicts[cur].Choice != 0 {
globalQuit(g)
}

Select(conflicts[cur], g, false)
Select(g, conflicts[cur], false)
return nil
}

// Scroll scrolls the two code view panels in `direction` by one line
func Scroll(g *gocui.Gui, c *conflict.Conflict, direction int) {
if direction == Up {
c.TopPeek--
Expand All @@ -244,5 +274,5 @@ func Scroll(g *gocui.Gui, c *conflict.Conflict, direction int) {
return
}

Select(c, g, false)
Select(g, c, false)
}
91 changes: 48 additions & 43 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,107 +13,113 @@ import (
)

var (
conflicts = []*conflict.Conflict{}
cur = 0
consecutiveError = 0
binding = key.Binding{}
viewOrientation = Vertical
conflicts = []*conflict.Conflict{}
binding = key.Binding{}
cur = 0
consecutiveErrCnt = 0
)

func printLines(v *gocui.View, lines []string) {
for _, line := range lines {
fmt.Fprint(v, line)
}
}

// globalQuit is invoked when the user quits the contact and or
// when all conflicts have been resolved
func globalQuit(g *gocui.Gui) {
g.Update(func(g *gocui.Gui) error {
return gocui.ErrQuit
})
}

// quit is invoked when the user presses "Ctrl+C"
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}

// parseInput is invoked when the user presses "Enter"
// It `evaluate`s the user's query and reflects the state on the UI
func parseInput(g *gocui.Gui, v *gocui.View) error {
in := strings.TrimSuffix(v.Buffer(), "\n")
v.Clear()
v.SetCursor(0, 0)

if err := Evaluate(g, v, conflicts[cur], in); err != nil {
if err == ErrUnknownCmd {
consecutiveError++
if consecutiveError > 3 {
Select(conflicts[cur], g, true)
consecutiveErrCnt++
if consecutiveErrCnt > 3 {
Select(g, conflicts[cur], true)
}
} else {
return err
}
} else {
consecutiveError = 0
consecutiveErrCnt = 0
}

PrintPrompt(g)
return nil
}

// Start initializes, configures, and starts a fresh instance of gocui
func Start() (err error) {
g, err := gocui.NewGui(gocui.OutputNormal)
// findConflicts looks at the current directory and returns an
// array of `File`s that contain merge conflicts
// It returns an error if it fails to parse the conflicts
func findConflicts() (files []conflict.File, err error) {
cwd, err := os.Getwd()
if err != nil {
return
}

defer g.Close()
g.SetManagerFunc(layout)
g.Cursor = true

if err = g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return
}
if err = g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, parseInput); err != nil {
if files, err = conflict.Find(cwd); err != nil {
return
}

Select(conflicts[cur], g, false)

if err = g.MainLoop(); err != nil {
return
for i := range files {
file := &files[i]
for j := range file.Conflicts {
conflicts = append(conflicts, &file.Conflicts[j])
}
}

return
}

func findConflicts() (files []conflict.File, err error) {
cwd, err := os.Getwd()
// runUI initializes, configures, and starts a fresh instance of gocui
func runUI() (err error) {
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {
return
}

if files, err = conflict.Find(cwd); err != nil {
defer g.Close()
g.SetManagerFunc(layout)
g.Cursor = true

if err = g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return
}
if err = g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, parseInput); err != nil {
return
}

for i := range files {
file := &files[i]
for j := range file.Conflicts {
conflicts = append(conflicts, &file.Conflicts[j])
}
Select(g, conflicts[cur], false)

if err = g.MainLoop(); err != nil {
return
}

return
}

func runUI() error {
// mainLoop manages how the main instances of gocui are created and destroyed
func mainLoop() error {
for {
if err := Start(); err != nil {
if err := runUI(); err != nil {
// Instantiates a fresh instance of gocui
// when user opens an editor because screen is dirty
if err == ErrOpenEditor {
newLines, err := editor.Open(conflicts[cur])
if err != nil {
return err
}
if err = conflicts[cur].Update(newLines); err != nil {
consecutiveError++
consecutiveErrCnt++
}
} else if err == gocui.ErrQuit {
break
Expand All @@ -137,7 +143,6 @@ func main() {
die(err)
}

// Find and parse conflicts
files, err := findConflicts()
if err != nil {
die(err)
Expand All @@ -148,7 +153,7 @@ func main() {
os.Exit(0)
}

if err = runUI(); err != nil {
if err = mainLoop(); err != nil {
die(err)
}

Expand All @@ -158,5 +163,5 @@ func main() {
}
}

printSummary(conflicts)
PrintSummary(conflicts)
}
Loading

0 comments on commit 6b6fb16

Please sign in to comment.