Repository: cli/cli
File: pkg/cmd/pr/shared/finder.go (Go)

Code before:
			return preloadPrChecks(httpClient, f.baseRefRepo, pr)
		})
	}
	if getProjectItems {
		g.Go(func() error {
			apiClient := api.NewClientFromHTTP(httpClient)
			err := api.ProjectsV2ItemsForPullRequest(apiClient, f.baseRefRepo, pr)
			if err != nil && !api.ProjectsV2IgnorableError(err) {
				return err
			}
			return nil
		})
	}

	return pr, f.baseRefRepo, g.Wait()
}

var pullURLRE = regexp.MustCompile(`^/([^/]+)/([^/]+)/pull/(\d+)(.*$)`)

// ParseURL parses a pull request URL and returns the repository and pull
// request number. If there is no error, the returned repo is not nil and will
// have non-empty hostname.
//
// Any path components (not query params or fragments) after the pull request
// number are also returned.
func ParseURL(prURL string) (ghrepo.Interface, int, string, error) {
	if prURL == "" {
		return nil, 0, "", fmt.Errorf("invalid URL: %q", prURL)
	}

	u, err := url.Parse(prURL)
	if err != nil {
		return nil, 0, "", err
	}

	if u.Scheme != "https" && u.Scheme != "http" {
		return nil, 0, "", fmt.Errorf("invalid scheme: %s", u.Scheme)
	}

	m := pullURLRE.FindStringSubmatch(u.Path)
	if m == nil {
		return nil, 0, "", fmt.Errorf("not a pull request URL: %s", prURL)
	}

	repo := ghrepo.NewWithHost(m[1], m[2], u.Hostname())
	prNumber, _ := strconv.Atoi(m[3])
	rest := m[4]
	return repo, prNumber, rest, nil
}

var fullReferenceRE = regexp.MustCompile(`^(?:([^/]+)/([^/]+))#(\d+)$`)

Review comment (suggestion):
No action required:

💭 I think this is a good thing to add. No comments on adding this to the PR finder; it makes sense to me.

💭 I'm surprised you only had to change the one place in `pr edit`. 

💭 Having so many return values adds a bit to the cognitive load trying to understand what this does. Maybe a good argument for named return values? Not really sure what's idiomatic there. It just took me a second to realize that (having not been familiar with this function) the int is the PR number and the string is the extra path components.

Code after:
			return preloadPrChecks(httpClient, f.baseRefRepo, pr)
		})
	}
	if getProjectItems {
		g.Go(func() error {
			apiClient := api.NewClientFromHTTP(httpClient)
			err := api.ProjectsV2ItemsForPullRequest(apiClient, f.baseRefRepo, pr)
			if err != nil && !api.ProjectsV2IgnorableError(err) {
				return err
			}
			return nil
		})
	}

	return pr, f.baseRefRepo, g.Wait()
}

var pullURLRE = regexp.MustCompile(`^/([^/]+)/([^/]+)/pull/(\d+)(.*$)`)

// ParseURL parses a pull request URL and returns the repository, pull request
// number, and any tailing path components. If there is no error, the returned
// repo is not nil and will have non-empty hostname.
func ParseURL(prURL string) (ghrepo.Interface, int, string, error) {
	if prURL == "" {
		return nil, 0, "", fmt.Errorf("invalid URL: %q", prURL)
	}

	u, err := url.Parse(prURL)
	if err != nil {
		return nil, 0, "", err
	}

	if u.Scheme != "https" && u.Scheme != "http" {
		return nil, 0, "", fmt.Errorf("invalid scheme: %s", u.Scheme)
	}

	m := pullURLRE.FindStringSubmatch(u.Path)
	if m == nil {
		return nil, 0, "", fmt.Errorf("not a pull request URL: %s", prURL)
	}

	repo := ghrepo.NewWithHost(m[1], m[2], u.Hostname())
	prNumber, _ := strconv.Atoi(m[3])
	tail := m[4]
	return repo, prNumber, tail, nil
}

var fullReferenceRE = regexp.MustCompile(`^(?:([^/]+)/([^/]+))#(\d+)$`)

// ParseFullReference parses a short issue/pull request reference of the form
// "owner/repo#number", where owner, repo and number are all required.