Skip to content

Commit

Permalink
Refactor git conflict marker parsing methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mkchoi212 committed May 12, 2018
1 parent 769ab01 commit c1dd493
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 85 deletions.
3 changes: 0 additions & 3 deletions conflict/command.go
Expand Up @@ -49,10 +49,7 @@ func MarkerLocations(cwd string) ([]string, error) {

if len(stderr) != 0 {
return nil, errors.New(stderr)
} else if len(stdout) == 0 {
return nil, NewErrNoConflict("No conflicts detected 🎉")
}

return strings.Split(stdout, "\n"), nil
}

Expand Down
27 changes: 11 additions & 16 deletions conflict/conflict.go
@@ -1,6 +1,8 @@
package conflict

import (
"strings"

"github.com/mkchoi212/fac/color"
)

Expand All @@ -26,22 +28,6 @@ type Conflict struct {
DisplayDiff bool
}

// ErrNoConflict is used to indicate that there
// are no errors present in the git repo
type ErrNoConflict struct {
message string
}

func NewErrNoConflict(message string) *ErrNoConflict {
return &ErrNoConflict{
message: message,
}
}

func (e *ErrNoConflict) Error() string {
return e.message
}

func (c *Conflict) Equal(c2 *Conflict) bool {
return c.AbsolutePath == c2.AbsolutePath && c.Start == c2.Start
}
Expand All @@ -50,6 +36,15 @@ func (c *Conflict) ToggleDiff() {
c.DisplayDiff = !(c.DisplayDiff)
}

// Extract extracts lines where conflicts exist
func (c *Conflict) Extract(lines []string) error {
c.LocalLines = lines[c.Start : c.Middle-1]
c.IncomingLines = lines[c.Middle : c.End-1]
c.CurrentName = strings.Split(lines[c.Start-1], " ")[1]
c.ForeignName = strings.Split(lines[c.End-1], " ")[1]
return nil
}

func (c *Conflict) PaddingLines() (topPadding, bottomPadding []string) {
lines := FileLines[c.AbsolutePath]
start, end := c.Start-1, c.End
Expand Down
78 changes: 32 additions & 46 deletions conflict/parse.go
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"log"
"os"
"path"
"strconv"
Expand Down Expand Up @@ -68,41 +67,36 @@ tokenizer:
return err
}

