History

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

Benno committed 6 years ago

Commit 149a9df1 parent 42ada7416 files · +460 −36Browse files

Showing the changes to services/gitdiff/gitdiff.go (1 of 16 files). Show all

Unified · Split

services/gitdiff/gitdiff.go+163−17
@@ -13,6 +13,7 @@ import ( · 12 unmodified lines
13 "html/template"13 "html/template"
14 "io"14 "io"
15 "io/ioutil"15 "io/ioutil"
16 "net/url"
16 "os"17 "os"
17 "os/exec"18 "os/exec"
18 "regexp"19 "regexp"
@@ -56,15 +57,42 @@ const ( · 37 unmodified lines
56 DiffFileRename57 DiffFileRename
57)58)
58 59
60// DiffLineExpandDirection represents the DiffLineSection expand direction
61type DiffLineExpandDirection uint8
62
63// DiffLineExpandDirection possible values.
64const (
65 DiffLineExpandNone DiffLineExpandDirection = iota + 1
66 DiffLineExpandSingle
67 DiffLineExpandUpDown
68 DiffLineExpandUp
69 DiffLineExpandDown
70)
71
59// DiffLine represents a line difference in a DiffSection.72// DiffLine represents a line difference in a DiffSection.
60type DiffLine struct {73type DiffLine struct {
61 LeftIdx int74 LeftIdx int
62 RightIdx int75 RightIdx int
63 Type DiffLineType76 Type DiffLineType
64 Content string77 Content string
65 Comments []*models.Comment78 Comments []*models.Comment
79 SectionInfo *DiffLineSectionInfo
80}
81
82// DiffLineSectionInfo represents diff line section meta data
83type DiffLineSectionInfo struct {
84 Path string
85 LastLeftIdx int
86 LastRightIdx int
87 LeftIdx int
88 RightIdx int
89 LeftHunkSize int
90 RightHunkSize int
66}91}
67 92
93// BlobExceprtChunkSize represent max lines of excerpt
94const BlobExceprtChunkSize = 20
95
68// GetType returns the type of a DiffLine.96// GetType returns the type of a DiffLine.
69func (d *DiffLine) GetType() int {97func (d *DiffLine) GetType() int {
70 return int(d.Type)98 return int(d.Type)
@@ -91,6 +119,71 @@ func (d *DiffLine) GetLineTypeMarker() string { · 20 unmodified lines
91 return ""119 return ""
92}120}
93 121
122// GetBlobExcerptQuery builds query string to get blob excerpt
123func (d *DiffLine) GetBlobExcerptQuery() string {
124 query := fmt.Sprintf(
125 "last_left=%d&last_right=%d&"+
126 "left=%d&right=%d&"+
127 "left_hunk_size=%d&right_hunk_size=%d&"+
128 "path=%s",
129 d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx,
130 d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx,
131 d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize,
132 url.QueryEscape(d.SectionInfo.Path))
133 return query
134}
135
136// GetExpandDirection gets DiffLineExpandDirection
137func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
138 if d.Type != DiffLineSection || d.SectionInfo == nil || d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx <= 1 {
139 return DiffLineExpandNone
140 }
141 if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 {
142 return DiffLineExpandUp
143 } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExceprtChunkSize && d.SectionInfo.RightHunkSize > 0 {
144 return DiffLineExpandUpDown
145 } else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 {
146 return DiffLineExpandDown
147 }
148 return DiffLineExpandSingle
149}
150
151func getDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo {
152 var (
153 leftLine int
154 leftHunk int
155 rightLine int
156 righHunk int
157 )
158 ss := strings.Split(line, "@@")
159 ranges := strings.Split(ss[1][1:], " ")
160 leftRange := strings.Split(ranges[0], ",")
161 leftLine, _ = com.StrTo(leftRange[0][1:]).Int()
162 if len(leftRange) > 1 {
163 leftHunk, _ = com.StrTo(leftRange[1]).Int()
164 }
165 if len(ranges) > 1 {
166 rightRange := strings.Split(ranges[1], ",")
167 rightLine, _ = com.StrTo(rightRange[0]).Int()
168 if len(rightRange) > 1 {
169 righHunk, _ = com.StrTo(rightRange[1]).Int()
170 }
171 } else {
172 log.Warn("Parse line number failed: %v", line)
173 rightLine = leftLine
174 righHunk = leftHunk
175 }
176 return &DiffLineSectionInfo{
177 Path: curFile.Name,
178 LastLeftIdx: lastLeftIdx,
179 LastRightIdx: lastRightIdx,
180 LeftIdx: leftLine,
181 RightIdx: rightLine,
182 LeftHunkSize: leftHunk,
183 RightHunkSize: righHunk,
184 }
185}
186
94// escape a line's content or return <br> needed for copy/paste purposes187// escape a line's content or return <br> needed for copy/paste purposes
95func getLineContent(content string) string {188func getLineContent(content string) string {
96 if len(content) > 0 {189 if len(content) > 0 {
@@ -248,6 +341,53 @@ func (diffFile *DiffFile) GetHighlightClass() string { · 151 unmodified lines
248 return highlight.FileNameToHighlightClass(diffFile.Name)341 return highlight.FileNameToHighlightClass(diffFile.Name)
249}342}
250 343
344// GetTailSection creates a fake DiffLineSection if the last section is not the end of the file
345func (diffFile *DiffFile) GetTailSection(gitRepo *git.Repository, leftCommitID, rightCommitID string) *DiffSection {
346 if diffFile.Type != DiffFileChange || diffFile.IsBin || diffFile.IsLFSFile {
347 return nil
348 }
349 leftCommit, err := gitRepo.GetCommit(leftCommitID)
350 if err != nil {
351 return nil
352 }
353 rightCommit, err := gitRepo.GetCommit(rightCommitID)
354 if err != nil {
355 return nil
356 }
357 lastSection := diffFile.Sections[len(diffFile.Sections)-1]
358 lastLine := lastSection.Lines[len(lastSection.Lines)-1]
359 leftLineCount := getCommitFileLineCount(leftCommit, diffFile.Name)
360 rightLineCount := getCommitFileLineCount(rightCommit, diffFile.Name)
361 if leftLineCount <= lastLine.LeftIdx || rightLineCount <= lastLine.RightIdx {
362 return nil
363 }
364 tailDiffLine := &DiffLine{
365 Type: DiffLineSection,
366 Content: " ",
367 SectionInfo: &DiffLineSectionInfo{
368 Path: diffFile.Name,
369 LastLeftIdx: lastLine.LeftIdx,
370 LastRightIdx: lastLine.RightIdx,
371 LeftIdx: leftLineCount,
372 RightIdx: rightLineCount,
373 }}
374 tailSection := &DiffSection{Lines: []*DiffLine{tailDiffLine}}
375 return tailSection
376
377}
378
379func getCommitFileLineCount(commit *git.Commit, filePath string) int {
380 blob, err := commit.GetBlobByPath(filePath)
381 if err != nil {
382 return 0
383 }
384 lineCount, err := blob.GetBlobLineCount()
385 if err != nil {
386 return 0
387 }
388 return lineCount
389}
390
251// Diff represents a difference between two git trees.391// Diff represents a difference between two git trees.
252type Diff struct {392type Diff struct {
253 TotalAddition, TotalDeletion int393 TotalAddition, TotalDeletion int
@@ -510,19 +650,16 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D · 256 unmodified lines
510 case line[0] == '@':650 case line[0] == '@':
511 curSection = &DiffSection{}651 curSection = &DiffSection{}
512 curFile.Sections = append(curFile.Sections, curSection)652 curFile.Sections = append(curFile.Sections, curSection)
513 ss := strings.Split(line, "@@")653 lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1)
514 diffLine := &DiffLine{Type: DiffLineSection, Content: line}654 diffLine := &DiffLine{
515 curSection.Lines = append(curSection.Lines, diffLine)655 Type: DiffLineSection,
516 656 Content: line,
517 // Parse line number.657 SectionInfo: lineSectionInfo,
518 ranges := strings.Split(ss[1][1:], " ")
519 leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int()
520 if len(ranges) > 1 {
521 rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int()
522 } else {
523 log.Warn("Parse line number failed: %v", line)
524 rightLine = leftLine
525 }658 }
659 curSection.Lines = append(curSection.Lines, diffLine)
660 // update line number.
661 leftLine = lineSectionInfo.LeftIdx
662 rightLine = lineSectionInfo.RightIdx
526 continue663 continue
527 case line[0] == '+':664 case line[0] == '+':
528 curFile.Addition++665 curFile.Addition++
@@ -599,6 +736,8 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D · 70 unmodified lines
599 break736 break
600 }737 }
601 curFileLinesCount = 0738 curFileLinesCount = 0
739 leftLine = 1
740 rightLine = 1
602 curFileLFSPrefix = false741 curFileLFSPrefix = false
603 742
604 // Check file diff type and is submodule.743 // Check file diff type and is submodule.
@@ -701,6 +840,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID · 96 unmodified lines
701 diffArgs = append(diffArgs, actualBeforeCommitID)840 diffArgs = append(diffArgs, actualBeforeCommitID)
702 diffArgs = append(diffArgs, afterCommitID)841 diffArgs = append(diffArgs, afterCommitID)
703 cmd = exec.Command(git.GitExecutable, diffArgs...)842 cmd = exec.Command(git.GitExecutable, diffArgs...)
843 beforeCommitID = actualBeforeCommitID
704 }844 }
705 cmd.Dir = repoPath845 cmd.Dir = repoPath
706 cmd.Stderr = os.Stderr846 cmd.Stderr = os.Stderr
@@ -721,6 +861,12 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID · 14 unmodified lines
721 if err != nil {861 if err != nil {
722 return nil, fmt.Errorf("ParsePatch: %v", err)862 return nil, fmt.Errorf("ParsePatch: %v", err)
723 }863 }
864 for _, diffFile := range diff.Files {
865 tailSection := diffFile.GetTailSection(gitRepo, beforeCommitID, afterCommitID)
866 if tailSection != nil {
867 diffFile.Sections = append(diffFile.Sections, tailSection)
868 }
869 }
724 870
725 if err = cmd.Wait(); err != nil {871 if err = cmd.Wait(); err != nil {
726 return nil, fmt.Errorf("Wait: %v", err)872 return nil, fmt.Errorf("Wait: %v", err)