1package types
2
3import (
4 "github.com/bluekeyes/go-gitdiff/gitdiff"
5)
6
7type SplitLine struct {
8 LineNumber int `json:"line_number,omitempty"`
9 Content string `json:"content"`
10 Op gitdiff.LineOp `json:"op"`
11 IsEmpty bool `json:"is_empty"`
12}
13
14type SplitFragment struct {
15 Header string `json:"header"`
16 LeftLines []SplitLine `json:"left_lines"`
17 RightLines []SplitLine `json:"right_lines"`
18}
19
20type SplitDiff struct {
21 Name string `json:"name"`
22 TextFragments []SplitFragment `json:"fragments"`
23}
24
25func (d SplitDiff) Id() string {
26 return d.Name
27}
28
29// separate lines into left and right, this includes additional logic to
30// group consecutive runs of additions and deletions in order to align them
31// properly in the final output
32//
33// TODO: move all diff stuff to a single package, we are spread across patchutil and types right now
34func SeparateLines(fragment *gitdiff.TextFragment) ([]SplitLine, []SplitLine) {
35 lines := fragment.Lines
36 var leftLines, rightLines []SplitLine
37 oldLineNum := fragment.OldPosition
38 newLineNum := fragment.NewPosition
39
40 // process deletions and additions in groups for better alignment
41 i := 0
42 for i < len(lines) {
43 line := lines[i]
44
45 switch line.Op {
46 case gitdiff.OpContext:
47 leftLines = append(leftLines, SplitLine{
48 LineNumber: int(oldLineNum),
49 Content: line.Line,
50 Op: gitdiff.OpContext,
51 IsEmpty: false,
52 })
53 rightLines = append(rightLines, SplitLine{
54 LineNumber: int(newLineNum),
55 Content: line.Line,
56 Op: gitdiff.OpContext,
57 IsEmpty: false,
58 })
59 oldLineNum++
60 newLineNum++
61 i++
62
63 case gitdiff.OpDelete:
64 deletionCount := 0
65 for j := i; j < len(lines) && lines[j].Op == gitdiff.OpDelete; j++ {
66 leftLines = append(leftLines, SplitLine{
67 LineNumber: int(oldLineNum),
68 Content: lines[j].Line,
69 Op: gitdiff.OpDelete,
70 IsEmpty: false,
71 })
72 oldLineNum++
73 deletionCount++
74 }
75 i += deletionCount
76
77 additionCount := 0
78 for j := i; j < len(lines) && lines[j].Op == gitdiff.OpAdd; j++ {
79 rightLines = append(rightLines, SplitLine{
80 LineNumber: int(newLineNum),
81 Content: lines[j].Line,
82 Op: gitdiff.OpAdd,
83 IsEmpty: false,
84 })
85 newLineNum++
86 additionCount++
87 }
88 i += additionCount
89
90 // add empty lines to balance the sides
91 if deletionCount > additionCount {
92 // more deletions than additions - pad right side
93 for k := 0; k < deletionCount-additionCount; k++ {
94 rightLines = append(rightLines, SplitLine{
95 Content: "",
96 Op: gitdiff.OpContext,
97 IsEmpty: true,
98 })
99 }
100 } else if additionCount > deletionCount {
101 // more additions than deletions - pad left side
102 for k := 0; k < additionCount-deletionCount; k++ {
103 leftLines = append(leftLines, SplitLine{
104 Content: "",
105 Op: gitdiff.OpContext,
106 IsEmpty: true,
107 })
108 }
109 }
110
111 case gitdiff.OpAdd:
112 // standalone addition (not preceded by deletion)
113 leftLines = append(leftLines, SplitLine{
114 Content: "",
115 Op: gitdiff.OpContext,
116 IsEmpty: true,
117 })
118 rightLines = append(rightLines, SplitLine{
119 LineNumber: int(newLineNum),
120 Content: line.Line,
121 Op: gitdiff.OpAdd,
122 IsEmpty: false,
123 })
124 newLineNum++
125 i++
126 }
127 }
128
129 return leftLines, rightLines
130}