func ReadFile(absPath string) error {
// ReadFile reads all lines of a given file
func ReadFile(absPath string) ([]string, error) {
input, err := os.Open(absPath)
if err != nil {
log.Fatal(err)
return nil, err
}
defer input.Close()

r := bufio.NewReader(input)
lines := []string{}

for {
data, err := r.ReadBytes('\n')
if err == nil || err == io.EOF {
FileLines[absPath] = append(FileLines[absPath], string(data))
// gocui currently doesn't support printing \r
line := strings.Replace(string(data), "\r", "", -1)
lines = append(lines, line)
}

if err != nil {
if err != io.EOF {
return err
return nil, err
}
break
}
}
return nil
}

func (c *Conflict) ExtractLines() error {
lines := FileLines[c.AbsolutePath]
c.LocalLines = lines[c.Start : c.Middle-1]
c.IncomingLines = lines[c.Middle : c.End-1]
c.CurrentName = strings.Split(lines[c.Start-1], " ")[1]
c.ForeignName = strings.Split(lines[c.End-1], " ")[1]
return nil
return lines, nil
}

func parseRawOutput(diff string, dict map[string][]int) error {
func parseGitMarkerInfo(diff string, dict map[string][]int) error {
parts := strings.Split(diff, ":")

if len(parts) < 3 || !strings.Contains(diff, "marker") {
Expand All @@ -118,29 +112,20 @@ func parseRawOutput(diff string, dict map[string][]int) error {
return nil
}

func New(absPath string, lines []int) ([]Conflict, error) {
// Check for diff3 output before parsing
for _, line := range FileLines[absPath] {
if strings.Contains(line, "||||||| merged common ancestors") {
return nil, errors.New(`fac does not support diff3 styled outputs yet 😞
Run below command to change to a compatible conflict style
>> git config --global merge.conflictstyle merge`)
}
}
func newConflicts(absPath string, fname string, lines []int) ([]Conflict, error) {
parsedConflicts := []Conflict{}

if len(lines)%3 != 0 {
return nil, errors.New("Invalid number of remaining conflict markers")
}

parsedConflicts := []Conflict{}
for i := 0; i < len(lines); i += 3 {
conf := Conflict{}
conf := Conflict{}
for i := 0; i < len(lines); i++ {
conf.Start = lines[i]
conf.Middle = lines[i+1]
conf.End = lines[i+2]
conf.AbsolutePath = absPath
_, conf.FileName = path.Split(absPath)
conf.FileName = fname
parsedConflicts = append(parsedConflicts, conf)
}

Expand All @@ -151,55 +136,56 @@ Run below command to change to a compatible conflict style
// If there are no conflicts, it returns a `ErrNoConflict`
// If there are conflicts, it parses the corresponding files
func Find(cwd string) ([]Conflict, error) {
conflicts := []Conflict{}
allConflicts := []Conflict{}

topPath, ok := TopLevelPath(cwd)
if ok != nil {
return nil, ok
}

markerLocations, ok := MarkerLocations(cwd)
markerLocations, ok := MarkerLocations(topPath)
if ok != nil {
return nil, ok
}

diffMap := make(map[string][]int)
markerLocMap := make(map[string][]int)
FileLines = make(map[string][]string)

for _, line := range markerLocations {
if len(line) == 0 {
continue
}

if err := parseRawOutput(line, diffMap); err != nil {
if err := parseGitMarkerInfo(line, markerLocMap); err != nil {
return nil, err
}
}

for fname := range diffMap {
for fname := range markerLocMap {
absPath := path.Join(topPath, fname)
if err := ReadFile(absPath); err != nil {

lines, err := ReadFile(absPath)
if err != nil {
return nil, err
}
if newConflicts, err := New(absPath, diffMap[fname]); err == nil {
conflicts = append(conflicts, newConflicts...)
FileLines[absPath] = append(FileLines[absPath], lines...)

if conflicts, err := newConflicts(absPath, fname, markerLocMap[fname]); err == nil {
allConflicts = append(allConflicts, conflicts...)
} else {
return nil, err
}
}

if len(conflicts) == 0 {
return nil, NewErrNoConflict("No conflicts detected 🎉")
}

for i := range conflicts {
if err := conflicts[i].ExtractLines(); err != nil {
for i := range allConflicts {
fileLines := FileLines[allConflicts[i].AbsolutePath]
if err := allConflicts[i].Extract(fileLines); err != nil {
return nil, err
}
if err := conflicts[i].HighlightSyntax(); err != nil {
if err := allConflicts[i].HighlightSyntax(); err != nil {
return nil, err
}
}

return conflicts, nil
return allConflicts, nil
}
15 changes: 5 additions & 10 deletions layout.go
Expand Up @@ -58,7 +58,6 @@ func makePanels(g *gocui.Gui) error {
y0, y1 = 0, viewHeight
x2, x3 = branchViewWidth, branchViewWidth*2
y2, y3 = 0, viewHeight

} else {
branchViewWidth = branchViewWidth * 2
viewHeight = (maxY - inputHeight) / 2
Expand Down Expand Up @@ -108,7 +107,7 @@ func makePrompt(g *gocui.Gui) error {
inputHeight := 2
viewHeight := maxY - inputHeight

// Instruction View
// Input instruction view
if v, err := g.SetView(Prompt, 0, viewHeight, 19, viewHeight+inputHeight); err != nil {
if err != gocui.ErrUnknownView {
return err
Expand All @@ -119,7 +118,7 @@ func makePrompt(g *gocui.Gui) error {
v.MoveCursor(11, 0, true)
}

// Input View
// Input view
if v, err := g.SetView(Input, 15, viewHeight, maxX, viewHeight+inputHeight); err != nil {
if err != gocui.ErrUnknownView {
return err
Expand All @@ -136,6 +135,7 @@ func makePrompt(g *gocui.Gui) error {
}

func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
// Update side panel
g.Update(func(g *gocui.Gui) error {
v, err := g.View(Panel)
if err != nil {
Expand Down Expand Up @@ -164,6 +164,7 @@ func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
return nil
})

// Update code view
g.Update(func(g *gocui.Gui) error {
v, err := g.View(Current)
if err != nil {
Expand All @@ -174,13 +175,7 @@ func Select(c *conflict.Conflict, g *gocui.Gui, showHelp bool) error {
top, bottom := c.PaddingLines()
v.Clear()
printLines(v, top)

// TODO: Diff display is not implemented yet
if c.DisplayDiff {
printLines(v, c.Diff())
} else {
printLines(v, c.ColoredLocalLines)
}
printLines(v, c.ColoredLocalLines)
printLines(v, bottom)
if c.Choice == Local {
v.FgColor = gocui.ColorGreen
Expand Down
19 changes: 9 additions & 10 deletions main.go
Expand Up @@ -3,7 +3,6 @@ package main
import (
"fmt"
"log"
"os"
"strings"

"github.com/jroimartin/gocui"
Expand All @@ -13,9 +12,9 @@ import (

var (
cur = 0
consecutiveError = 0
all = []conflict.Conflict{}
numConflicts = 0
consecutiveError = 0
)

func printLines(v *gocui.View, lines []string) {
Expand Down Expand Up @@ -89,15 +88,15 @@ func parseInput(g *gocui.Gui, v *gocui.View) error {
}

func main() {
cwd, _ := os.Getwd()
//cwd, _ := os.Getwd()
cwd := "./test"
conflicts, err := conflict.Find(cwd)
if err != nil {
switch err.(type) {
case *conflict.ErrNoConflict:
fmt.Println(color.Green(color.Regular, err.Error()))
default:
fmt.Println(color.Red(color.Regular, err.Error()))
}
fmt.Println(color.Red(color.Regular, err.Error()))
return
}
if len(conflicts) == 0 {
fmt.Println(color.Green(color.Regular, "No conflicts detected 🎉"))
return
}

Expand All @@ -115,7 +114,7 @@ func main() {
log.Panic(err)
}
if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, parseInput); err != nil {
log.Panic(err)
log.Panicln(err)
}

Select(&all[0], g, false)
Expand Down
1 change: 1 addition & 0 deletions summary.go
Expand Up @@ -74,6 +74,7 @@ func writeChanges(absPath string) (err error) {
return
}

// FinalizeChanges writes the changes the user selected
func FinalizeChanges(absPath string) (err error) {
targetConflicts := conflict.In(all, absPath)

Expand Down

0 comments on commit c1dd493

Please sign in to comment.