diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 55e67a1243..6020edc95a 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -19,11 +19,11 @@ import ( type CodeComments map[string]map[int64][]*Comment // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line -func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) { - return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments) +func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool, filePath *string) (CodeComments, error) { + return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments, filePath) } -func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) { +func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool, filePath *string) (CodeComments, error) { pathToLineToComment := make(CodeComments) if review == nil { review = &Review{ID: 0} @@ -34,6 +34,15 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u ReviewID: review.ID, } + if filePath != nil { + opts = FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + ReviewID: review.ID, + TreePath: *filePath, + } + } + comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments) if err != nil { return nil, err diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index c08e3b970d..eb37be56e5 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -68,7 +68,7 @@ func TestFetchCodeComments(t *testing.T) { issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false) + res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false, nil) assert.NoError(t, err) assert.Contains(t, res, "README.md") assert.Contains(t, res["README.md"], int64(4)) @@ -76,7 +76,7 @@ func TestFetchCodeComments(t *testing.T) { assert.Equal(t, int64(4), res["README.md"][4][0].ID) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false) + res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false, nil) assert.NoError(t, err) assert.Len(t, res, 1) } diff --git a/models/issues/review.go b/models/issues/review.go index 71fdb7456f..bff76c6245 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -159,7 +159,7 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) { if err = r.LoadIssue(ctx); err != nil { return err } - r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false) + r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false, nil) return err } diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index de34a9375c..9d6d5b53be 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -14,6 +14,7 @@ import ( "net/http" "net/url" "path/filepath" + "sort" "strings" "code.gitea.io/gitea/models/db" @@ -41,6 +42,7 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/gitdiff" + user_service "code.gitea.io/gitea/services/user" ) const ( @@ -881,6 +883,10 @@ func ExcerptBlob(ctx *context.Context) { direction := ctx.FormString("direction") filePath := ctx.FormString("path") gitRepo := ctx.Repo.GitRepo + if ctx.FormBool("pull") { + ctx.Data["PageIsPullFiles"] = true + } + if ctx.Data["PageIsWiki"] == true { var err error gitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository.WikiStorageRepo()) @@ -941,7 +947,9 @@ func ExcerptBlob(ctx *context.Context) { RightIdx: idxRight, LeftHunkSize: leftHunkSize, RightHunkSize: rightHunkSize, + HasComments: false, }, + Comments: nil, } switch direction { case "up": @@ -950,10 +958,69 @@ func ExcerptBlob(ctx *context.Context) { section.Lines = append(section.Lines, lineSection) } } + issueIndex := ctx.FormInt64("issue_index") + if ctx.FormBool("pull") && issueIndex > 0 { + issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, issueIndex) + if err != nil { + ctx.ServerError("GetIssueByIndex", err) + return + } + allComments, err := issues_model.FetchCodeComments(ctx, issue, ctx.Doer, false, &filePath) + if err != nil { + ctx.ServerError("FetchCodeComments", err) + return + } + lineCommits := allComments[filePath] + for index, line := range section.Lines { + if line.SectionInfo != nil && line.Type == 4 && !(line.SectionInfo.LastRightIdx == 0 && index+1 == len(section.Lines)) { + start := int64(line.SectionInfo.LastRightIdx + 1) + end := int64(line.SectionInfo.RightIdx - 1) + for start <= end { + if _, ok := lineCommits[start]; ok { + if !line.SectionInfo.HasComments { + line.SectionInfo.HasComments = true + break + } + } + start++ + } + } + if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { + line.Comments = append(line.Comments, comments...) + } + if comments, ok := lineCommits[int64(line.RightIdx)]; ok { + line.Comments = append(line.Comments, comments...) + } + + sort.SliceStable(line.Comments, func(i, j int) bool { + return line.Comments[i].CreatedUnix < line.Comments[j].CreatedUnix + }) + } + for _, line := range section.Lines { + for _, comment := range line.Comments { + if err := comment.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } + } + } + ctx.Data["Issue"] = issue + ctx.Data["IssueIndex"] = issue.Index + } ctx.Data["section"] = section ctx.Data["FileNameHash"] = git.HashFilePathForWebUI(filePath) ctx.Data["AfterCommitID"] = commitID ctx.Data["Anchor"] = anchor + ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool { + return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee) + } + if ctx.Data["SignedUserID"] == nil { + ctx.Data["SignedUserID"] = ctx.Doer.ID + } + ctx.Data["SignedUser"] = ctx.Doer + ctx.Data["IsSigned"] = ctx.Doer != nil + ctx.Data["Repository"] = ctx.Repo.Repository + ctx.Data["Permission"] = &ctx.Repo.Permission ctx.HTML(http.StatusOK, tplBlobExcerpt) } @@ -982,6 +1049,7 @@ func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chu RightIdx: line + 1, Type: gitdiff.DiffLinePlain, Content: " " + lineText, + Comments: []*issues_model.Comment{}, } diffLines = append(diffLines, diffLine) } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index f662152e2e..30b9f7139b 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -129,6 +129,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { } ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, emoji.ReplaceAliases(issue.Title)) ctx.Data["Issue"] = issue + ctx.Data["IssueIndex"] = issue.Index if !issue.IsPull { ctx.NotFound(nil) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 9964329876..792b8c0b2a 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -95,6 +95,7 @@ type DiffLineSectionInfo struct { RightIdx int LeftHunkSize int RightHunkSize int + HasComments bool } // DiffHTMLOperation is the HTML version of diffmatchpatch.Diff @@ -189,6 +190,7 @@ func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int RightIdx: rightLine, LeftHunkSize: leftHunk, RightHunkSize: righHunk, + HasComments: false, } } @@ -386,6 +388,7 @@ func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommi LastRightIdx: lastLine.RightIdx, LeftIdx: leftLineCount, RightIdx: rightLineCount, + HasComments: false, }, } tailSection := &DiffSection{FileName: diffFile.Name, Lines: []*DiffLine{tailDiffLine}} @@ -456,14 +459,28 @@ type Diff struct { // LoadComments loads comments into each line func (diff *Diff) LoadComments(ctx context.Context, issue *issues_model.Issue, currentUser *user_model.User, showOutdatedComments bool) error { - allComments, err := issues_model.FetchCodeComments(ctx, issue, currentUser, showOutdatedComments) + allComments, err := issues_model.FetchCodeComments(ctx, issue, currentUser, showOutdatedComments, nil) if err != nil { return err } + for _, file := range diff.Files { if lineCommits, ok := allComments[file.Name]; ok { for _, section := range file.Sections { - for _, line := range section.Lines { + for index, line := range section.Lines { + if line.SectionInfo != nil && line.Type == 4 && !(line.SectionInfo.LastRightIdx == 0 && index+1 == len(section.Lines)) { + start := int64(line.SectionInfo.LastRightIdx + 1) + end := int64(line.SectionInfo.RightIdx - 1) + for start <= end { + if _, ok := lineCommits[start]; ok { + if !line.SectionInfo.HasComments { + line.SectionInfo.HasComments = true + break + } + } + start++ + } + } if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { line.Comments = append(line.Comments, comments...) } diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index ae702e4189..fd1d7caebc 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -62,6 +62,7 @@ func TestGetDiffPreview(t *testing.T) { RightIdx: 1, LeftHunkSize: 3, RightHunkSize: 4, + HasComments: false, }, }, { diff --git a/templates/repo/diff/blob_excerpt.tmpl b/templates/repo/diff/blob_excerpt.tmpl index 4089d8fb33..941ac44ae2 100644 --- a/templates/repo/diff/blob_excerpt.tmpl +++ b/templates/repo/diff/blob_excerpt.tmpl @@ -1,10 +1,37 @@ {{$blobExcerptLink := print $.RepoLink (Iif $.PageIsWiki "/wiki" "") "/blob_excerpt/" (PathEscape $.AfterCommitID) (QueryBuild "?" "anchor" $.Anchor)}} {{if $.IsSplitStyle}} {{range $k, $line := $.section.Lines}} + {{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
- {{- end -}}
-
{{/*
+ */}}{{end}}{{/*
+ */}}
- {{- end -}}
-
{{/*
+ */}}{{end}}{{/*
+ */}}{{$inlineDiff.Content}}
{{$inlineDiff.Content}}
+