diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index e19e22eea..2ddc2397f 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -16,14 +16,18 @@ import ( // Result a search result to display type Result struct { - RepoID int64 - Filename string - CommitID string - UpdatedUnix timeutil.TimeStamp - Language string - Color string - LineNumbers []int - FormattedLines template.HTML + RepoID int64 + Filename string + CommitID string + UpdatedUnix timeutil.TimeStamp + Language string + Color string + Lines []ResultLine +} + +type ResultLine struct { + Num int + FormattedContent template.HTML } type SearchResultLanguages = internal.SearchResultLanguages @@ -70,7 +74,7 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res var formattedLinesBuffer bytes.Buffer contentLines := strings.SplitAfter(result.Content[startIndex:endIndex], "\n") - lineNumbers := make([]int, len(contentLines)) + lines := make([]ResultLine, 0, len(contentLines)) index := startIndex for i, line := range contentLines { var err error @@ -93,21 +97,29 @@ func searchResult(result *internal.SearchResult, startIndex, endIndex int) (*Res return nil, err } - lineNumbers[i] = startLineNum + i + lines = append(lines, ResultLine{Num: startLineNum + i}) index += len(line) } - highlighted, _ := highlight.Code(result.Filename, "", formattedLinesBuffer.String()) + // we should highlight the whole code block first, otherwise it doesn't work well with multiple line highlighting + hl, _ := highlight.Code(result.Filename, "", formattedLinesBuffer.String()) + highlightedLines := strings.Split(string(hl), "\n") + + // The lines outputted by highlight.Code might not match the original lines, because "highlight" removes the last `\n` + lines = lines[:min(len(highlightedLines), len(lines))] + highlightedLines = highlightedLines[:len(lines)] + for i := 0; i < len(lines); i++ { + lines[i].FormattedContent = template.HTML(highlightedLines[i]) + } return &Result{ - RepoID: result.RepoID, - Filename: result.Filename, - CommitID: result.CommitID, - UpdatedUnix: result.UpdatedUnix, - Language: result.Language, - Color: result.Color, - LineNumbers: lineNumbers, - FormattedLines: highlighted, + RepoID: result.RepoID, + Filename: result.Filename, + CommitID: result.CommitID, + UpdatedUnix: result.UpdatedUnix, + Language: result.Language, + Color: result.Color, + Lines: lines, }, nil } diff --git a/services/repository/files/search.go b/services/repository/files/search.go index f8317c489..09c3ab5bf 100644 --- a/services/repository/files/search.go +++ b/services/repository/files/search.go @@ -16,14 +16,18 @@ import ( ) type Result struct { - RepoID int64 // ignored - Filename string - CommitID string // branch - UpdatedUnix timeutil.TimeStamp // ignored - Language string - Color string - LineNumbers []int64 - FormattedLines template.HTML + RepoID int64 // ignored + Filename string + CommitID string // branch + UpdatedUnix timeutil.TimeStamp // ignored + Language string + Color string + Lines []ResultLine +} + +type ResultLine struct { + Num int64 + FormattedContent template.HTML } const pHEAD = "HEAD:" @@ -46,7 +50,8 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin "-n", // line nums "-i", // ignore case "--full-name", // full file path, rel to repo - //"--column", // for adding better highlighting support + //"--column", // for adding better highlighting support + "-e", // for queries starting with "-" ). AddDynamicArguments(keyword). AddArguments("HEAD"). @@ -57,6 +62,8 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin for _, block := range strings.Split(stdout, "\n\n") { res := Result{CommitID: repo.DefaultBranch} + + linenum := []int64{} code := []string{} for _, line := range strings.Split(block, "\n") { @@ -71,18 +78,32 @@ func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword strin continue } - res.LineNumbers = append(res.LineNumbers, i) + linenum = append(linenum, i) code = append(code, after) } } - if res.Filename == "" || len(code) == 0 || len(res.LineNumbers) == 0 { + if res.Filename == "" || len(code) == 0 || len(linenum) == 0 { continue } - res.FormattedLines, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) + var hl template.HTML + + hl, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) res.Color = enry.GetColor(res.Language) + hlCode := strings.Split(string(hl), "\n") + n := min(len(hlCode), len(linenum)) + + res.Lines = make([]ResultLine, n) + + for i := 0; i < n; i++ { + res.Lines[i] = ResultLine{ + Num: linenum[i], + FormattedContent: template.HTML(hlCode[i]), + } + } + data = append(data, &res) } diff --git a/services/repository/files/search_test.go b/services/repository/files/search_test.go index 959ddaa9f..2f2f87368 100644 --- a/services/repository/files/search_test.go +++ b/services/repository/files/search_test.go @@ -25,14 +25,16 @@ func TestNewRepoGrep(t *testing.T) { expected := []*Result{ { - RepoID: 0, - Filename: "README.md", - CommitID: "master", - UpdatedUnix: 0, - Language: "Markdown", - Color: "#083fa1", - LineNumbers: []int64{2, 3}, - FormattedLines: "\nDescription for repo1", + RepoID: 0, + Filename: "README.md", + CommitID: "master", + UpdatedUnix: 0, + Language: "Markdown", + Color: "#083fa1", + Lines: []ResultLine{ + {Num: 2, FormattedContent: ""}, + {Num: 3, FormattedContent: "Description for repo1"}, + }, }, } diff --git a/templates/code/searchresults.tmpl b/templates/code/searchresults.tmpl index bb21a5e0d..dca7dea7d 100644 --- a/templates/code/searchresults.tmpl +++ b/templates/code/searchresults.tmpl @@ -22,20 +22,7 @@ <a role="button" class="ui basic tiny button" rel="nofollow" href="{{$repo.Link}}/src/commit/{{$result.CommitID | PathEscape}}/{{.Filename | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a> </h4> <div class="ui attached table segment"> - <div class="file-body file-code code-view"> - <table> - <tbody> - <tr> - <td class="lines-num"> - {{range .LineNumbers}} - <a href="{{$repo.Link}}/src/commit/{{$result.CommitID | PathEscape}}/{{$result.Filename | PathEscapeSegments}}#L{{.}}"><span>{{.}}</span></a> - {{end}} - </td> - <td class="lines-code chroma"><code class="code-inner">{{.FormattedLines}}</code></td> - </tr> - </tbody> - </table> - </div> + {{template "shared/searchfile" dict "RepoLink" $repo.Link "IsIndexer" true "SearchResult" .}} </div> {{template "shared/searchbottom" dict "root" $ "result" .}} </div> diff --git a/templates/repo/search.tmpl b/templates/repo/search.tmpl index d0d376f1c..8eaecab68 100644 --- a/templates/repo/search.tmpl +++ b/templates/repo/search.tmpl @@ -46,20 +46,7 @@ <a role="button" class="ui basic tiny button" rel="nofollow" href="{{$.SourcePath}}/src/{{if $.CodeIndexerEnabled}}commit{{else}}branch{{end}}/{{PathEscape $result.CommitID}}/{{PathEscapeSegments .Filename}}">{{ctx.Locale.Tr "repo.diff.view_file"}}</a> </h4> <div class="ui attached table segment"> - <div class="file-body file-code code-view"> - <table> - <tbody> - <tr> - <td class="lines-num"> - {{range .LineNumbers}} - <a href="{{$.SourcePath}}/src/{{if $.CodeIndexerEnabled}}commit{{else}}branch{{end}}/{{PathEscape $result.CommitID}}/{{PathEscapeSegments $result.Filename}}#L{{.}}"><span>{{.}}</span></a> - {{end}} - </td> - <td class="lines-code chroma"><code class="code-inner">{{.FormattedLines}}</code></td> - </tr> - </tbody> - </table> - </div> + {{template "shared/searchfile" dict "RepoLink" $.SourcePath "IsIndexer" $.CodeIndexerEnabled "SearchResult" .}} </div> {{template "shared/searchbottom" dict "root" $ "result" .}} </div> diff --git a/templates/shared/searchfile.tmpl b/templates/shared/searchfile.tmpl new file mode 100644 index 000000000..6ffcd7118 --- /dev/null +++ b/templates/shared/searchfile.tmpl @@ -0,0 +1,14 @@ +<div class="file-body file-code code-view"> + <table> + <tbody> + {{range .SearchResult.Lines}} + <tr> + <td class="lines-num"> + <a href="{{$.RepoLink}}/src/{{if not $.IsIndexer}}branch{{else}}commit{{end}}/{{PathEscape $.SearchResult.CommitID}}/{{PathEscapeSegments $.SearchResult.Filename}}#L{{.Num}}"><span>{{.Num}}</span></a> + </td> + <td class="lines-code chroma"><code class="code-inner">{{.FormattedContent}}</code></td> + </tr> + {{end}} + </tbody> + </table> +</div>