* update #8659 fold/unfold code diffs * add fold button style * update #8659 implement expand up/down codes (blob excerpt) * fix golint errors * fix expand direction * remove debug message * update css style for blob exceprt * fix typo in comment * update style sheet with less * update expect diff (add SectionInfo) * update #8942 accept suggested change (fix typo) * close reader and check file type before get tail section * adjust button position and check file type before insert fold button * move index js to web_src * merge index.js with master * generate index.js * update js coding styletags/v1.11.0-rc1
@@ -6,6 +6,7 @@ | |||||
package git | package git | ||||
import ( | import ( | ||||
"bytes" | |||||
"encoding/base64" | "encoding/base64" | ||||
"io" | "io" | ||||
"io/ioutil" | "io/ioutil" | ||||
@@ -50,6 +51,28 @@ func (b *Blob) GetBlobContent() (string, error) { | |||||
return string(buf), nil | return string(buf), nil | ||||
} | } | ||||
// GetBlobLineCount gets line count of lob as raw text | |||||
func (b *Blob) GetBlobLineCount() (int, error) { | |||||
reader, err := b.DataAsync() | |||||
if err != nil { | |||||
return 0, err | |||||
} | |||||
defer reader.Close() | |||||
buf := make([]byte, 32*1024) | |||||
count := 0 | |||||
lineSep := []byte{'\n'} | |||||
for { | |||||
c, err := reader.Read(buf) | |||||
count += bytes.Count(buf[:c], lineSep) | |||||
switch { | |||||
case err == io.EOF: | |||||
return count, nil | |||||
case err != nil: | |||||
return count, err | |||||
} | |||||
} | |||||
} | |||||
// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string | // GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string | ||||
func (b *Blob) GetBlobContentBase64() (string, error) { | func (b *Blob) GetBlobContentBase64() (string, error) { | ||||
dataRc, err := b.DataAsync() | dataRc, err := b.DataAsync() | ||||
@@ -55,6 +55,15 @@ func TestGetDiffPreview(t *testing.T) { | |||||
Type: 4, | Type: 4, | ||||
Content: "@@ -1,3 +1,4 @@", | Content: "@@ -1,3 +1,4 @@", | ||||
Comments: nil, | Comments: nil, | ||||
SectionInfo: &gitdiff.DiffLineSectionInfo{ | |||||
Path: "README.md", | |||||
LastLeftIdx: 0, | |||||
LastRightIdx: 0, | |||||
LeftIdx: 1, | |||||
RightIdx: 1, | |||||
LeftHunkSize: 3, | |||||
RightHunkSize: 4, | |||||
}, | |||||
}, | }, | ||||
{ | { | ||||
LeftIdx: 1, | LeftIdx: 1, | ||||
@@ -898,6 +898,7 @@ tbody.commit-list{vertical-align:baseline} | |||||
.repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important} | .repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important} | ||||
.repo-buttons .ui.labeled.button>.label{border-left:0!important;margin:0!important} | .repo-buttons .ui.labeled.button>.label{border-left:0!important;margin:0!important} | ||||
.tag-code,.tag-code td{background-color:#f0f0f0!important;border-color:#d3cfcf!important;padding-top:8px;padding-bottom:8px} | .tag-code,.tag-code td{background-color:#f0f0f0!important;border-color:#d3cfcf!important;padding-top:8px;padding-bottom:8px} | ||||
td.blob-excerpt{background-color:#fafafa} | |||||
.issue-keyword{border-bottom:1px dotted #959da5;display:inline-block} | .issue-keyword{border-bottom:1px dotted #959da5;display:inline-block} | ||||
.file-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px!important} | .file-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px!important} | ||||
.file-info{display:flex;align-items:center} | .file-info{display:flex;align-items:center} | ||||
@@ -1068,4 +1069,8 @@ tbody.commit-list{vertical-align:baseline} | |||||
.comment-code-cloud .footer:after{clear:both;content:"";display:block} | .comment-code-cloud .footer:after{clear:both;content:"";display:block} | ||||
.comment-code-cloud button.comment-form-reply{margin:.5em .5em .5em 4.5em} | .comment-code-cloud button.comment-form-reply{margin:.5em .5em .5em 4.5em} | ||||
.comment-code-cloud form.comment-form-reply{margin:0 0 0 4em} | .comment-code-cloud form.comment-form-reply{margin:0 0 0 4em} | ||||
.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)} | |||||
.file-comment{font:12px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;color:rgba(0,0,0,.87)} | |||||
.ui.fold-code{margin-right:1em;padding-left:5px;cursor:pointer;width:22px;font-size:12px} | |||||
.ui.fold-code:hover{color:#428bca} | |||||
.ui.blob-excerpt{display:block;line-height:20px;font-size:16px;cursor:pointer} | |||||
.ui.blob-excerpt:hover{color:#428bca} |
@@ -245,6 +245,7 @@ func Diff(ctx *context.Context) { | |||||
} | } | ||||
ctx.Data["CommitID"] = commitID | ctx.Data["CommitID"] = commitID | ||||
ctx.Data["AfterCommitID"] = commitID | |||||
ctx.Data["Username"] = userName | ctx.Data["Username"] = userName | ||||
ctx.Data["Reponame"] = repoName | ctx.Data["Reponame"] = repoName | ||||
@@ -5,21 +5,26 @@ | |||||
package repo | package repo | ||||
import ( | import ( | ||||
"bufio" | |||||
"fmt" | "fmt" | ||||
"html" | |||||
"path" | "path" | ||||
"path/filepath" | |||||
"strings" | "strings" | ||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
"code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
"code.gitea.io/gitea/modules/git" | "code.gitea.io/gitea/modules/git" | ||||
"code.gitea.io/gitea/modules/highlight" | |||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"code.gitea.io/gitea/services/gitdiff" | "code.gitea.io/gitea/services/gitdiff" | ||||
) | ) | ||||
const ( | const ( | ||||
tplCompare base.TplName = "repo/diff/compare" | |||||
tplCompare base.TplName = "repo/diff/compare" | |||||
tplBlobExcerpt base.TplName = "repo/diff/blob_excerpt" | |||||
) | ) | ||||
// setPathsCompareContext sets context data for source and raw paths | // setPathsCompareContext sets context data for source and raw paths | ||||
@@ -434,3 +439,109 @@ func CompareDiff(ctx *context.Context) { | |||||
ctx.HTML(200, tplCompare) | ctx.HTML(200, tplCompare) | ||||
} | } | ||||
// ExcerptBlob render blob excerpt contents | |||||
func ExcerptBlob(ctx *context.Context) { | |||||
commitID := ctx.Params("sha") | |||||
lastLeft := ctx.QueryInt("last_left") | |||||
lastRight := ctx.QueryInt("last_right") | |||||
idxLeft := ctx.QueryInt("left") | |||||
idxRight := ctx.QueryInt("right") | |||||
leftHunkSize := ctx.QueryInt("left_hunk_size") | |||||
rightHunkSize := ctx.QueryInt("right_hunk_size") | |||||
anchor := ctx.Query("anchor") | |||||
direction := ctx.Query("direction") | |||||
filePath := ctx.Query("path") | |||||
gitRepo := ctx.Repo.GitRepo | |||||
chunkSize := gitdiff.BlobExceprtChunkSize | |||||
commit, err := gitRepo.GetCommit(commitID) | |||||
if err != nil { | |||||
ctx.Error(500, "GetCommit") | |||||
return | |||||
} | |||||
section := &gitdiff.DiffSection{ | |||||
Name: filePath, | |||||
} | |||||
if direction == "up" && (idxLeft-lastLeft) > chunkSize { | |||||
idxLeft -= chunkSize | |||||
idxRight -= chunkSize | |||||
leftHunkSize += chunkSize | |||||
rightHunkSize += chunkSize | |||||
section.Lines, err = getExcerptLines(commit, filePath, idxLeft-1, idxRight-1, chunkSize) | |||||
} else if direction == "down" && (idxLeft-lastLeft) > chunkSize { | |||||
section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, chunkSize) | |||||
lastLeft += chunkSize | |||||
lastRight += chunkSize | |||||
} else { | |||||
section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, idxRight-lastRight-1) | |||||
leftHunkSize = 0 | |||||
rightHunkSize = 0 | |||||
idxLeft = lastLeft | |||||
idxRight = lastRight | |||||
} | |||||
if err != nil { | |||||
ctx.Error(500, "getExcerptLines") | |||||
return | |||||
} | |||||
if idxRight > lastRight { | |||||
lineText := " " | |||||
if rightHunkSize > 0 || leftHunkSize > 0 { | |||||
lineText = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) | |||||
} | |||||
lineText = html.EscapeString(lineText) | |||||
lineSection := &gitdiff.DiffLine{ | |||||
Type: gitdiff.DiffLineSection, | |||||
Content: lineText, | |||||
SectionInfo: &gitdiff.DiffLineSectionInfo{ | |||||
Path: filePath, | |||||
LastLeftIdx: lastLeft, | |||||
LastRightIdx: lastRight, | |||||
LeftIdx: idxLeft, | |||||
RightIdx: idxRight, | |||||
LeftHunkSize: leftHunkSize, | |||||
RightHunkSize: rightHunkSize, | |||||
}} | |||||
if direction == "up" { | |||||
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...) | |||||
} else if direction == "down" { | |||||
section.Lines = append(section.Lines, lineSection) | |||||
} | |||||
} | |||||
ctx.Data["section"] = section | |||||
ctx.Data["fileName"] = filePath | |||||
ctx.Data["highlightClass"] = highlight.FileNameToHighlightClass(filepath.Base(filePath)) | |||||
ctx.Data["AfterCommitID"] = commitID | |||||
ctx.Data["Anchor"] = anchor | |||||
ctx.HTML(200, tplBlobExcerpt) | |||||
} | |||||
func getExcerptLines(commit *git.Commit, filePath string, idxLeft int, idxRight int, chunkSize int) ([]*gitdiff.DiffLine, error) { | |||||
blob, err := commit.Tree.GetBlobByPath(filePath) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
reader, err := blob.DataAsync() | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
defer reader.Close() | |||||
scanner := bufio.NewScanner(reader) | |||||
var diffLines []*gitdiff.DiffLine | |||||
for line := 0; line < idxRight+chunkSize; line++ { | |||||
if ok := scanner.Scan(); !ok { | |||||
break | |||||
} | |||||
if line < idxRight { | |||||
continue | |||||
} | |||||
lineText := scanner.Text() | |||||
diffLine := &gitdiff.DiffLine{ | |||||
LeftIdx: idxLeft + (line - idxRight) + 1, | |||||
RightIdx: line + 1, | |||||
Type: gitdiff.DiffLinePlain, | |||||
Content: " " + lineText, | |||||
} | |||||
diffLines = append(diffLines, diffLine) | |||||
} | |||||
return diffLines, nil | |||||
} |
@@ -552,6 +552,7 @@ func ViewPullFiles(ctx *context.Context) { | |||||
ctx.Data["Username"] = pull.MustHeadUserName() | ctx.Data["Username"] = pull.MustHeadUserName() | ||||
ctx.Data["Reponame"] = pull.HeadRepo.Name | ctx.Data["Reponame"] = pull.HeadRepo.Name | ||||
} | } | ||||
ctx.Data["AfterCommitID"] = endCommitID | |||||
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(diffRepoPath, | diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(diffRepoPath, | ||||
startCommitID, endCommitID, setting.Git.MaxGitDiffLines, | startCommitID, endCommitID, setting.Git.MaxGitDiffLines, | ||||
@@ -864,6 +864,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
m.Get("", repo.Branches) | m.Get("", repo.Branches) | ||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) | }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) | ||||
m.Group("/blob_excerpt", func() { | |||||
m.Get("/:sha", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) | |||||
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) | |||||
m.Group("/pulls/:index", func() { | m.Group("/pulls/:index", func() { | ||||
m.Get(".diff", repo.DownloadPullDiff) | m.Get(".diff", repo.DownloadPullDiff) | ||||
m.Get(".patch", repo.DownloadPullPatch) | m.Get(".patch", repo.DownloadPullPatch) | ||||
@@ -13,6 +13,7 @@ import ( | |||||
"html/template" | "html/template" | ||||
"io" | "io" | ||||
"io/ioutil" | "io/ioutil" | ||||
"net/url" | |||||
"os" | "os" | ||||
"os/exec" | "os/exec" | ||||
"regexp" | "regexp" | ||||
@@ -56,15 +57,42 @@ const ( | |||||
DiffFileRename | DiffFileRename | ||||
) | ) | ||||
// DiffLineExpandDirection represents the DiffLineSection expand direction | |||||
type DiffLineExpandDirection uint8 | |||||
// DiffLineExpandDirection possible values. | |||||
const ( | |||||
DiffLineExpandNone DiffLineExpandDirection = iota + 1 | |||||
DiffLineExpandSingle | |||||
DiffLineExpandUpDown | |||||
DiffLineExpandUp | |||||
DiffLineExpandDown | |||||
) | |||||
// DiffLine represents a line difference in a DiffSection. | // DiffLine represents a line difference in a DiffSection. | ||||
type DiffLine struct { | type DiffLine struct { | ||||
LeftIdx int | |||||
RightIdx int | |||||
Type DiffLineType | |||||
Content string | |||||
Comments []*models.Comment | |||||
LeftIdx int | |||||
RightIdx int | |||||
Type DiffLineType | |||||
Content string | |||||
Comments []*models.Comment | |||||
SectionInfo *DiffLineSectionInfo | |||||
} | |||||
// DiffLineSectionInfo represents diff line section meta data | |||||
type DiffLineSectionInfo struct { | |||||
Path string | |||||
LastLeftIdx int | |||||
LastRightIdx int | |||||
LeftIdx int | |||||
RightIdx int | |||||
LeftHunkSize int | |||||
RightHunkSize int | |||||
} | } | ||||
// BlobExceprtChunkSize represent max lines of excerpt | |||||
const BlobExceprtChunkSize = 20 | |||||
// GetType returns the type of a DiffLine. | // GetType returns the type of a DiffLine. | ||||
func (d *DiffLine) GetType() int { | func (d *DiffLine) GetType() int { | ||||
return int(d.Type) | return int(d.Type) | ||||
@@ -91,6 +119,71 @@ func (d *DiffLine) GetLineTypeMarker() string { | |||||
return "" | return "" | ||||
} | } | ||||
// GetBlobExcerptQuery builds query string to get blob excerpt | |||||
func (d *DiffLine) GetBlobExcerptQuery() string { | |||||
query := fmt.Sprintf( | |||||
"last_left=%d&last_right=%d&"+ | |||||
"left=%d&right=%d&"+ | |||||
"left_hunk_size=%d&right_hunk_size=%d&"+ | |||||
"path=%s", | |||||
d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx, | |||||
d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx, | |||||
d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize, | |||||
url.QueryEscape(d.SectionInfo.Path)) | |||||
return query | |||||
} | |||||
// GetExpandDirection gets DiffLineExpandDirection | |||||
func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection { | |||||
if d.Type != DiffLineSection || d.SectionInfo == nil || d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx <= 1 { | |||||
return DiffLineExpandNone | |||||
} | |||||
if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 { | |||||
return DiffLineExpandUp | |||||
} else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExceprtChunkSize && d.SectionInfo.RightHunkSize > 0 { | |||||
return DiffLineExpandUpDown | |||||
} else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 { | |||||
return DiffLineExpandDown | |||||
} | |||||
return DiffLineExpandSingle | |||||
} | |||||
func getDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo { | |||||
var ( | |||||
leftLine int | |||||
leftHunk int | |||||
rightLine int | |||||
righHunk int | |||||
) | |||||
ss := strings.Split(line, "@@") | |||||
ranges := strings.Split(ss[1][1:], " ") | |||||
leftRange := strings.Split(ranges[0], ",") | |||||
leftLine, _ = com.StrTo(leftRange[0][1:]).Int() | |||||
if len(leftRange) > 1 { | |||||
leftHunk, _ = com.StrTo(leftRange[1]).Int() | |||||
} | |||||
if len(ranges) > 1 { | |||||
rightRange := strings.Split(ranges[1], ",") | |||||
rightLine, _ = com.StrTo(rightRange[0]).Int() | |||||
if len(rightRange) > 1 { | |||||
righHunk, _ = com.StrTo(rightRange[1]).Int() | |||||
} | |||||
} else { | |||||
log.Warn("Parse line number failed: %v", line) | |||||
rightLine = leftLine | |||||
righHunk = leftHunk | |||||
} | |||||
return &DiffLineSectionInfo{ | |||||
Path: curFile.Name, | |||||
LastLeftIdx: lastLeftIdx, | |||||
LastRightIdx: lastRightIdx, | |||||
LeftIdx: leftLine, | |||||
RightIdx: rightLine, | |||||
LeftHunkSize: leftHunk, | |||||
RightHunkSize: righHunk, | |||||
} | |||||
} | |||||
// escape a line's content or return <br> needed for copy/paste purposes | // escape a line's content or return <br> needed for copy/paste purposes | ||||
func getLineContent(content string) string { | func getLineContent(content string) string { | ||||
if len(content) > 0 { | if len(content) > 0 { | ||||
@@ -248,6 +341,53 @@ func (diffFile *DiffFile) GetHighlightClass() string { | |||||
return highlight.FileNameToHighlightClass(diffFile.Name) | return highlight.FileNameToHighlightClass(diffFile.Name) | ||||
} | } | ||||
// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file | |||||
func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID, rightCommitID string) *DiffSection { | |||||
if diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile { | |||||
return nil | |||||
} | |||||
leftCommit, err := gitRepo.GetCommit(leftCommitID) | |||||
if err != nil { | |||||
return nil | |||||
} | |||||
rightCommit, err := gitRepo.GetCommit(rightCommitID) | |||||
if err != nil { | |||||
return nil | |||||
} | |||||
lastSection := diffFile.Sections[len(diffFile.Sections)-1] | |||||
lastLine := lastSection.Lines[len(lastSection.Lines)-1] | |||||
leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name) | |||||
rightLineCount := getCommitFileLineCount(rightCommit, diffFile.Name) | |||||
if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx { | |||||
return nil | |||||
} | |||||
tailDiffLine := &DiffLine{ | |||||
Type: DiffLineSection, | |||||
Content: " ", | |||||
SectionInfo: &DiffLineSectionInfo{ | |||||
Path: diffFile.Name, | |||||
LastLeftIdx: lastLine.LeftIdx, | |||||
LastRightIdx: lastLine.RightIdx, | |||||
LeftIdx: leftLineCount, | |||||
RightIdx: rightLineCount, | |||||
}} | |||||
tailSection := &DiffSection{Lines: []*DiffLine{tailDiffLine}} | |||||
return tailSection | |||||
} | |||||
func getCommitFileLineCount(commit *git.Commit, filePath string) int { | |||||
blob, err := commit.GetBlobByPath(filePath) | |||||
if err != nil { | |||||
return 0 | |||||
} | |||||
lineCount, err := blob.GetBlobLineCount() | |||||
if err != nil { | |||||
return 0 | |||||
} | |||||
return lineCount | |||||
} | |||||
// Diff represents a difference between two git trees. | // Diff represents a difference between two git trees. | ||||
type Diff struct { | type Diff struct { | ||||
TotalAddition, TotalDeletion int | TotalAddition, TotalDeletion int | ||||
@@ -510,19 +650,16 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D | |||||
case line[0] == '@': | case line[0] == '@': | ||||
curSection = &DiffSection{} | curSection = &DiffSection{} | ||||
curFile.Sections = append(curFile.Sections, curSection) | curFile.Sections = append(curFile.Sections, curSection) | ||||
ss := strings.Split(line, "@@") | |||||
diffLine := &DiffLine{Type: DiffLineSection, Content: line} | |||||
curSection.Lines = append(curSection.Lines, diffLine) | |||||
// Parse line number. | |||||
ranges := strings.Split(ss[1][1:], " ") | |||||
leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | |||||
if len(ranges) > 1 { | |||||
rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int() | |||||
} else { | |||||
log.Warn("Parse line number failed: %v", line) | |||||
rightLine = leftLine | |||||
lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1) | |||||
diffLine := &DiffLine{ | |||||
Type: DiffLineSection, | |||||
Content: line, | |||||
SectionInfo: lineSectionInfo, | |||||
} | } | ||||
curSection.Lines = append(curSection.Lines, diffLine) | |||||
// update line number. | |||||
leftLine = lineSectionInfo.LeftIdx | |||||
rightLine = lineSectionInfo.RightIdx | |||||
continue | continue | ||||
case line[0] == '+': | case line[0] == '+': | ||||
curFile.Addition++ | curFile.Addition++ | ||||
@@ -599,6 +736,8 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D | |||||
break | break | ||||
} | } | ||||
curFileLinesCount = 0 | curFileLinesCount = 0 | ||||
leftLine = 1 | |||||
rightLine = 1 | |||||
curFileLFSPrefix = false | curFileLFSPrefix = false | ||||
// Check file diff type and is submodule. | // Check file diff type and is submodule. | ||||
@@ -701,6 +840,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID | |||||
diffArgs = append(diffArgs, actualBeforeCommitID) | diffArgs = append(diffArgs, actualBeforeCommitID) | ||||
diffArgs = append(diffArgs, afterCommitID) | diffArgs = append(diffArgs, afterCommitID) | ||||
cmd = exec.Command(git.GitExecutable, diffArgs...) | cmd = exec.Command(git.GitExecutable, diffArgs...) | ||||
beforeCommitID = actualBeforeCommitID | |||||
} | } | ||||
cmd.Dir = repoPath | cmd.Dir = repoPath | ||||
cmd.Stderr = os.Stderr | cmd.Stderr = os.Stderr | ||||
@@ -721,6 +861,12 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID | |||||
if err != nil { | if err != nil { | ||||
return nil, fmt.Errorf("ParsePatch: %v", err) | return nil, fmt.Errorf("ParsePatch: %v", err) | ||||
} | } | ||||
for _, diffFile := range diff.Files { | |||||
tailSection := diffFile.GetTailSection(gitRepo, beforeCommitID, afterCommitID) | |||||
if tailSection != nil { | |||||
diffFile.Sections = append(diffFile.Sections, tailSection) | |||||
} | |||||
} | |||||
if err = cmd.Wait(); err != nil { | if err = cmd.Wait(); err != nil { | ||||
return nil, fmt.Errorf("Wait: %v", err) | return nil, fmt.Errorf("Wait: %v", err) | ||||
@@ -0,0 +1,50 @@ | |||||
{{if $.IsSplitStyle}} | |||||
{{range $k, $line := $.section.Lines}} | |||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | |||||
{{if eq .GetType 4}} | |||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"> | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | |||||
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }} | |||||
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 2)}} | |||||
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
</td> | |||||
<td colspan="5" class="lines-code lines-code-old "><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{$.section.GetComputedInlineDiffFor $line}}</span></td> | |||||
{{else}} | |||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $.fileName}}L{{$line.LeftIdx}}{{end}}"></span></td> | |||||
<td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td> | |||||
<td class="blob-excerpt lines-code lines-code-old halfwidth"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $.fileName}}R{{$line.RightIdx}}{{end}}"></span></td> | |||||
<td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker=""></span>{{end}}</td> | |||||
<td class="blob-excerpt lines-code lines-code-new halfwidth"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$.section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
{{end}} | |||||
</tr> | |||||
{{end}} | |||||
{{else}} | |||||
{{range $k, $line := $.section.Lines}} | |||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | |||||
{{if eq .GetType 4}} | |||||
<td colspan="2" class="lines-num"> | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }} | |||||
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | |||||
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 2)}} | |||||
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="{{$.Anchor}}"></i> | |||||
{{end}} | |||||
</td> | |||||
{{else}} | |||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $.fileName}}L{{$line.LeftIdx}}{{end}}"></span></td> | |||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $.fileName}}R{{$line.RightIdx}}{{end}}"></span></td> | |||||
{{end}} | |||||
<td class="blob-excerpt lines-type-marker"><span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> | |||||
<td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><span class="mono wrap{{if $.highlightClass}} language-{{$.highlightClass}}{{else}} nohighlight{{end}}">{{$.section.GetComputedInlineDiffFor $line}}</span></td> | |||||
</tr> | |||||
{{end}} | |||||
{{end}} |
@@ -81,6 +81,15 @@ | |||||
{{else}} | {{else}} | ||||
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}"> | <div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}"> | ||||
<h4 class="ui top attached normal header"> | <h4 class="ui top attached normal header"> | ||||
{{$isImage := false}} | |||||
{{if $file.IsDeleted}} | |||||
{{$isImage = (call $.IsImageFileInBase $file.Name)}} | |||||
{{else}} | |||||
{{$isImage = (call $.IsImageFileInHead $file.Name)}} | |||||
{{end}} | |||||
{{if or (not $file.IsBin) $isImage}} | |||||
<i class="ui fold-code grey fa fa-chevron-down"></i> | |||||
{{end}} | |||||
<div class="diff-counter count"> | <div class="diff-counter count"> | ||||
{{if $file.IsBin}} | {{if $file.IsBin}} | ||||
{{$.i18n.Tr "repo.diff.bin"}} | {{$.i18n.Tr "repo.diff.bin"}} | ||||
@@ -104,12 +113,6 @@ | |||||
</h4> | </h4> | ||||
<div class="ui attached unstackable table segment"> | <div class="ui attached unstackable table segment"> | ||||
{{if ne $file.Type 4}} | {{if ne $file.Type 4}} | ||||
{{$isImage := false}} | |||||
{{if $file.IsDeleted}} | |||||
{{$isImage = (call $.IsImageFileInBase $file.Name)}} | |||||
{{else}} | |||||
{{$isImage = (call $.IsImageFileInHead $file.Name)}} | |||||
{{end}} | |||||
<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}"> | <div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}"> | ||||
<table> | <table> | ||||
<tbody> | <tbody> | ||||
@@ -121,12 +124,27 @@ | |||||
{{range $j, $section := $file.Sections}} | {{range $j, $section := $file.Sections}} | ||||
{{range $k, $line := $section.Lines}} | {{range $k, $line := $section.Lines}} | ||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | <tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | ||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | |||||
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | |||||
<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | |||||
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | |||||
<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
{{if eq .GetType 4}} | |||||
<td class="lines-num lines-num-old"> | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | |||||
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=down" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }} | |||||
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=up" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 2)}} | |||||
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.RepoLink}}/blob_excerpt/{{$.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=split&direction=" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
</td> | |||||
<td colspan="5" class="lines-code lines-code-old "><span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</span></td> | |||||
{{else}} | |||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | |||||
<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | |||||
<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | |||||
<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | |||||
<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td> | |||||
{{end}} | |||||
</tr> | </tr> | ||||
{{if gt (len $line.Comments) 0}} | {{if gt (len $line.Comments) 0}} | ||||
<tr class="add-code-comment"> | <tr class="add-code-comment"> | ||||
@@ -4,9 +4,17 @@ | |||||
{{range $k, $line := $section.Lines}} | {{range $k, $line := $section.Lines}} | ||||
<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | <tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | ||||
{{if eq .GetType 4}} | {{if eq .GetType 4}} | ||||
<td colspan="2" class="lines-num"> | |||||
{{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}} | |||||
</td> | |||||
<td colspan="2" class="lines-num"> | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | |||||
<i class="ui blob-excerpt fa fa-caret-down" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=down" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4) }} | |||||
<i class="ui blob-excerpt fa fa-caret-up" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=up" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
{{if or (eq $line.GetExpandDirection 2)}} | |||||
<i class="ui blob-excerpt octicon octicon-fold" data-url="{{$.root.RepoLink}}/blob_excerpt/{{$.root.AfterCommitID}}" data-query="{{$line.GetBlobExcerptQuery}}&style=unified&direction=" data-anchor="diff-{{Sha1 $file.Name}}K{{$line.SectionInfo.RightIdx}}"></i> | |||||
{{end}} | |||||
</td> | |||||
{{else}} | {{else}} | ||||
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | <td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | ||||
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | ||||
@@ -1852,6 +1852,27 @@ function initCodeView() { | |||||
} | } | ||||
}).trigger('hashchange'); | }).trigger('hashchange'); | ||||
} | } | ||||
$('.ui.fold-code').on('click', (e) => { | |||||
const $foldButton = $(e.target); | |||||
if ($foldButton.hasClass('fa-chevron-down')) { | |||||
$(e.target).parent().next().slideUp('fast', () => { | |||||
$foldButton.removeClass('fa-chevron-down').addClass('fa-chevron-right'); | |||||
}); | |||||
} else { | |||||
$(e.target).parent().next().slideDown('fast', () => { | |||||
$foldButton.removeClass('fa-chevron-right').addClass('fa-chevron-down'); | |||||
}); | |||||
} | |||||
}); | |||||
function insertBlobExcerpt(e) { | |||||
const $blob = $(e.target); | |||||
const $row = $blob.parent().parent(); | |||||
$.get(`${$blob.data('url')}?${$blob.data('query')}&anchor=${$blob.data('anchor')}`, (blob) => { | |||||
$row.replaceWith(blob); | |||||
$(`[data-anchor="${$blob.data('anchor')}"]`).on('click', (e) => { insertBlobExcerpt(e); }); | |||||
}); | |||||
} | |||||
$('.ui.blob-excerpt').on('click', (e) => { insertBlobExcerpt(e); }); | |||||
} | } | ||||
function initU2FAuth() { | function initU2FAuth() { | ||||
@@ -2438,6 +2438,10 @@ tbody.commit-list { | |||||
padding-bottom: 8px; | padding-bottom: 8px; | ||||
} | } | ||||
td.blob-excerpt { | |||||
background-color: #fafafa; | |||||
} | |||||
.issue-keyword { | .issue-keyword { | ||||
border-bottom: 1px dotted #959da5; | border-bottom: 1px dotted #959da5; | ||||
display: inline-block; | display: inline-block; | ||||
@@ -108,3 +108,26 @@ | |||||
font: 12px @monospaced-fonts, monospace; | font: 12px @monospaced-fonts, monospace; | ||||
color: rgba(0, 0, 0, 0.87); | color: rgba(0, 0, 0, 0.87); | ||||
} | } | ||||
.ui.fold-code { | |||||
margin-right: 1em; | |||||
padding-left: 5px; | |||||
cursor: pointer; | |||||
width: 22px; | |||||
font-size: 12px; | |||||
} | |||||
.ui.fold-code:hover { | |||||
color: #428bca; | |||||
} | |||||
.ui.blob-excerpt { | |||||
display: block; | |||||
line-height: 20px; | |||||
font-size: 16px; | |||||
cursor: pointer; | |||||
} | |||||
.ui.blob-excerpt:hover { | |||||
color: #428bca; | |||||
} |