types.NiceDiff and patchutil.Interdiff now implement the new interface. this allows us to remove the differing rendering logic necessary to present each kind of diff.
+2
-2
appview/pulls/opengraph.go
+2
-2
appview/pulls/opengraph.go
···
18
"tangled.org/core/types"
19
)
20
21
-
func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commentCount int, diffStats types.DiffStat, filesChanged int) (*ogcard.Card, error) {
22
width, height := ogcard.DefaultSize()
23
mainCard, err := ogcard.NewCard(width, height)
24
if err != nil {
···
284
commentCount := len(comments)
285
286
// Calculate diff stats from latest submission using patchutil
287
-
var diffStats types.DiffStat
288
filesChanged := 0
289
if len(pull.Submissions) > 0 {
290
latestSubmission := pull.Submissions[len(pull.Submissions)-1]
···
18
"tangled.org/core/types"
19
)
20
21
+
func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commentCount int, diffStats types.DiffFileStat, filesChanged int) (*ogcard.Card, error) {
22
width, height := ogcard.DefaultSize()
23
mainCard, err := ogcard.NewCard(width, height)
24
if err != nil {
···
284
commentCount := len(comments)
285
286
// Calculate diff stats from latest submission using patchutil
287
+
var diffStats types.DiffFileStat
288
filesChanged := 0
289
if len(pull.Submissions) > 0 {
290
latestSubmission := pull.Submissions[len(pull.Submissions)-1]
+3
-8
knotserver/git/diff.go
+3
-8
knotserver/git/diff.go
···
64
65
for _, tf := range d.TextFragments {
66
ndiff.TextFragments = append(ndiff.TextFragments, *tf)
67
-
for _, l := range tf.Lines {
68
-
switch l.Op {
69
-
case gitdiff.OpAdd:
70
-
nd.Stat.Insertions += 1
71
-
case gitdiff.OpDelete:
72
-
nd.Stat.Deletions += 1
73
-
}
74
-
}
75
}
76
77
nd.Diff = append(nd.Diff, ndiff)
78
}
79
80
nd.Commit.FromGoGitCommit(c)
81
82
return &nd, nil
···
64
65
for _, tf := range d.TextFragments {
66
ndiff.TextFragments = append(ndiff.TextFragments, *tf)
67
+
nd.Stat.Insertions += tf.LinesAdded
68
+
nd.Stat.Deletions += tf.LinesDeleted
69
}
70
71
nd.Diff = append(nd.Diff, ndiff)
72
}
73
74
+
nd.Stat.FilesChanged += len(diffs)
75
nd.Commit.FromGoGitCommit(c)
76
77
return &nd, nil
+66
-10
patchutil/interdiff.go
+66
-10
patchutil/interdiff.go
···
5
"strings"
6
7
"github.com/bluekeyes/go-gitdiff/gitdiff"
8
"tangled.org/core/types"
9
)
10
···
12
Files []*InterdiffFile
13
}
14
15
-
func (i *InterdiffResult) AffectedFiles() []string {
16
-
files := make([]string, len(i.Files))
17
-
for _, f := range i.Files {
18
-
files = append(files, f.Name)
19
}
20
-
return files
21
}
22
23
func (i *InterdiffResult) String() string {
···
36
Status InterdiffFileStatus
37
}
38
39
-
func (s *InterdiffFile) Split() *types.SplitDiff {
40
fragments := make([]types.SplitFragment, len(s.TextFragments))
41
42
for i, fragment := range s.TextFragments {
···
49
}
50
}
51
52
-
return &types.SplitDiff{
53
Name: s.Id(),
54
TextFragments: fragments,
55
}
56
}
57
58
-
// used by html elements as a unique ID for hrefs
59
-
func (s *InterdiffFile) Id() string {
60
-
return s.Name
61
}
62
63
func (s *InterdiffFile) String() string {
···
5
"strings"
6
7
"github.com/bluekeyes/go-gitdiff/gitdiff"
8
+
"tangled.org/core/appview/filetree"
9
"tangled.org/core/types"
10
)
11
···
13
Files []*InterdiffFile
14
}
15
16
+
func (i *InterdiffResult) Stats() types.DiffStat {
17
+
var ins, del int64
18
+
for _, s := range i.ChangedFiles() {
19
+
stat := s.Stats()
20
+
ins += stat.Insertions
21
+
del += stat.Deletions
22
+
}
23
+
return types.DiffStat{
24
+
Insertions: ins,
25
+
Deletions: del,
26
+
FilesChanged: len(i.Files),
27
}
28
+
}
29
+
30
+
func (i *InterdiffResult) ChangedFiles() []types.DiffFileRenderer {
31
+
drs := make([]types.DiffFileRenderer, len(i.Files))
32
+
for i, s := range i.Files {
33
+
drs[i] = s
34
+
}
35
+
return drs
36
+
}
37
+
38
+
func (i *InterdiffResult) FileTree() *filetree.FileTreeNode {
39
+
fs := make([]string, len(i.Files))
40
+
for i, s := range i.Files {
41
+
fs[i] = s.Name
42
+
}
43
+
return filetree.FileTree(fs)
44
}
45
46
func (i *InterdiffResult) String() string {
···
59
Status InterdiffFileStatus
60
}
61
62
+
func (s *InterdiffFile) Id() string {
63
+
return s.Name
64
+
}
65
+
66
+
func (s *InterdiffFile) Split() types.SplitDiff {
67
fragments := make([]types.SplitFragment, len(s.TextFragments))
68
69
for i, fragment := range s.TextFragments {
···
76
}
77
}
78
79
+
return types.SplitDiff{
80
Name: s.Id(),
81
TextFragments: fragments,
82
}
83
}
84
85
+
func (s *InterdiffFile) CanRender() string {
86
+
if s.Status.IsUnchanged() {
87
+
return "This file has not been changed."
88
+
} else if s.Status.IsRebased() {
89
+
return "This patch was likely rebased, as context lines do not match."
90
+
} else if s.Status.IsError() {
91
+
return "Failed to calculate interdiff for this file."
92
+
} else {
93
+
return ""
94
+
}
95
+
}
96
+
97
+
func (s *InterdiffFile) Names() types.DiffFileName {
98
+
var n types.DiffFileName
99
+
n.New = s.Name
100
+
return n
101
+
}
102
+
103
+
func (s *InterdiffFile) Stats() types.DiffFileStat {
104
+
var ins, del int64
105
+
106
+
if s.File != nil {
107
+
for _, f := range s.TextFragments {
108
+
ins += f.LinesAdded
109
+
del += f.LinesDeleted
110
+
}
111
+
}
112
+
113
+
return types.DiffFileStat{
114
+
Insertions: ins,
115
+
Deletions: del,
116
+
}
117
}
118
119
func (s *InterdiffFile) String() string {
+9
patchutil/patchutil_test.go
+9
patchutil/patchutil_test.go
···
4
"errors"
5
"reflect"
6
"testing"
7
+
8
+
"tangled.org/core/types"
9
)
10
11
func TestIsPatchValid(t *testing.T) {
···
325
})
326
}
327
}
328
+
329
+
func TestImplsInterfaces(t *testing.T) {
330
+
id := &InterdiffResult{}
331
+
_ = isDiffsRenderer(id)
332
+
}
333
+
334
+
func isDiffsRenderer[S types.DiffRenderer](S) bool { return true }
+78
-30
types/diff.go
+78
-30
types/diff.go
···
1
package types
2
3
import (
4
"github.com/bluekeyes/go-gitdiff/gitdiff"
5
)
6
7
type DiffOpts struct {
8
Split bool `json:"split"`
9
}
10
11
-
type TextFragment struct {
12
-
Header string `json:"comment"`
13
-
Lines []gitdiff.Line `json:"lines"`
14
}
15
16
type Diff struct {
···
26
IsRename bool `json:"is_rename"`
27
}
28
29
-
type DiffStat struct {
30
-
Insertions int64
31
-
Deletions int64
32
-
}
33
-
34
-
func (d *Diff) Stats() DiffStat {
35
-
var stats DiffStat
36
for _, f := range d.TextFragments {
37
stats.Insertions += f.LinesAdded
38
stats.Deletions += f.LinesDeleted
···
40
return stats
41
}
42
43
-
// A nicer git diff representation.
44
-
type NiceDiff struct {
45
-
Commit Commit `json:"commit"`
46
-
Stat struct {
47
-
FilesChanged int `json:"files_changed"`
48
-
Insertions int `json:"insertions"`
49
-
Deletions int `json:"deletions"`
50
-
} `json:"stat"`
51
-
Diff []Diff `json:"diff"`
52
}
53
54
type DiffTree struct {
···
58
Diff []*gitdiff.File `json:"diff"`
59
}
60
61
-
func (d *NiceDiff) ChangedFiles() []string {
62
-
files := make([]string, len(d.Diff))
63
64
-
for i, f := range d.Diff {
65
-
if f.IsDelete {
66
-
files[i] = f.Name.Old
67
} else {
68
-
files[i] = f.Name.New
69
}
70
}
71
72
-
return files
73
}
74
75
-
// used by html elements as a unique ID for hrefs
76
-
func (d *Diff) Id() string {
77
if d.IsDelete {
78
return d.Name.Old
79
}
80
return d.Name.New
81
}
82
83
-
func (d *Diff) Split() *SplitDiff {
84
fragments := make([]SplitFragment, len(d.TextFragments))
85
for i, fragment := range d.TextFragments {
86
leftLines, rightLines := SeparateLines(&fragment)
···
91
}
92
}
93
94
-
return &SplitDiff{
95
Name: d.Id(),
96
TextFragments: fragments,
97
}
···
1
package types
2
3
import (
4
+
"net/url"
5
+
6
"github.com/bluekeyes/go-gitdiff/gitdiff"
7
+
"tangled.org/core/appview/filetree"
8
)
9
10
type DiffOpts struct {
11
Split bool `json:"split"`
12
}
13
14
+
func (d DiffOpts) Encode() string {
15
+
values := make(url.Values)
16
+
if d.Split {
17
+
values.Set("diff", "split")
18
+
} else {
19
+
values.Set("diff", "unified")
20
+
}
21
+
return values.Encode()
22
+
}
23
+
24
+
// A nicer git diff representation.
25
+
type NiceDiff struct {
26
+
Commit Commit `json:"commit"`
27
+
Stat DiffStat `json:"stat"`
28
+
Diff []Diff `json:"diff"`
29
}
30
31
type Diff struct {
···
41
IsRename bool `json:"is_rename"`
42
}
43
44
+
func (d Diff) Stats() DiffFileStat {
45
+
var stats DiffFileStat
46
for _, f := range d.TextFragments {
47
stats.Insertions += f.LinesAdded
48
stats.Deletions += f.LinesDeleted
···
50
return stats
51
}
52
53
+
type DiffStat struct {
54
+
Insertions int64 `json:"insertions"`
55
+
Deletions int64 `json:"deletions"`
56
+
FilesChanged int `json:"files_changed"`
57
+
}
58
+
59
+
type DiffFileStat struct {
60
+
Insertions int64
61
+
Deletions int64
62
}
63
64
type DiffTree struct {
···
68
Diff []*gitdiff.File `json:"diff"`
69
}
70
71
+
type DiffFileName struct {
72
+
Old string
73
+
New string
74
+
}
75
+
76
+
func (d NiceDiff) ChangedFiles() []DiffFileRenderer {
77
+
drs := make([]DiffFileRenderer, len(d.Diff))
78
+
for i, s := range d.Diff {
79
+
drs[i] = s
80
+
}
81
+
return drs
82
+
}
83
84
+
func (d NiceDiff) FileTree() *filetree.FileTreeNode {
85
+
fs := make([]string, len(d.Diff))
86
+
for i, s := range d.Diff {
87
+
n := s.Names()
88
+
if n.New == "" {
89
+
fs[i] = n.Old
90
} else {
91
+
fs[i] = n.New
92
}
93
}
94
+
return filetree.FileTree(fs)
95
+
}
96
97
+
func (d NiceDiff) Stats() DiffStat {
98
+
return d.Stat
99
}
100
101
+
func (d Diff) Id() string {
102
if d.IsDelete {
103
return d.Name.Old
104
}
105
return d.Name.New
106
}
107
108
+
func (d Diff) Names() DiffFileName {
109
+
var n DiffFileName
110
+
if d.IsDelete {
111
+
n.Old = d.Name.Old
112
+
return n
113
+
} else if d.IsCopy || d.IsRename {
114
+
n.Old = d.Name.Old
115
+
n.New = d.Name.New
116
+
return n
117
+
} else {
118
+
n.New = d.Name.New
119
+
return n
120
+
}
121
+
}
122
+
123
+
func (d Diff) CanRender() string {
124
+
if d.IsBinary {
125
+
return "This is a binary file and will not be displayed."
126
+
}
127
+
128
+
return ""
129
+
}
130
+
131
+
func (d Diff) Split() SplitDiff {
132
fragments := make([]SplitFragment, len(d.TextFragments))
133
for i, fragment := range d.TextFragments {
134
leftLines, rightLines := SeparateLines(&fragment)
···
139
}
140
}
141
142
+
return SplitDiff{
143
Name: d.Id(),
144
TextFragments: fragments,
145
}
+11
-2
types/diff_test.go
+11
-2
types/diff_test.go
···
1
package types
2
3
+
import (
4
+
"testing"
5
+
)
6
7
func TestDiffId(t *testing.T) {
8
tests := []struct {
···
107
}
108
109
for i, diff := range nd.Diff {
110
+
if changedFiles[i].Id() != diff.Id() {
111
t.Errorf("ChangedFiles()[%d] = %q, but Diff.Id() = %q", i, changedFiles[i], diff.Id())
112
}
113
}
114
}
115
+
116
+
func TestImplsInterfaces(t *testing.T) {
117
+
nd := NiceDiff{}
118
+
_ = isDiffsRenderer(nd)
119
+
}
120
+
121
+
func isDiffsRenderer[S DiffRenderer](S) bool { return true }
+1
-2
types/split.go
+1
-2
types/split.go
History
2 rounds
0 comments
1 commit
expand
collapse
patchutil,types: implement DiffRenderer interface for all diffs
types.NiceDiff and patchutil.Interdiff now implement the new interface.
this allows us to remove the differing rendering logic necessary to
present each kind of diff.
3/3 success
expand
collapse
expand 0 comments
pull request successfully merged
1 commit
expand
collapse
patchutil,types: implement DiffRenderer interface for all diffs
types.NiceDiff and patchutil.Interdiff now implement the new interface.
this allows us to remove the differing rendering logic necessary to
present each kind of diff.