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 18 "tangled.org/core/types" 19 19 ) 20 20 21 - func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commentCount int, diffStats types.DiffStat, filesChanged int) (*ogcard.Card, error) { 21 + func (s *Pulls) drawPullSummaryCard(pull *models.Pull, repo *models.Repo, commentCount int, diffStats types.DiffFileStat, filesChanged int) (*ogcard.Card, error) { 22 22 width, height := ogcard.DefaultSize() 23 23 mainCard, err := ogcard.NewCard(width, height) 24 24 if err != nil { ··· 284 284 commentCount := len(comments) 285 285 286 286 // Calculate diff stats from latest submission using patchutil 287 - var diffStats types.DiffStat 287 + var diffStats types.DiffFileStat 288 288 filesChanged := 0 289 289 if len(pull.Submissions) > 0 { 290 290 latestSubmission := pull.Submissions[len(pull.Submissions)-1]
+3 -8
knotserver/git/diff.go
··· 64 64 65 65 for _, tf := range d.TextFragments { 66 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 - } 67 + nd.Stat.Insertions += tf.LinesAdded 68 + nd.Stat.Deletions += tf.LinesDeleted 75 69 } 76 70 77 71 nd.Diff = append(nd.Diff, ndiff) 78 72 } 79 73 74 + nd.Stat.FilesChanged += len(diffs) 80 75 nd.Commit.FromGoGitCommit(c) 81 76 82 77 return &nd, nil
+66 -10
patchutil/interdiff.go
··· 5 5 "strings" 6 6 7 7 "github.com/bluekeyes/go-gitdiff/gitdiff" 8 + "tangled.org/core/appview/filetree" 8 9 "tangled.org/core/types" 9 10 ) 10 11 ··· 12 13 Files []*InterdiffFile 13 14 } 14 15 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) 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), 19 27 } 20 - return files 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) 21 44 } 22 45 23 46 func (i *InterdiffResult) String() string { ··· 36 59 Status InterdiffFileStatus 37 60 } 38 61 39 - func (s *InterdiffFile) Split() *types.SplitDiff { 62 + func (s *InterdiffFile) Id() string { 63 + return s.Name 64 + } 65 + 66 + func (s *InterdiffFile) Split() types.SplitDiff { 40 67 fragments := make([]types.SplitFragment, len(s.TextFragments)) 41 68 42 69 for i, fragment := range s.TextFragments { ··· 49 76 } 50 77 } 51 78 52 - return &types.SplitDiff{ 79 + return types.SplitDiff{ 53 80 Name: s.Id(), 54 81 TextFragments: fragments, 55 82 } 56 83 } 57 84 58 - // used by html elements as a unique ID for hrefs 59 - func (s *InterdiffFile) Id() string { 60 - return s.Name 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 + } 61 117 } 62 118 63 119 func (s *InterdiffFile) String() string {
+9
patchutil/patchutil_test.go
··· 4 4 "errors" 5 5 "reflect" 6 6 "testing" 7 + 8 + "tangled.org/core/types" 7 9 ) 8 10 9 11 func TestIsPatchValid(t *testing.T) { ··· 323 325 }) 324 326 } 325 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 1 package types 2 2 3 3 import ( 4 + "net/url" 5 + 4 6 "github.com/bluekeyes/go-gitdiff/gitdiff" 7 + "tangled.org/core/appview/filetree" 5 8 ) 6 9 7 10 type DiffOpts struct { 8 11 Split bool `json:"split"` 9 12 } 10 13 11 - type TextFragment struct { 12 - Header string `json:"comment"` 13 - Lines []gitdiff.Line `json:"lines"` 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"` 14 29 } 15 30 16 31 type Diff struct { ··· 26 41 IsRename bool `json:"is_rename"` 27 42 } 28 43 29 - type DiffStat struct { 30 - Insertions int64 31 - Deletions int64 32 - } 33 - 34 - func (d *Diff) Stats() DiffStat { 35 - var stats DiffStat 44 + func (d Diff) Stats() DiffFileStat { 45 + var stats DiffFileStat 36 46 for _, f := range d.TextFragments { 37 47 stats.Insertions += f.LinesAdded 38 48 stats.Deletions += f.LinesDeleted ··· 40 50 return stats 41 51 } 42 52 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"` 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 52 62 } 53 63 54 64 type DiffTree struct { ··· 58 68 Diff []*gitdiff.File `json:"diff"` 59 69 } 60 70 61 - func (d *NiceDiff) ChangedFiles() []string { 62 - files := make([]string, len(d.Diff)) 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 + } 63 83 64 - for i, f := range d.Diff { 65 - if f.IsDelete { 66 - files[i] = f.Name.Old 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 67 90 } else { 68 - files[i] = f.Name.New 91 + fs[i] = n.New 69 92 } 70 93 } 94 + return filetree.FileTree(fs) 95 + } 71 96 72 - return files 97 + func (d NiceDiff) Stats() DiffStat { 98 + return d.Stat 73 99 } 74 100 75 - // used by html elements as a unique ID for hrefs 76 - func (d *Diff) Id() string { 101 + func (d Diff) Id() string { 77 102 if d.IsDelete { 78 103 return d.Name.Old 79 104 } 80 105 return d.Name.New 81 106 } 82 107 83 - func (d *Diff) Split() *SplitDiff { 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 { 84 132 fragments := make([]SplitFragment, len(d.TextFragments)) 85 133 for i, fragment := range d.TextFragments { 86 134 leftLines, rightLines := SeparateLines(&fragment) ··· 91 139 } 92 140 } 93 141 94 - return &SplitDiff{ 142 + return SplitDiff{ 95 143 Name: d.Id(), 96 144 TextFragments: fragments, 97 145 }
+11 -2
types/diff_test.go
··· 1 1 package types 2 2 3 - import "testing" 3 + import ( 4 + "testing" 5 + ) 4 6 5 7 func TestDiffId(t *testing.T) { 6 8 tests := []struct { ··· 105 107 } 106 108 107 109 for i, diff := range nd.Diff { 108 - if changedFiles[i] != diff.Id() { 110 + if changedFiles[i].Id() != diff.Id() { 109 111 t.Errorf("ChangedFiles()[%d] = %q, but Diff.Id() = %q", i, changedFiles[i], diff.Id()) 110 112 } 111 113 } 112 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 22 TextFragments []SplitFragment `json:"fragments"` 23 23 } 24 24 25 - // used by html elements as a unique ID for hrefs 26 - func (d *SplitDiff) Id() string { 25 + func (d SplitDiff) Id() string { 27 26 return d.Name 28 27 } 29 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