@@ -35,7 +35,7 @@ type overviewResult struct {
3535
3636// overviewForm holds the raw inputs to the overview form.
3737type overviewForm struct {
38- Query string // the issue ID to lookup
38+ Query string // the issue ID to lookup, or golang/go#12345 or github.com/golang/go/issues/12345 form
3939 OverviewType string // the type of overview to generate
4040}
4141
@@ -90,6 +90,45 @@ func fmtTimeString(s string) string {
9090 return t .Format (time .DateOnly )
9191}
9292
93+ // parseIssueNumber parses the issue number from the given issue ID string.
94+ // The issue ID string can be in one of the following formats:
95+ // - "12345" (by default, assume it is a golang/go project's issue)
96+ // - "golang/go#12345"
97+ // - "github.com/golang/go/issues/12345" or "https://github.com/golang/go/issues/12345"
98+ // - "go.dev/issues/12345" or "https://go.dev/issues/12345"
99+ func parseIssueNumber (issueID string ) (project string , issue int64 , _ error ) {
100+ issueID = strings .TrimSpace (issueID )
101+ if issueID == "" {
102+ return "" , 0 , nil
103+ }
104+ split := func (q string ) (string , string ) {
105+ q = strings .TrimPrefix (q , "https://" )
106+ // recognize github.com/golang/go/issues/12345
107+ if proj , ok := strings .CutPrefix (q , "github.com/" ); ok {
108+ i := strings .LastIndex (proj , "/issues/" )
109+ if i < 0 {
110+ return "" , q
111+ }
112+ return proj [:i ], proj [i + len ("/issues/" ):]
113+ }
114+ // recognize "go.dev/issues/12345"
115+ if num , ok := strings .CutPrefix (q , "go.dev/issues/" ); ok {
116+ return "golang/go" , num
117+ }
118+ // recognize golang/go#12345
119+ if proj , num , ok := strings .Cut (q , "#" ); ok {
120+ return proj , num
121+ }
122+ return "" , q
123+ }
124+ proj , num := split (issueID )
125+ issue , err := strconv .ParseInt (num , 10 , 64 )
126+ if err != nil || issue <= 0 {
127+ return "" , 0 , fmt .Errorf ("invalid issue number %q" , issueID )
128+ }
129+ return proj , issue , nil
130+ }
131+
93132// populateOverviewPage returns the contents of the overview page.
94133func (g * Gaby ) populateOverviewPage (r * http.Request ) overviewPage {
95134 p := overviewPage {
@@ -98,20 +137,22 @@ func (g *Gaby) populateOverviewPage(r *http.Request) overviewPage {
98137 OverviewType : r .FormValue ("t" ),
99138 },
100139 }
101- q := strings .TrimSpace (p .Form .Query )
102- if q == "" {
140+ proj , issue , err := parseIssueNumber (p .Form .Query )
141+ if err != nil {
142+ p .Error = fmt .Errorf ("invalid form value: %v" , err )
103143 return p
104144 }
105- issue , err := strconv .ParseInt (q , 10 , 64 )
106- if err != nil {
107- p .Error = fmt .Errorf ("invalid form value %q: %w" , q , err )
145+ if proj == "" {
146+ proj = g .githubProject // default to golang/go
147+ }
148+ if g .githubProject != proj {
149+ p .Error = fmt .Errorf ("invalid form value (unrecognized project): %q" , p .Form .Query )
108150 return p
109151 }
110- if issue < 0 {
111- p .Error = fmt .Errorf ("invalid form value %q" , q )
152+ if issue <= 0 {
112153 return p
113154 }
114- overview , err := g .overview (r .Context (), issue , p .Form .OverviewType )
155+ overview , err := g .overview (r .Context (), proj , issue , p .Form .OverviewType )
115156 if err != nil {
116157 p .Error = err
117158 return p
@@ -121,20 +162,20 @@ func (g *Gaby) populateOverviewPage(r *http.Request) overviewPage {
121162}
122163
123164// overview generates an overview of the issue of the given type.
124- func (g * Gaby ) overview (ctx context.Context , issue int64 , overviewType string ) (* overviewResult , error ) {
165+ func (g * Gaby ) overview (ctx context.Context , proj string , issue int64 , overviewType string ) (* overviewResult , error ) {
125166 switch overviewType {
126167 case "" , issueOverviewType :
127- return g .issueOverview (ctx , issue )
168+ return g .issueOverview (ctx , proj , issue )
128169 case relatedOverviewType :
129- return g .relatedOverview (ctx , issue )
170+ return g .relatedOverview (ctx , proj , issue )
130171 default :
131172 return nil , fmt .Errorf ("unknown overview type %q" , overviewType )
132173 }
133174}
134175
135176// issueOverview generates an overview of the issue and its comments.
136- func (g * Gaby ) issueOverview (ctx context.Context , issue int64 ) (* overviewResult , error ) {
137- overview , err := github .IssueOverview (ctx , g .llm , g .db , g . githubProject , issue )
177+ func (g * Gaby ) issueOverview (ctx context.Context , proj string , issue int64 ) (* overviewResult , error ) {
178+ overview , err := github .IssueOverview (ctx , g .llm , g .db , proj , issue )
138179 if err != nil {
139180 return nil , err
140181 }
@@ -145,8 +186,8 @@ func (g *Gaby) issueOverview(ctx context.Context, issue int64) (*overviewResult,
145186}
146187
147188// relatedOverview generates an overview of the issue and its related documents.
148- func (g * Gaby ) relatedOverview (ctx context.Context , issue int64 ) (* overviewResult , error ) {
149- iss , err := github .LookupIssue (g .db , g . githubProject , issue )
189+ func (g * Gaby ) relatedOverview (ctx context.Context , proj string , issue int64 ) (* overviewResult , error ) {
190+ iss , err := github .LookupIssue (g .db , proj , issue )
150191 if err != nil {
151192 return nil , err
152193 }
@@ -165,7 +206,7 @@ func (g *Gaby) relatedOverview(ctx context.Context, issue int64) (*overviewResul
165206}
166207
167208// Related returns the relative URL of the related-entity search
168- // for the issue.
209+ // for the issue. This is used in the overview page template.
169210func (r * overviewResult ) Related () string {
170211 return fmt .Sprintf ("/search?q=%s" , r .Issue .HTMLURL )
171212}
0 commit comments