forked from forgejo/forgejo
Pull request review/approval and comment on code (#3748)
* Initial ui components for pull request review
* Add Review
Add IssueComment types
Signed-off-by: Jonas Franz <info@jonasfranz.software>
(cherry picked from commit 2b4daab)
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Replace ReviewComment with Content
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add load functions
Add ReviewID to findComments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add create review comment implementation
Add migration for review
Other small changes
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Simplified create and find functions for review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Moved "Pending" to first position
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add GetCurrentReview to simplify fetching current review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Preview for listing comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Move new comment form to its own file
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Implement Review form
Show Review comments on comment stream
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for single comments
Showing buttons in context
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add pending tag to pending review comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add unit tests for Review
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fetch all review ids at once
Add unit tests
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Improved comment rendering in "Files" view by adding Comments to DiffLine
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for invalidating comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Switched back to code.gitea.io/git
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Moved review migration from v64 to v65
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Rebuild css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Improve translations
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix unit tests by updating fixtures and updating outdated test
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Comments will be shown at the right place now
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for deleting CodeComments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix problems caused by files in subdirectories
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for showing code comments of reviews in conversation
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for "Show/Hide outdated"
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update code.gitea.io/git
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add support for new webhooks
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update comparison
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Resolve conflicts
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Minor UI improvements
* update code.gitea.io/git
* Fix ui bug reported by @lunny causing wrong position of add button
Add functionality to "Cancel" button
Add scale effects to add button
Hide "Cancel" button for existing comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Prepare solving conflicts
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Show add button only if no comments already exist for the line
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add missing vendor files
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Check if reviewer is nil
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Show forms only to users who are logged in
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Revert "Show forms only to users who are logged in"
This reverts commit c083682
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Save patch in comment
Render patch for code comments
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add link to comment in code
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add reply form to comment list
Show forms only to signed in users
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add 'Reply' as translatable
Add CODE_COMMENT_LINES setting
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* gofmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix problems introduced by checking for singed in user
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add v70
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update generated stylesheet
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix preview
Beginn with new review comment patch system
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add new algo to generate diff for line range
Remove old algo used for cutting big diffs (it was very buggy)
* Add documentation and example for CutDiffAroundLine
* Fix example of CutDiffAroundLine
* Fix some comment UI rendering bugs
* Add code comment edit mode
* Send notifications / actions to users until review gets published
Fix diff generation bug
Fix wrong hashtag
* Fix vet errors
* Send notifications also for single comments
* Fix some notification bugs, fix link
* Fix: add comment icon is only shown on code lines
* Add lint comment
* Add unit tests for git diff
* Add more error messages
* Regenerated css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* fmt
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Regenerated CSS with latest less version
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix test by updating comment type to new ID
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Introducing CodeComments as type for map[string]map[int64][]*Comment
Other minor code improvements
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Fix data-tab issues
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Remove unnecessary change
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* refactored checkForInvalidation
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Append comments instead of setting
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Use HeadRepo instead of BaseRepo
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update migration
Signed-off-by: Jonas Franz <info@jonasfranz.de>
* Regenerated CSS
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Add copyright
Signed-off-by: Jonas Franz <info@jonasfranz.software>
* Update index.css
Signed-off-by: Jonas Franz <info@jonasfranz.software>
This commit is contained in:
parent
9c354a539a
commit
6e64f9db8e
40 changed files with 2010 additions and 89 deletions
|
@ -14,6 +14,8 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -57,6 +59,7 @@ type DiffLine struct {
|
|||
RightIdx int
|
||||
Type DiffLineType
|
||||
Content string
|
||||
Comments []*Comment
|
||||
}
|
||||
|
||||
// GetType returns the type of a DiffLine.
|
||||
|
@ -64,6 +67,19 @@ func (d *DiffLine) GetType() int {
|
|||
return int(d.Type)
|
||||
}
|
||||
|
||||
// CanComment returns whether or not a line can get commented
|
||||
func (d *DiffLine) CanComment() bool {
|
||||
return len(d.Comments) == 0 && d.Type != DiffLineSection
|
||||
}
|
||||
|
||||
// GetCommentSide returns the comment side of the first comment, if not set returns empty string
|
||||
func (d *DiffLine) GetCommentSide() string {
|
||||
if len(d.Comments) == 0 {
|
||||
return ""
|
||||
}
|
||||
return d.Comments[0].DiffSide()
|
||||
}
|
||||
|
||||
// DiffSection represents a section of a DiffFile.
|
||||
type DiffSection struct {
|
||||
Name string
|
||||
|
@ -225,11 +241,167 @@ type Diff struct {
|
|||
IsIncomplete bool
|
||||
}
|
||||
|
||||
// LoadComments loads comments into each line
|
||||
func (diff *Diff) LoadComments(issue *Issue, currentUser *User) error {
|
||||
allComments, err := FetchCodeComments(issue, currentUser)
|
||||
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 {
|
||||
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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NumFiles returns number of files changes in a diff.
|
||||
func (diff *Diff) NumFiles() int {
|
||||
return len(diff.Files)
|
||||
}
|
||||
|
||||
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
|
||||
var hunkRegex = regexp.MustCompile(`^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@`)
|
||||
|
||||
func isHeader(lof string) bool {
|
||||
return strings.HasPrefix(lof, cmdDiffHead) || strings.HasPrefix(lof, "---") || strings.HasPrefix(lof, "+++")
|
||||
}
|
||||
|
||||
// CutDiffAroundLine cuts a diff of a file in way that only the given line + numberOfLine above it will be shown
|
||||
// it also recalculates hunks and adds the appropriate headers to the new diff.
|
||||
// Warning: Only one-file diffs are allowed.
|
||||
func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLine int) string {
|
||||
if line == 0 || numbersOfLine == 0 {
|
||||
// no line or num of lines => no diff
|
||||
return ""
|
||||
}
|
||||
scanner := bufio.NewScanner(originalDiff)
|
||||
hunk := make([]string, 0)
|
||||
// begin is the start of the hunk containing searched line
|
||||
// end is the end of the hunk ...
|
||||
// currentLine is the line number on the side of the searched line (differentiated by old)
|
||||
// otherLine is the line number on the opposite side of the searched line (differentiated by old)
|
||||
var begin, end, currentLine, otherLine int64
|
||||
var headerLines int
|
||||
for scanner.Scan() {
|
||||
lof := scanner.Text()
|
||||
// Add header to enable parsing
|
||||
if isHeader(lof) {
|
||||
hunk = append(hunk, lof)
|
||||
headerLines++
|
||||
}
|
||||
if currentLine > line {
|
||||
break
|
||||
}
|
||||
// Detect "hunk" with contains commented lof
|
||||
if strings.HasPrefix(lof, "@@") {
|
||||
// Already got our hunk. End of hunk detected!
|
||||
if len(hunk) > headerLines {
|
||||
break
|
||||
}
|
||||
groups := hunkRegex.FindStringSubmatch(lof)
|
||||
if old {
|
||||
begin = com.StrTo(groups[1]).MustInt64()
|
||||
end = com.StrTo(groups[2]).MustInt64()
|
||||
// init otherLine with begin of opposite side
|
||||
otherLine = com.StrTo(groups[3]).MustInt64()
|
||||
} else {
|
||||
begin = com.StrTo(groups[3]).MustInt64()
|
||||
end = com.StrTo(groups[4]).MustInt64()
|
||||
// init otherLine with begin of opposite side
|
||||
otherLine = com.StrTo(groups[1]).MustInt64()
|
||||
}
|
||||
end += begin // end is for real only the number of lines in hunk
|
||||
// lof is between begin and end
|
||||
if begin <= line && end >= line {
|
||||
hunk = append(hunk, lof)
|
||||
currentLine = begin
|
||||
continue
|
||||
}
|
||||
} else if len(hunk) > headerLines {
|
||||
hunk = append(hunk, lof)
|
||||
// Count lines in context
|
||||
switch lof[0] {
|
||||
case '+':
|
||||
if !old {
|
||||
currentLine++
|
||||
} else {
|
||||
otherLine++
|
||||
}
|
||||
case '-':
|
||||
if old {
|
||||
currentLine++
|
||||
} else {
|
||||
otherLine++
|
||||
}
|
||||
default:
|
||||
currentLine++
|
||||
otherLine++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No hunk found
|
||||
if currentLine == 0 {
|
||||
return ""
|
||||
}
|
||||
// headerLines + hunkLine (1) = totalNonCodeLines
|
||||
if len(hunk)-headerLines-1 <= numbersOfLine {
|
||||
// No need to cut the hunk => return existing hunk
|
||||
return strings.Join(hunk, "\n")
|
||||
}
|
||||
var oldBegin, oldNumOfLines, newBegin, newNumOfLines int64
|
||||
if old {
|
||||
oldBegin = currentLine
|
||||
newBegin = otherLine
|
||||
} else {
|
||||
oldBegin = otherLine
|
||||
newBegin = currentLine
|
||||
}
|
||||
// headers + hunk header
|
||||
newHunk := make([]string, headerLines)
|
||||
// transfer existing headers
|
||||
for idx, lof := range hunk[:headerLines] {
|
||||
newHunk[idx] = lof
|
||||
}
|
||||
// transfer last n lines
|
||||
for _, lof := range hunk[len(hunk)-numbersOfLine-1:] {
|
||||
newHunk = append(newHunk, lof)
|
||||
}
|
||||
// calculate newBegin, ... by counting lines
|
||||
for i := len(hunk) - 1; i >= len(hunk)-numbersOfLine; i-- {
|
||||
switch hunk[i][0] {
|
||||
case '+':
|
||||
newBegin--
|
||||
newNumOfLines++
|
||||
case '-':
|
||||
oldBegin--
|
||||
oldNumOfLines++
|
||||
default:
|
||||
oldBegin--
|
||||
newBegin--
|
||||
newNumOfLines++
|
||||
oldNumOfLines++
|
||||
}
|
||||
}
|
||||
// construct the new hunk header
|
||||
newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
|
||||
oldBegin, oldNumOfLines, newBegin, newNumOfLines)
|
||||
return strings.Join(newHunk, "\n")
|
||||
}
|
||||
|
||||
const cmdDiffHead = "diff --git "
|
||||
|
||||
// ParsePatch builds a Diff object from a io.Reader and some
|
||||
|
@ -307,7 +479,6 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
|
|||
if curFileLinesCount >= maxLines {
|
||||
curFile.IsIncomplete = true
|
||||
}
|
||||
|
||||
switch {
|
||||
case line[0] == ' ':
|
||||
diffLine := &DiffLine{Type: DiffLinePlain, Content: line, LeftIdx: leftLine, RightIdx: rightLine}
|
||||
|
@ -524,32 +695,46 @@ const (
|
|||
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
|
||||
// TODO: move this function to gogits/git-module
|
||||
func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
|
||||
return GetRawDiffForFile(repoPath, "", commitID, diffType, "", writer)
|
||||
}
|
||||
|
||||
// GetRawDiffForFile dumps diff results of file in given commit ID to io.Writer.
|
||||
// TODO: move this function to gogits/git-module
|
||||
func GetRawDiffForFile(repoPath, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error {
|
||||
repo, err := git.OpenRepository(repoPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("OpenRepository: %v", err)
|
||||
}
|
||||
|
||||
commit, err := repo.GetCommit(commitID)
|
||||
commit, err := repo.GetCommit(endCommit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetCommit: %v", err)
|
||||
}
|
||||
|
||||
fileArgs := make([]string, 0)
|
||||
if len(file) > 0 {
|
||||
fileArgs = append(fileArgs, "--", file)
|
||||
}
|
||||
var cmd *exec.Cmd
|
||||
switch diffType {
|
||||
case RawDiffNormal:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "show", commitID)
|
||||
if len(startCommit) != 0 {
|
||||
cmd = exec.Command("git", append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...)
|
||||
} else if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", append([]string{"show", endCommit}, fileArgs...)...)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
cmd = exec.Command("git", "diff", "-M", c.ID.String(), commitID)
|
||||
cmd = exec.Command("git", append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...)
|
||||
}
|
||||
case RawDiffPatch:
|
||||
if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", "--root", commitID)
|
||||
if len(startCommit) != 0 {
|
||||
query := fmt.Sprintf("%s...%s", endCommit, startCommit)
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...)
|
||||
} else if commit.ParentCount() == 0 {
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...)
|
||||
} else {
|
||||
c, _ := commit.Parent(0)
|
||||
query := fmt.Sprintf("%s...%s", commitID, c.ID.String())
|
||||
cmd = exec.Command("git", "format-patch", "--no-signature", "--stdout", query)
|
||||
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
|
||||
cmd = exec.Command("git", append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||
|
@ -560,7 +745,6 @@ func GetRawDiff(repoPath, commitID string, diffType RawDiffType, writer io.Write
|
|||
cmd.Dir = repoPath
|
||||
cmd.Stdout = writer
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err = cmd.Run(); err != nil {
|
||||
return fmt.Errorf("Run: %v - %s", err, stderr)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue