You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

search.go 3.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package code
  5. import (
  6. "bytes"
  7. "html"
  8. gotemplate "html/template"
  9. "strings"
  10. "code.gitea.io/gitea/modules/highlight"
  11. "code.gitea.io/gitea/modules/timeutil"
  12. "code.gitea.io/gitea/modules/util"
  13. )
  14. // Result a search result to display
  15. type Result struct {
  16. RepoID int64
  17. Filename string
  18. CommitID string
  19. UpdatedUnix timeutil.TimeStamp
  20. Language string
  21. Color string
  22. HighlightClass string
  23. LineNumbers []int
  24. FormattedLines gotemplate.HTML
  25. }
  26. func indices(content string, selectionStartIndex, selectionEndIndex int) (int, int) {
  27. startIndex := selectionStartIndex
  28. numLinesBefore := 0
  29. for ; startIndex > 0; startIndex-- {
  30. if content[startIndex-1] == '\n' {
  31. if numLinesBefore == 1 {
  32. break
  33. }
  34. numLinesBefore++
  35. }
  36. }
  37. endIndex := selectionEndIndex
  38. numLinesAfter := 0
  39. for ; endIndex < len(content); endIndex++ {
  40. if content[endIndex] == '\n' {
  41. if numLinesAfter == 1 {
  42. break
  43. }
  44. numLinesAfter++
  45. }
  46. }
  47. return startIndex, endIndex
  48. }
  49. func writeStrings(buf *bytes.Buffer, strs ...string) error {
  50. for _, s := range strs {
  51. _, err := buf.WriteString(s)
  52. if err != nil {
  53. return err
  54. }
  55. }
  56. return nil
  57. }
  58. func searchResult(result *SearchResult, startIndex, endIndex int) (*Result, error) {
  59. startLineNum := 1 + strings.Count(result.Content[:startIndex], "\n")
  60. var formattedLinesBuffer bytes.Buffer
  61. contentLines := strings.SplitAfter(result.Content[startIndex:endIndex], "\n")
  62. lineNumbers := make([]int, len(contentLines))
  63. index := startIndex
  64. for i, line := range contentLines {
  65. var err error
  66. if index < result.EndIndex &&
  67. result.StartIndex < index+len(line) &&
  68. result.StartIndex < result.EndIndex {
  69. openActiveIndex := util.Max(result.StartIndex-index, 0)
  70. closeActiveIndex := util.Min(result.EndIndex-index, len(line))
  71. err = writeStrings(&formattedLinesBuffer,
  72. `<li>`,
  73. html.EscapeString(line[:openActiveIndex]),
  74. `<span class='active'>`,
  75. html.EscapeString(line[openActiveIndex:closeActiveIndex]),
  76. `</span>`,
  77. html.EscapeString(line[closeActiveIndex:]),
  78. `</li>`,
  79. )
  80. } else {
  81. err = writeStrings(&formattedLinesBuffer,
  82. `<li>`,
  83. html.EscapeString(line),
  84. `</li>`,
  85. )
  86. }
  87. if err != nil {
  88. return nil, err
  89. }
  90. lineNumbers[i] = startLineNum + i
  91. index += len(line)
  92. }
  93. return &Result{
  94. RepoID: result.RepoID,
  95. Filename: result.Filename,
  96. CommitID: result.CommitID,
  97. UpdatedUnix: result.UpdatedUnix,
  98. Language: result.Language,
  99. Color: result.Color,
  100. HighlightClass: highlight.FileNameToHighlightClass(result.Filename),
  101. LineNumbers: lineNumbers,
  102. FormattedLines: gotemplate.HTML(formattedLinesBuffer.String()),
  103. }, nil
  104. }
  105. // PerformSearch perform a search on a repository
  106. func PerformSearch(repoIDs []int64, language, keyword string, page, pageSize int) (int, []*Result, []*SearchResultLanguages, error) {
  107. if len(keyword) == 0 {
  108. return 0, nil, nil, nil
  109. }
  110. total, results, resultLanguages, err := indexer.Search(repoIDs, language, keyword, page, pageSize)
  111. if err != nil {
  112. return 0, nil, nil, err
  113. }
  114. displayResults := make([]*Result, len(results))
  115. for i, result := range results {
  116. startIndex, endIndex := indices(result.Content, result.StartIndex, result.EndIndex)
  117. displayResults[i], err = searchResult(result, startIndex, endIndex)
  118. if err != nil {
  119. return 0, nil, nil, err
  120. }
  121. }
  122. return int(total), displayResults, resultLanguages, nil
  123. }