* 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 | "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 | DiffFileRename | 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 | // DiffLine represents a line difference in a DiffSection. | 72 | // DiffLine represents a line difference in a DiffSection. |
| 60 | type DiffLine struct { | 73 | type DiffLine struct { |
| 61 | LeftIdx int | 74 | LeftIdx int |
| 62 | RightIdx int | 75 | RightIdx int |
| 63 | Type DiffLineType | 76 | Type DiffLineType |
| 64 | Content string | 77 | Content string |
| 65 | Comments []*models.Comment | 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 | // GetType returns the type of a DiffLine. | 96 | // GetType returns the type of a DiffLine. |
| 69 | func (d *DiffLine) GetType() int { | 97 | func (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 | ||
| 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 | // escape a line's content or return <br> needed for copy/paste purposes | 187 | // escape a line's content or return <br> needed for copy/paste purposes |
| 95 | func getLineContent(content string) string { | 188 | func 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 | ||
| 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 | // Diff represents a difference between two git trees. | 391 | // Diff represents a difference between two git trees. |
| 252 | type Diff struct { | 392 | type Diff struct { |
| 253 | TotalAddition, TotalDeletion int | 393 | 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 | continue | 663 | 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 | break | 736 | break |
| 600 | } | 737 | } |
| 601 | curFileLinesCount = 0 | 738 | curFileLinesCount = 0 |
| 739 | leftLine = 1 | ||
| 740 | rightLine = 1 | ||
| 602 | curFileLFSPrefix = false | 741 | 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 = repoPath | 845 | cmd.Dir = repoPath |
| 706 | cmd.Stderr = os.Stderr | 846 | 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) |