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 { 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:" 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 //"--column", // for adding better highlighting support "-e", // for queries starting with "-" ). 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} linenum := []int64{} 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 } linenum = append(linenum, i) code = append(code, after) } } if res.Filename == "" || len(code) == 0 || len(linenum) == 0 { continue } 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) } return data, nil }