···876876 Round int877877 Interdiff *patchutil.InterdiffResult878878 OrderedReactionKinds []db.ReactionKind879879+ DiffOpts types.DiffOpts879880}880881881882// this name is a mouthful
···414414 return415415 }416416417417+ var diffOpts types.DiffOpts418418+ if d := r.URL.Query().Get("diff"); d == "split" {419419+ diffOpts.Split = true420420+ }421421+417422 pull, ok := r.Context().Value("pull").(*db.Pull)418423 if !ok {419424 log.Println("failed to get pull")···467462468463 interdiff := patchutil.Interdiff(previousPatch, currentPatch)469464465465+ for _, f := range interdiff.Files {466466+ log.Println("", "", f.Split())467467+ }468468+470469 s.pages.RepoPullInterdiffPage(w, pages.RepoPullInterdiffParams{471470 LoggedInUser: s.oauth.GetUser(r),472471 RepoInfo: f.RepoInfo(user),···478469 Round: roundIdInt,479470 DidHandleMap: didHandleMap,480471 Interdiff: interdiff,472472+ DiffOpts: diffOpts,481473 })482482- return483474}484475485476func (s *Pulls) RepoPullPatchRaw(w http.ResponseWriter, r *http.Request) {
+25
patchutil/interdiff.go
···55 "strings"6677 "github.com/bluekeyes/go-gitdiff/gitdiff"88+ "tangled.sh/tangled.sh/core/types"89)9101011type InterdiffResult struct {···3433 *gitdiff.File3534 Name string3635 Status InterdiffFileStatus3636+}3737+3838+func (s *InterdiffFile) Split() *types.SplitDiff {3939+ fragments := make([]types.SplitFragment, len(s.TextFragments))4040+4141+ for i, fragment := range s.TextFragments {4242+ leftLines, rightLines := types.SeparateLines(fragment)4343+4444+ fragments[i] = types.SplitFragment{4545+ Header: fragment.Header(),4646+ LeftLines: leftLines,4747+ RightLines: rightLines,4848+ }4949+ }5050+5151+ return &types.SplitDiff{5252+ Name: s.Id(),5353+ TextFragments: fragments,5454+ }5555+}5656+5757+// used by html elements as a unique ID for hrefs5858+func (s *InterdiffFile) Id() string {5959+ return s.Name3760}38613962func (s *InterdiffFile) String() string {
+131
types/split.go
···11+package types22+33+import (44+ "github.com/bluekeyes/go-gitdiff/gitdiff"55+)66+77+type SplitLine struct {88+ LineNumber int `json:"line_number,omitempty"`99+ Content string `json:"content"`1010+ Op gitdiff.LineOp `json:"op"`1111+ IsEmpty bool `json:"is_empty"`1212+}1313+1414+type SplitFragment struct {1515+ Header string `json:"header"`1616+ LeftLines []SplitLine `json:"left_lines"`1717+ RightLines []SplitLine `json:"right_lines"`1818+}1919+2020+type SplitDiff struct {2121+ Name string `json:"name"`2222+ TextFragments []SplitFragment `json:"fragments"`2323+}2424+2525+// used by html elements as a unique ID for hrefs2626+func (d *SplitDiff) Id() string {2727+ return d.Name2828+}2929+3030+// separate lines into left and right, this includes additional logic to3131+// group consecutive runs of additions and deletions in order to align them3232+// properly in the final output3333+//3434+// TODO: move all diff stuff to a single package, we are spread across patchutil and types right now3535+func SeparateLines(fragment *gitdiff.TextFragment) ([]SplitLine, []SplitLine) {3636+ lines := fragment.Lines3737+ var leftLines, rightLines []SplitLine3838+ oldLineNum := fragment.OldPosition3939+ newLineNum := fragment.OldPosition4040+4141+ // process deletions and additions in groups for better alignment4242+ i := 04343+ for i < len(lines) {4444+ line := lines[i]4545+4646+ switch line.Op {4747+ case gitdiff.OpContext:4848+ leftLines = append(leftLines, SplitLine{4949+ LineNumber: int(oldLineNum),5050+ Content: line.Line,5151+ Op: gitdiff.OpContext,5252+ IsEmpty: false,5353+ })5454+ rightLines = append(rightLines, SplitLine{5555+ LineNumber: int(newLineNum),5656+ Content: line.Line,5757+ Op: gitdiff.OpContext,5858+ IsEmpty: false,5959+ })6060+ oldLineNum++6161+ newLineNum++6262+ i++6363+6464+ case gitdiff.OpDelete:6565+ deletionCount := 06666+ for j := i; j < len(lines) && lines[j].Op == gitdiff.OpDelete; j++ {6767+ leftLines = append(leftLines, SplitLine{6868+ LineNumber: int(oldLineNum),6969+ Content: lines[j].Line,7070+ Op: gitdiff.OpDelete,7171+ IsEmpty: false,7272+ })7373+ oldLineNum++7474+ deletionCount++7575+ }7676+ i += deletionCount7777+7878+ additionCount := 07979+ for j := i; j < len(lines) && lines[j].Op == gitdiff.OpAdd; j++ {8080+ rightLines = append(rightLines, SplitLine{8181+ LineNumber: int(newLineNum),8282+ Content: lines[j].Line,8383+ Op: gitdiff.OpAdd,8484+ IsEmpty: false,8585+ })8686+ newLineNum++8787+ additionCount++8888+ }8989+ i += additionCount9090+9191+ // add empty lines to balance the sides9292+ if deletionCount > additionCount {9393+ // more deletions than additions - pad right side9494+ for k := 0; k < deletionCount-additionCount; k++ {9595+ rightLines = append(rightLines, SplitLine{9696+ Content: "",9797+ Op: gitdiff.OpContext,9898+ IsEmpty: true,9999+ })100100+ }101101+ } else if additionCount > deletionCount {102102+ // more additions than deletions - pad left side103103+ for k := 0; k < additionCount-deletionCount; k++ {104104+ leftLines = append(leftLines, SplitLine{105105+ Content: "",106106+ Op: gitdiff.OpContext,107107+ IsEmpty: true,108108+ })109109+ }110110+ }111111+112112+ case gitdiff.OpAdd:113113+ // standalone addition (not preceded by deletion)114114+ leftLines = append(leftLines, SplitLine{115115+ Content: "",116116+ Op: gitdiff.OpContext,117117+ IsEmpty: true,118118+ })119119+ rightLines = append(rightLines, SplitLine{120120+ LineNumber: int(newLineNum),121121+ Content: line.Line,122122+ Op: gitdiff.OpAdd,123123+ IsEmpty: false,124124+ })125125+ newLineNum++126126+ i++127127+ }128128+ }129129+130130+ return leftLines, rightLines131131+}