2017-10-27 09:10:54 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package search
import (
"bytes"
2018-01-23 00:28:16 +03:00
"html"
2017-10-27 09:10:54 +03:00
gotemplate "html/template"
"strings"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/indexer"
"code.gitea.io/gitea/modules/util"
)
// Result a search result to display
type Result struct {
2018-03-16 17:04:33 +03:00
RepoID int64
2017-10-27 09:10:54 +03:00
Filename string
HighlightClass string
LineNumbers [ ] int
FormattedLines gotemplate . HTML
}
func indices ( content string , selectionStartIndex , selectionEndIndex int ) ( int , int ) {
startIndex := selectionStartIndex
numLinesBefore := 0
for ; startIndex > 0 ; startIndex -- {
if content [ startIndex - 1 ] == '\n' {
if numLinesBefore == 1 {
break
}
numLinesBefore ++
}
}
endIndex := selectionEndIndex
numLinesAfter := 0
for ; endIndex < len ( content ) ; endIndex ++ {
if content [ endIndex ] == '\n' {
if numLinesAfter == 1 {
break
}
numLinesAfter ++
}
}
return startIndex , endIndex
}
func writeStrings ( buf * bytes . Buffer , strs ... string ) error {
for _ , s := range strs {
_ , err := buf . WriteString ( s )
if err != nil {
return err
}
}
return nil
}
func searchResult ( result * indexer . RepoSearchResult , startIndex , endIndex int ) ( * Result , error ) {
startLineNum := 1 + strings . Count ( result . Content [ : startIndex ] , "\n" )
var formattedLinesBuffer bytes . Buffer
contentLines := strings . SplitAfter ( result . Content [ startIndex : endIndex ] , "\n" )
lineNumbers := make ( [ ] int , len ( contentLines ) )
index := startIndex
for i , line := range contentLines {
var err error
if index < result . EndIndex &&
result . StartIndex < index + len ( line ) &&
result . StartIndex < result . EndIndex {
openActiveIndex := util . Max ( result . StartIndex - index , 0 )
closeActiveIndex := util . Min ( result . EndIndex - index , len ( line ) )
err = writeStrings ( & formattedLinesBuffer ,
` <li> ` ,
2018-01-23 00:28:16 +03:00
html . EscapeString ( line [ : openActiveIndex ] ) ,
2017-10-27 09:10:54 +03:00
` <span class='active'> ` ,
2018-01-23 00:28:16 +03:00
html . EscapeString ( line [ openActiveIndex : closeActiveIndex ] ) ,
2017-10-27 09:10:54 +03:00
` </span> ` ,
2018-01-23 00:28:16 +03:00
html . EscapeString ( line [ closeActiveIndex : ] ) ,
2017-10-27 09:10:54 +03:00
` </li> ` ,
)
} else {
err = writeStrings ( & formattedLinesBuffer ,
` <li> ` ,
2018-01-23 00:28:16 +03:00
html . EscapeString ( line ) ,
2017-10-27 09:10:54 +03:00
` </li> ` ,
)
}
if err != nil {
return nil , err
}
lineNumbers [ i ] = startLineNum + i
index += len ( line )
}
return & Result {
2018-03-16 17:04:33 +03:00
RepoID : result . RepoID ,
2017-10-27 09:10:54 +03:00
Filename : result . Filename ,
HighlightClass : highlight . FileNameToHighlightClass ( result . Filename ) ,
LineNumbers : lineNumbers ,
FormattedLines : gotemplate . HTML ( formattedLinesBuffer . String ( ) ) ,
} , nil
}
// PerformSearch perform a search on a repository
2018-03-16 17:04:33 +03:00
func PerformSearch ( repoIDs [ ] int64 , keyword string , page , pageSize int ) ( int , [ ] * Result , error ) {
2017-10-27 09:10:54 +03:00
if len ( keyword ) == 0 {
return 0 , nil , nil
}
2018-03-16 17:04:33 +03:00
total , results , err := indexer . SearchRepoByKeyword ( repoIDs , keyword , page , pageSize )
2017-10-27 09:10:54 +03:00
if err != nil {
return 0 , nil , err
}
displayResults := make ( [ ] * Result , len ( results ) )
for i , result := range results {
startIndex , endIndex := indices ( result . Content , result . StartIndex , result . EndIndex )
displayResults [ i ] , err = searchResult ( result , startIndex , endIndex )
if err != nil {
return 0 , nil , err
}
}
return int ( total ) , displayResults , nil
}