diff options
author | Ethan Koenig <ethantkoenig@gmail.com> | 2017-10-26 23:10:54 -0700 |
---|---|---|
committer | Lauris BH <lauris@nix.lv> | 2017-10-27 09:10:54 +0300 |
commit | 5866eb23217de4d29b181e30c26cee28ebc6aedc (patch) | |
tree | f8f67462544c709e8dd6988ca4d55a22cfc3a22c /modules/search | |
parent | 762f1d7237de5727815ebda9593f7f9a20a5a077 (diff) | |
download | gitea-5866eb23217de4d29b181e30c26cee28ebc6aedc.tar.gz gitea-5866eb23217de4d29b181e30c26cee28ebc6aedc.zip |
Code/repo search (#2582)
Indexed search of repository contents (for default branch only)
Diffstat (limited to 'modules/search')
-rw-r--r-- | modules/search/search.go | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/modules/search/search.go b/modules/search/search.go new file mode 100644 index 0000000000..1c13f11f8f --- /dev/null +++ b/modules/search/search.go @@ -0,0 +1,128 @@ +// 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" + 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 { + 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>`, + line[:openActiveIndex], + `<span class='active'>`, + line[openActiveIndex:closeActiveIndex], + `</span>`, + line[closeActiveIndex:], + `</li>`, + ) + } else { + err = writeStrings(&formattedLinesBuffer, + `<li>`, + line, + `</li>`, + ) + } + if err != nil { + return nil, err + } + + lineNumbers[i] = startLineNum + i + index += len(line) + } + return &Result{ + Filename: result.Filename, + HighlightClass: highlight.FileNameToHighlightClass(result.Filename), + LineNumbers: lineNumbers, + FormattedLines: gotemplate.HTML(formattedLinesBuffer.String()), + }, nil +} + +// PerformSearch perform a search on a repository +func PerformSearch(repoID int64, keyword string, page, pageSize int) (int, []*Result, error) { + if len(keyword) == 0 { + return 0, nil, nil + } + + total, results, err := indexer.SearchRepoByKeyword(repoID, keyword, page, pageSize) + 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 +} |