* 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
Showing the changes to services/gitdiff/gitdiff.go (1 of 16 files). Show all
Unified · Split
| @@ -13,6 +13,7 @@ import ( · 12 unmodified lines | ||
| 13 | 13 | "html/template" |
| 14 | 14 | "io" |
| 15 | 15 | "io/ioutil" |
| 16 | + "net/url" | |
| 16 | 17 | "os" |
| 17 | 18 | "os/exec" |
| 18 | 19 | "regexp" |
| @@ -56,15 +57,42 @@ const ( · 37 unmodified lines | ||
| 56 | 57 | DiffFileRename |
| 57 | 58 | ) |
| 58 | 59 | |
| 60 | +// DiffLineExpandDirection represents the DiffLineSection expand direction | |
| 61 | +type DiffLineExpandDirection uint8 | |
| 62 | + | |
| 63 | +// DiffLineExpandDirection possible values. | |
| 64 | +const ( | |
| 65 | + DiffLineExpandNone DiffLineExpandDirection = iota + 1 | |
| 66 | + DiffLineExpandSingle | |
| 67 | + DiffLineExpandUpDown | |
| 68 | + DiffLineExpandUp | |
| 69 | + DiffLineExpandDown | |
| 70 | +) | |
| 71 | + | |
| 59 | 72 | // DiffLine represents a line difference in a DiffSection. |
| 60 | 73 | type DiffLine struct { |
| 61 | − LeftIdx int | |
| 62 | − RightIdx int | |
| 63 | − Type DiffLineType | |
| 64 | − Content string | |
| 65 | − Comments []*models.Comment | |
| 74 | + LeftIdx int | |
| 75 | + RightIdx int | |
| 76 | + Type DiffLineType | |
| 77 | + Content string | |
| 78 | + Comments []*models.Comment | |
| 79 | + SectionInfo *DiffLineSectionInfo | |
| 80 | +} | |
| 81 | + | |
| 82 | +// DiffLineSectionInfo represents diff line section meta data | |
| 83 | +type 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 | |
| 94 | +const BlobExceprtChunkSize = 20 | |
| 95 | + | |
| 68 | 96 | // GetType returns the type of a DiffLine. |
| 69 | 97 | func (d *DiffLine) GetType() int { |
| 70 | 98 | return int(d.Type) |
| @@ -91,6 +119,71 @@ func (d *DiffLine) GetLineTypeMarker() string { · 20 unmodified lines | ||
| 91 | 119 | return "" |
| 92 | 120 | } |
| 93 | 121 | |
| 122 | +// GetBlobExcerptQuery builds query string to get blob excerpt | |
| 123 | +func (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 | |
| 137 | +func (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 | + | |
| 151 | +func 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 | 187 | // escape a line's content or return <br> needed for copy/paste purposes |
| 95 | 188 | func getLineContent(content string) string { |
| 96 | 189 | if len(content) > 0 { |
| @@ -248,6 +341,53 @@ func (diffFile *DiffFile) GetHighlightClass() string { · 151 unmodified lines | ||
| 248 | 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 | |
| 345 | +func (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 | + | |
| 379 | +func 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 | 391 | // Diff represents a difference between two git trees. |
| 252 | 392 | type Diff struct { |
| 253 | 393 | TotalAddition, TotalDeletion int |
| @@ -510,19 +650,16 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D · 256 unmodified lines | ||
| 510 | 650 | case line[0] == '@': |
| 511 | 651 | curSection = &DiffSection{} |
| 512 | 652 | curFile.Sections = append(curFile.Sections, curSection) |
| 513 | − ss := strings.Split(line, "@@") | |
| 514 | − diffLine := &DiffLine{Type: DiffLineSection, Content: line} | |
| 515 | − curSection.Lines = append(curSection.Lines, diffLine) | |
| 516 | − | |
| 517 | − // Parse line number. | |
| 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 | |
| 653 | + lineSectionInfo := getDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1) | |
| 654 | + diffLine := &DiffLine{ | |
| 655 | + Type: DiffLineSection, | |
| 656 | + Content: line, | |
| 657 | + SectionInfo: lineSectionInfo, | |
| 525 | 658 | } |
| 659 | + curSection.Lines = append(curSection.Lines, diffLine) | |
| 660 | + // update line number. | |
| 661 | + leftLine = lineSectionInfo.LeftIdx | |
| 662 | + rightLine = lineSectionInfo.RightIdx | |
| 526 | 663 | continue |
| 527 | 664 | case line[0] == '+': |
| 528 | 665 | curFile.Addition++ |
| @@ -599,6 +736,8 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D · 70 unmodified lines | ||
| 599 | 736 | break |
| 600 | 737 | } |
| 601 | 738 | curFileLinesCount = 0 |
| 739 | + leftLine = 1 | |
| 740 | + rightLine = 1 | |
| 602 | 741 | curFileLFSPrefix = false |
| 603 | 742 | |
| 604 | 743 | // Check file diff type and is submodule. |
| @@ -701,6 +840,7 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID · 96 unmodified lines | ||
| 701 | 840 | diffArgs = append(diffArgs, actualBeforeCommitID) |
| 702 | 841 | diffArgs = append(diffArgs, afterCommitID) |
| 703 | 842 | cmd = exec.Command(git.GitExecutable, diffArgs...) |
| 843 | + beforeCommitID = actualBeforeCommitID | |
| 704 | 844 | } |
| 705 | 845 | cmd.Dir = repoPath |
| 706 | 846 | cmd.Stderr = os.Stderr |
| @@ -721,6 +861,12 @@ func GetDiffRangeWithWhitespaceBehavior(repoPath, beforeCommitID, afterCommitID · 14 unmodified lines | ||
| 721 | 861 | if err != nil { |
| 722 | 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 | 871 | if err = cmd.Wait(); err != nil { |
| 726 | 872 | return nil, fmt.Errorf("Wait: %v", err) |