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
25// used by html elements as a unique ID for hrefs
26func (d *SplitDiff) Id() string {
27 return d.Name
28}
29
30// separate lines into left and right, this includes additional logic to
31// group consecutive runs of additions and deletions in order to align them
32// properly in the final output
33//
34// TODO: move all diff stuff to a single package, we are spread across patchutil and types right now
35func SeparateLines(fragment *gitdiff.TextFragment) ([]SplitLine, []SplitLine) {
36 lines := fragment.Lines
37 var leftLines, rightLines []SplitLine
38 oldLineNum := fragment.OldPosition
39 newLineNum := fragment.NewPosition
40
41 // process deletions and additions in groups for better alignment
42 i := 0
43 for i < len(lines) {
44 line := lines[i]
45
46 switch line.Op {
47 case gitdiff.OpContext:
48 leftLines = append(leftLines, SplitLine{
49 LineNumber: int(oldLineNum),
50 Content: line.Line,
51 Op: gitdiff.OpContext,
52 IsEmpty: false,
53 })
54 rightLines = append(rightLines, SplitLine{
55 LineNumber: int(newLineNum),
56 Content: line.Line,
57 Op: gitdiff.OpContext,
58 IsEmpty: false,
59 })
60 oldLineNum++
61 newLineNum++
62 i++
63
64 case gitdiff.OpDelete:
65 deletionCount := 0
66 for j := i; j < len(lines) && lines[j].Op == gitdiff.OpDelete; j++ {
67 leftLines = append(leftLines, SplitLine{
68 LineNumber: int(oldLineNum),
69 Content: lines[j].Line,
70 Op: gitdiff.OpDelete,
71 IsEmpty: false,
72 })
73 oldLineNum++
74 deletionCount++
75 }
76 i += deletionCount
77
78 additionCount := 0
79 for j := i; j < len(lines) && lines[j].Op == gitdiff.OpAdd; j++ {
80 rightLines = append(rightLines, SplitLine{
81 LineNumber: int(newLineNum),
82 Content: lines[j].Line,
83 Op: gitdiff.OpAdd,
84 IsEmpty: false,
85 })
86 newLineNum++
87 additionCount++
88 }
89 i += additionCount
90
91 // add empty lines to balance the sides
92 if deletionCount > additionCount {
93 // more deletions than additions - pad right side
94 for k := 0; k < deletionCount-additionCount; k++ {
95 rightLines = append(rightLines, SplitLine{
96 Content: "",
97 Op: gitdiff.OpContext,
98 IsEmpty: true,
99 })
100 }
101 } else if additionCount > deletionCount {
102 // more additions than deletions - pad left side
103 for k := 0; k < additionCount-deletionCount; k++ {
104 leftLines = append(leftLines, SplitLine{
105 Content: "",
106 Op: gitdiff.OpContext,
107 IsEmpty: true,
108 })
109 }
110 }
111
112 case gitdiff.OpAdd:
113 // standalone addition (not preceded by deletion)
114 leftLines = append(leftLines, SplitLine{
115 Content: "",
116 Op: gitdiff.OpContext,
117 IsEmpty: true,
118 })
119 rightLines = append(rightLines, SplitLine{
120 LineNumber: int(newLineNum),
121 Content: line.Line,
122 Op: gitdiff.OpAdd,
123 IsEmpty: false,
124 })
125 newLineNum++
126 i++
127 }
128 }
129
130 return leftLines, rightLines
131}