mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 19:11:25 +01:00
Cut issue, pr, label, milestone and comment examples to at most three, drop doc comments that only restate the name, fold the comment and attachment argument lookups into one closure each, and inline label helpers with one caller. Signed-off-by: bircni <bircni@icloud.com>
126 lines
5.5 KiB
Go
126 lines
5.5 KiB
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package app
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.dev/tea/internal/clierr"
|
|
"gitea.dev/tea/internal/services"
|
|
"gitea.dev/tea/internal/terminal"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func (a *application) discussionSearchCommand(pulls bool) *cobra.Command {
|
|
noun, nouns, alias := "issue", "issues", "issues"
|
|
if pulls {
|
|
noun, nouns, alias = "pr", "pull requests", "prs"
|
|
}
|
|
var opts services.DiscussionSearchOptions
|
|
var labels []string
|
|
var updated timeRange
|
|
opts.Pulls = pulls
|
|
example := fmt.Sprintf(`$ tea search %[1]s "login error"
|
|
# Search one repository; its labels and milestones are checked
|
|
$ tea search %[1]s crash -R alice/project --label bug --milestone v1.0`, noun)
|
|
if pulls {
|
|
example += "\n$ tea search pr --review-requested"
|
|
}
|
|
command := &cobra.Command{Use: noun + " [<query>...]", Aliases: []string{alias}, Short: "Search " + nouns, Long: fmt.Sprintf(`Search the %[1]s of every repository you can access on the instance, or of one
|
|
repository with --repo. Words of the query match titles and bodies.`, nouns), Example: example, RunE: func(cmd *cobra.Command, args []string) error {
|
|
if opts.Author != "" && opts.Authored {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--author and --authored are mutually exclusive"))
|
|
}
|
|
var err error
|
|
if opts.Since, opts.Before, err = updated.parse(a.deps.Now()); err != nil {
|
|
return err
|
|
}
|
|
if err := requireNames("label", "", labels); err != nil {
|
|
return err
|
|
}
|
|
milestones, err := nameFilter("milestone", opts.Milestones)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target, err := a.resolve(cmd.Context(), false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if target.Repo != "" && opts.Owner != "" && !strings.EqualFold(strings.SplitN(target.Repo, "/", 2)[0], opts.Owner) {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--owner conflicts with selected repository"))
|
|
}
|
|
if opts.Team != "" && opts.Owner == "" {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--team requires --owner"))
|
|
}
|
|
if (opts.Team != "" || opts.ReviewRequested || opts.Reviewed) && target.Repo != "" {
|
|
return clierr.Wrap(clierr.Usage, errors.New("--team, --review-requested and --reviewed require instance-wide search; omit --repo or TEA_REPO"))
|
|
}
|
|
opts.Author = target.expandMe(opts.Author)
|
|
if target.Repo != "" {
|
|
if opts.Labels, opts.Milestones, err = issueFilters(cmd, target, labels, milestones); err != nil {
|
|
return err
|
|
}
|
|
} else if len(labels)+len(milestones) > 0 {
|
|
opts.Labels, opts.Milestones = strings.Join(labels, ","), strings.Join(milestones, ",")
|
|
if _, err := fmt.Fprintln(cmd.ErrOrStderr(), "warning: instance-wide search ignores --label and --milestone values that match nothing; pass --repo to check them"); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
opts.Query = strings.Join(args, " ")
|
|
page, err := (services.Issues{Client: target.client, Repository: target.Repo}).SearchPage(cmd.Context(), opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
where := "on " + strings.TrimPrefix(strings.TrimPrefix(target.Host, "https://"), "http://")
|
|
if target.Repo != "" {
|
|
where = "in " + target.Repo
|
|
}
|
|
if opts.Query != "" {
|
|
where = fmt.Sprintf("matching %q %s", opts.Query, where)
|
|
}
|
|
what := nouns
|
|
if opts.State != "all" {
|
|
what = opts.State + " " + nouns
|
|
}
|
|
return writeTable(a, cmd, page.Items, listSpec[services.SearchItem]{
|
|
Columns: []terminal.Column{{Header: "Repo#ID"}, {Header: "Title", Flex: true}, {Header: "State"}, {Header: "Author"}, {Header: "Updated"}},
|
|
Row: searchRow, Empty: "no " + what + " " + where, Context: what + " " + where,
|
|
}, servicePage(page, opts.Page))
|
|
}}
|
|
flags := command.Flags()
|
|
pageFlags(command, &opts.Limit, &opts.Page)
|
|
enumFlag(command, &opts.State, "state", "s", "open", "Filter by state", "open", "closed", "all")
|
|
flags.StringVar(&opts.Owner, "owner", "", "Search only the repositories of this `owner`")
|
|
flags.StringVar(&opts.Team, "team", "", "Search only the repositories of this organization `team` (requires --owner)")
|
|
flags.StringVarP(&opts.Author, "author", "A", "", "Filter by author; @me for yourself")
|
|
flags.StringSliceVarP(&labels, "label", "l", nil, "Filter by label `names`")
|
|
flags.StringVarP(&opts.Milestones, "milestone", "m", "", "Filter by milestone `titles`, comma-separated")
|
|
updated.register(command, "since", "Updated since a date, timestamp, or duration ago", "until", "Updated before a date, timestamp, or duration ago")
|
|
flagAliases(command, map[string]string{"before": "until"})
|
|
flags.BoolVar(&opts.Assigned, "assigned", false, "Assigned to you")
|
|
flags.BoolVar(&opts.Authored, "authored", false, "Created by you")
|
|
flags.BoolVar(&opts.Mentioned, "mentioned", false, "Mentioning you")
|
|
if pulls {
|
|
flags.BoolVar(&opts.ReviewRequested, "review-requested", false, "Requesting your review (instance-wide search)")
|
|
flags.BoolVar(&opts.Reviewed, "reviewed", false, "Reviewed by you (instance-wide search)")
|
|
}
|
|
_ = command.RegisterFlagCompletionFunc("label", a.completeLabels())
|
|
_ = command.RegisterFlagCompletionFunc("milestone", commaSeparated(a.completeMilestones()))
|
|
_ = command.RegisterFlagCompletionFunc("author", a.completeUsers())
|
|
_ = command.RegisterFlagCompletionFunc("owner", a.completeOrgs())
|
|
return command
|
|
}
|
|
|
|
func searchRow(item services.SearchItem) []terminal.Cell {
|
|
return []terminal.Cell{
|
|
{Text: fmt.Sprintf("%s#%d", item.Repository, item.Number), Link: item.URL, Color: stateColors[item.State]},
|
|
{Text: item.Title},
|
|
{Text: item.State, Color: stateColors[item.State]},
|
|
{Text: item.Author},
|
|
{Time: terminal.ParseTime(item.UpdatedAt)},
|
|
}
|
|
}
|