Monorepo for Tangled tangled.org

patchutil,types: implement DiffRenderer interface for all diffs #964

merged opened by oppi.li targeting master from op/vyrymqtwolsn

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.

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:qfpnj4og54vl56wngdriaxug/sh.tangled.repo.pull/3mcjiyey6id22
+170 -54
Diff #1
+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
··· 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
··· 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
··· 4 "errors" 5 "reflect" 6 "testing" 7 ) 8 9 func TestIsPatchValid(t *testing.T) { ··· 323 }) 324 } 325 }
··· 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
··· 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
··· 1 package types 2 3 - import "testing" 4 5 func TestDiffId(t *testing.T) { 6 tests := []struct { ··· 105 } 106 107 for i, diff := range nd.Diff { 108 - if changedFiles[i] != diff.Id() { 109 t.Errorf("ChangedFiles()[%d] = %q, but Diff.Id() = %q", i, changedFiles[i], diff.Id()) 110 } 111 } 112 }
··· 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
··· 22 TextFragments []SplitFragment `json:"fragments"` 23 } 24 25 - // used by html elements as a unique ID for hrefs 26 - func (d *SplitDiff) Id() string { 27 return d.Name 28 } 29
··· 22 TextFragments []SplitFragment `json:"fragments"` 23 } 24 25 + func (d SplitDiff) Id() string { 26 return d.Name 27 } 28

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
patchutil,types: implement DiffRenderer interface for all diffs
3/3 success
expand
expand 0 comments
pull request successfully merged
oppi.li submitted #0
1 commit
expand
patchutil,types: implement DiffRenderer interface for all diffs
3/3 success
expand
expand 0 comments