mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Expand/Collapse Files and Blob Excerpt while Reviewing/Comparing code (#8924)
* 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 style
This commit is contained in:
		@@ -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}
 | 
				
			||||||
@@ -1069,3 +1070,7 @@ tbody.commit-list{vertical-align:baseline}
 | 
				
			|||||||
.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}
 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -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
 | 
						LeftIdx     int
 | 
				
			||||||
	RightIdx int
 | 
						RightIdx    int
 | 
				
			||||||
	Type     DiffLineType
 | 
						Type        DiffLineType
 | 
				
			||||||
	Content  string
 | 
						Content     string
 | 
				
			||||||
	Comments []*models.Comment
 | 
						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, "@@")
 | 
								lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1)
 | 
				
			||||||
			diffLine := &DiffLine{Type: DiffLineSection, Content: line}
 | 
								diffLine := &DiffLine{
 | 
				
			||||||
			curSection.Lines = append(curSection.Lines, diffLine)
 | 
									Type:        DiffLineSection,
 | 
				
			||||||
 | 
									Content:     line,
 | 
				
			||||||
			// Parse line number.
 | 
									SectionInfo: lineSectionInfo,
 | 
				
			||||||
			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
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										50
									
								
								templates/repo/diff/blob_excerpt.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								templates/repo/diff/blob_excerpt.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
																				{{if eq .GetType 4}}
 | 
				
			||||||
															<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-num lines-num-old">
 | 
				
			||||||
															<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>
 | 
																						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
				
			||||||
															<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>
 | 
																							<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>
 | 
				
			||||||
															<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
 | 
																						{{end}}
 | 
				
			||||||
															<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 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">
 | 
									<td colspan="2" class="lines-num">
 | 
				
			||||||
				{{/* {{if gt $j 0}}<span class="fold octicon octicon-fold"></span>{{end}} */}}
 | 
									{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }}
 | 
				
			||||||
			</td>
 | 
										<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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user