2024-02-20 11:05:42 +00:00
|
|
|
package files
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"html/template"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
|
|
"code.gitea.io/gitea/modules/git"
|
|
|
|
"code.gitea.io/gitea/modules/gitrepo"
|
|
|
|
"code.gitea.io/gitea/modules/highlight"
|
|
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
|
|
|
|
"github.com/go-enry/go-enry/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Result struct {
|
2024-03-10 15:35:30 +00:00
|
|
|
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
|
2024-02-20 11:05:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const pHEAD = "HEAD:"
|
|
|
|
|
|
|
|
func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword string) ([]*Result, error) {
|
|
|
|
t, _, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
data := []*Result{}
|
|
|
|
|
|
|
|
stdout, _, err := git.NewCommand(ctx,
|
|
|
|
"grep",
|
|
|
|
"-1", // n before and after lines
|
|
|
|
"-z",
|
|
|
|
"--heading",
|
|
|
|
"--break", // easier parsing
|
|
|
|
"--fixed-strings", // disallow regex for now
|
|
|
|
"-n", // line nums
|
|
|
|
"-i", // ignore case
|
|
|
|
"--full-name", // full file path, rel to repo
|
2024-03-10 15:35:30 +00:00
|
|
|
//"--column", // for adding better highlighting support
|
|
|
|
"-e", // for queries starting with "-"
|
2024-02-20 11:05:42 +00:00
|
|
|
).
|
|
|
|
AddDynamicArguments(keyword).
|
|
|
|
AddArguments("HEAD").
|
|
|
|
RunStdString(&git.RunOpts{Dir: t.Path})
|
|
|
|
if err != nil {
|
|
|
|
return data, nil // non zero exit code when there are no results
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, block := range strings.Split(stdout, "\n\n") {
|
|
|
|
res := Result{CommitID: repo.DefaultBranch}
|
2024-03-10 15:35:30 +00:00
|
|
|
|
|
|
|
linenum := []int64{}
|
2024-02-20 11:05:42 +00:00
|
|
|
code := []string{}
|
|
|
|
|
|
|
|
for _, line := range strings.Split(block, "\n") {
|
|
|
|
if strings.HasPrefix(line, pHEAD) {
|
|
|
|
res.Filename = strings.TrimPrefix(line, pHEAD)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if ln, after, ok := strings.Cut(line, "\x00"); ok {
|
|
|
|
i, err := strconv.ParseInt(ln, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-03-10 15:35:30 +00:00
|
|
|
linenum = append(linenum, i)
|
2024-02-20 11:05:42 +00:00
|
|
|
code = append(code, after)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-10 15:35:30 +00:00
|
|
|
if res.Filename == "" || len(code) == 0 || len(linenum) == 0 {
|
2024-02-20 11:05:42 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2024-03-10 15:35:30 +00:00
|
|
|
var hl template.HTML
|
|
|
|
|
|
|
|
hl, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n"))
|
2024-02-20 11:05:42 +00:00
|
|
|
res.Color = enry.GetColor(res.Language)
|
|
|
|
|
2024-03-10 15:35:30 +00:00
|
|
|
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]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-20 11:05:42 +00:00
|
|
|
data = append(data, &res)
|
|
|
|
}
|
|
|
|
|
|
|
|
return data, nil
|
|
|
|
}
|