Add Tabular Diff for CSV files (#14661)
Implements request #14320 The rendering of CSV files does match the diff style. * Moved CSV logic into base package. * Added method to create a tabular diff. * Added CSV compare context. * Added CSV diff template. * Use new table style in CSV markup. * Added file size limit for CSV rendering. * Display CSV parser errors in diff. * Lazy read single file. * Lazy read rows for full diff. * Added unit tests for various CSV changes.
This commit is contained in:
parent
d3b8127ad3
commit
0c6137617f
20 changed files with 937 additions and 118 deletions
|
@ -336,9 +336,8 @@ func Diff(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
}
|
||||
setImageCompareContext(ctx, parentCommit, commit)
|
||||
headTarget := path.Join(userName, repoName)
|
||||
setPathsCompareContext(ctx, parentCommit, commit, headTarget)
|
||||
setCompareContext(ctx, parentCommit, commit, headTarget)
|
||||
ctx.Data["Title"] = commit.Summary() + " · " + base.ShortSha(commitID)
|
||||
ctx.Data["Commit"] = commit
|
||||
verification := models.ParseCommitWithSignature(commit)
|
||||
|
|
|
@ -6,14 +6,20 @@ package repo
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
csv_module "code.gitea.io/gitea/modules/csv"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
@ -26,6 +32,16 @@ const (
|
|||
tplBlobExcerpt base.TplName = "repo/diff/blob_excerpt"
|
||||
)
|
||||
|
||||
// setCompareContext sets context data.
|
||||
func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
|
||||
ctx.Data["BaseCommit"] = base
|
||||
ctx.Data["HeadCommit"] = head
|
||||
|
||||
setPathsCompareContext(ctx, base, head, headTarget)
|
||||
setImageCompareContext(ctx, base, head)
|
||||
setCsvCompareContext(ctx)
|
||||
}
|
||||
|
||||
// setPathsCompareContext sets context data for source and raw paths
|
||||
func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
|
||||
sourcePath := setting.AppSubURL + "/%s/src/commit/%s"
|
||||
|
@ -65,6 +81,73 @@ func setImageCompareContext(ctx *context.Context, base *git.Commit, head *git.Co
|
|||
}
|
||||
}
|
||||
|
||||
// setCsvCompareContext sets context data that is required by the CSV compare template
|
||||
func setCsvCompareContext(ctx *context.Context) {
|
||||
ctx.Data["IsCsvFile"] = func(diffFile *gitdiff.DiffFile) bool {
|
||||
extension := strings.ToLower(filepath.Ext(diffFile.Name))
|
||||
return extension == ".csv" || extension == ".tsv"
|
||||
}
|
||||
|
||||
type CsvDiffResult struct {
|
||||
Sections []*gitdiff.TableDiffSection
|
||||
Error string
|
||||
}
|
||||
|
||||
ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseCommit *git.Commit, headCommit *git.Commit) CsvDiffResult {
|
||||
if diffFile == nil || baseCommit == nil || headCommit == nil {
|
||||
return CsvDiffResult{nil, ""}
|
||||
}
|
||||
|
||||
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
|
||||
|
||||
csvReaderFromCommit := func(c *git.Commit) (*csv.Reader, error) {
|
||||
blob, err := c.GetBlobByPath(diffFile.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
|
||||
return nil, errTooLarge
|
||||
}
|
||||
|
||||
reader, err := blob.DataAsync()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b = charset.ToUTF8WithFallback(b)
|
||||
|
||||
return csv_module.CreateReaderAndGuessDelimiter(b), nil
|
||||
}
|
||||
|
||||
baseReader, err := csvReaderFromCommit(baseCommit)
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
headReader, err := csvReaderFromCommit(headCommit)
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
|
||||
sections, err := gitdiff.CreateCsvDiff(diffFile, baseReader, headReader)
|
||||
if err != nil {
|
||||
errMessage, err := csv_module.FormatError(err, ctx.Locale)
|
||||
if err != nil {
|
||||
log.Error("RenderCsvDiff failed: %v", err)
|
||||
return CsvDiffResult{nil, ""}
|
||||
}
|
||||
return CsvDiffResult{nil, errMessage}
|
||||
}
|
||||
return CsvDiffResult{sections, ""}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseCompareInfo parse compare info between two commit for preparing comparing references
|
||||
func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
|
||||
baseRepo := ctx.Repo.Repository
|
||||
|
@ -490,9 +573,8 @@ func PrepareCompareDiff(
|
|||
ctx.Data["Username"] = headUser.Name
|
||||
ctx.Data["Reponame"] = headRepo.Name
|
||||
|
||||
setImageCompareContext(ctx, baseCommit, headCommit)
|
||||
headTarget := path.Join(headUser.Name, repo.Name)
|
||||
setPathsCompareContext(ctx, baseCommit, headCommit, headTarget)
|
||||
setCompareContext(ctx, baseCommit, headCommit, headTarget)
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -591,7 +591,6 @@ func ViewPullFiles(ctx *context.Context) {
|
|||
gitRepo *git.Repository
|
||||
)
|
||||
|
||||
var headTarget string
|
||||
var prInfo *git.CompareInfo
|
||||
if pull.HasMerged {
|
||||
prInfo = PrepareMergedViewPullInfo(ctx, issue)
|
||||
|
@ -618,7 +617,6 @@ func ViewPullFiles(ctx *context.Context) {
|
|||
startCommitID = prInfo.MergeBase
|
||||
endCommitID = headCommitID
|
||||
|
||||
headTarget = path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||
ctx.Data["AfterCommitID"] = endCommitID
|
||||
|
@ -672,8 +670,8 @@ func ViewPullFiles(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
setImageCompareContext(ctx, baseCommit, commit)
|
||||
setPathsCompareContext(ctx, baseCommit, commit, headTarget)
|
||||
headTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||
setCompareContext(ctx, baseCommit, commit, headTarget)
|
||||
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
ctx.Data["RequireSimpleMDE"] = true
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue