Monorepo for Tangled
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139package pulls
import ( "context" "errors" "fmt" "html/template" "io" "log/slog" "net/http" "strings"
"github.com/bluesky-social/indigo/atproto/syntax" "golang.org/x/sync/errgroup" "tangled.org/core/appview/pages" gitmirrorv1 "tangled.org/core/gitmirror/proto/gen")
// htmx fragment. render diff between commitsfunc (s *Pulls) PullDiffFragment(w http.ResponseWriter, r *http.Request) { l := s.logger.With("handler", "PullDiffFragment") ctx := r.Context()
var ( repoRaw = r.URL.Query().Get("repo") base = r.URL.Query().Get("base") // base commit ID head = r.URL.Query().Get("head") // head commit ID unified = r.URL.Query().Get("view") == "unified" ) repo, err := syntax.ParseDID(repoRaw) if err != nil { http.Error(w, "invalid repo DID", http.StatusBadRequest) return } l.Debug("diff fragment", "base", base, "head", head)
var params pages.PullDiffFragmentParams params.Repo = repo params.DiffBase = base params.DiffHead = head params.DiffUrl = r.URL.Path params.Unified = unified params.Files, params.ErrorMsg = s.diffFragmentParams(ctx, l, repo, base, head, unified) if err := s.pages.PullDiffFragment(w, params); err != nil { l.Error("failed to render", "err", err) }}
func (s *Pulls) diffFragmentParams(ctx context.Context, l *slog.Logger, repo syntax.DID, base, head string, unified bool) ([]pages.DiffFile, string) { // a. drain the diff stream into one fileDiff per changed file. var baseRevSpec []byte if base != "" { baseRevSpec = []byte(base) } stream, err := s.gitmirror.Diff(ctx, &gitmirrorv1.DiffRequest{ Repo: repo.String(), BaseRevSpec: baseRevSpec, HeadRevSpec: []byte(head), }) if err != nil { l.Error("failed to diff", "err", err) return nil, "Failed to diff. Try again later." } var files []*fileDiff for { fd, err := stream.Recv() if errors.Is(err, io.EOF) { break } if err != nil { l.Error("failed to drain diff response", "err", err) return nil, "Failed to diff. Try again later." } files = append(files, &fileDiff{diff: fd}) }
// b. fetch each file's base/head blob in parallel and split into lines. g, gctx := errgroup.WithContext(ctx) for _, f := range files { g.Go(func() error { lhs, rhs := f.diff.GetLhsSrc(), f.diff.GetRhsSrc() // Binary/submodule files have no line content to fetch. if isBinaryOrSubmodule(lhs) || isBinaryOrSubmodule(rhs) { return nil } baseBlob, err := s.getBlob(gctx, repo, lhs.GetOid()) if err != nil { return err } headBlob, err := s.getBlob(gctx, repo, rhs.GetOid()) if err != nil { return err }
// TODO: highlight each blobs // pass lhs_positions & rhs_positions so highlighter can apply diff highlights for line := range strings.SplitSeq(strings.TrimSuffix(string(baseBlob), "\n"), "\n") { f.baseLines = append(f.baseLines, template.HTML(fmt.Sprintf("<div><span>%s</span></div>", template.HTMLEscapeString(line)))) } for line := range strings.SplitSeq(strings.TrimSuffix(string(headBlob), "\n"), "\n") { f.headLines = append(f.headLines, template.HTML(fmt.Sprintf("<div><span>%s</span></div>", template.HTMLEscapeString(line)))) }
// f.baseLines = strings.Split(strings.TrimSuffix(string(baseBlob), "\n"), "\n") // f.headLines = strings.Split(strings.TrimSuffix(string(headBlob), "\n"), "\n") return nil }) } if err := g.Wait(); err != nil { l.Error("failed to prepare html", "err", err) return nil, "Failed to render diff. Try again later." }
// c. build the render model var outFiles []pages.DiffFile for _, f := range files { df := pages.DiffFile{Path: f.diff.GetRhsSrc().GetPath()}
if isBinaryOrSubmodule(f.diff.GetLhsSrc()) || isBinaryOrSubmodule(f.diff.GetRhsSrc()) { df.Note = "binary or submodule" outFiles = append(outFiles, df) continue }
for _, h := range buildHunks(f.baseLines, f.headLines, f.diff.Hunks) { var dh pages.DiffHunk if unified { dh.Lines = buildUnifiedLines(h, f.baseLines, f.headLines) } else { dh.Rows = buildSplitRows(h, f.baseLines, f.headLines) } df.Hunks = append(df.Hunks, dh) } outFiles = append(outFiles, df) }
return outFiles, ""}