Skip to content
89 changes: 72 additions & 17 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1604,26 +1604,81 @@ ${contents}
if (!githubRepo) {
return;
}
const prNumber = await vscode.window.showInputBox({
ignoreFocusOut: true, prompt: vscode.l10n.t('Enter the pull request number or URL'),
validateInput: (input: string) => {
const result = validateAndParseInput(input, githubRepo.repo.remote.owner, githubRepo.repo.remote.repositoryName);
return result.isValid ? undefined : result.errorMessage;

// Create QuickPick to show all PRs
const quickPick = vscode.window.createQuickPick<vscode.QuickPickItem & { pr?: PullRequestModel }>();
quickPick.placeholder = vscode.l10n.t('Enter a pull request number/URL or select from the list');
quickPick.matchOnDescription = true;
quickPick.matchOnDetail = true;
quickPick.show();
quickPick.busy = true;

let acceptDisposable: vscode.Disposable | undefined;
let hideDisposable: vscode.Disposable | undefined;

// Fetch all open PRs (lightweight query)
try {
const prs = await githubRepo.repo.getPullRequestNumbers();
if (!prs) {
return vscode.window.showErrorMessage(vscode.l10n.t('Failed to fetch pull requests'));
}
});
if ((prNumber === undefined) || prNumber === '#') {
return;
}
// Sort PRs by number in descending order (most recent first)
const sortedPRs = prs.sort((a, b) => b.number - a.number);
const prItems: (vscode.QuickPickItem & { prNumber: number })[] = sortedPRs.map(pr => ({
label: `#${pr.number} ${pr.title}`,
description: `by @${pr.author.login}`,
prNumber: pr.number
}));

quickPick.items = prItems;
quickPick.busy = false;

// Handle selection
const selected = await new Promise<(vscode.QuickPickItem & { prNumber?: number }) | string | undefined>((resolve) => {
acceptDisposable = quickPick.onDidAccept(() => {
if (quickPick.selectedItems.length > 0) {
resolve(quickPick.selectedItems[0]);
} else if (quickPick.value) {
// User typed something but didn't select from list
resolve(quickPick.value);
} else {
// User pressed Enter with no selection and no input
resolve(undefined);
}
});
hideDisposable = quickPick.onDidHide(() => resolve(undefined));
});

// Extract PR number from input (either direct number or URL)
const parseResult = validateAndParseInput(prNumber, githubRepo.repo.remote.owner, githubRepo.repo.remote.repositoryName);
if (!parseResult.isValid) {
return vscode.window.showErrorMessage(parseResult.errorMessage || vscode.l10n.t('Invalid pull request number or URL'));
}
if (!selected) {
return;
}
quickPick.busy = true;
let prModel: PullRequestModel | undefined;

// Check if user selected from the list or typed a custom value
if (typeof selected === 'string') {
// User typed a PR number or URL
const parseResult = validateAndParseInput(selected, githubRepo.repo.remote.owner, githubRepo.repo.remote.repositoryName);
if (!parseResult.isValid) {
return vscode.window.showErrorMessage(parseResult.errorMessage || vscode.l10n.t('Invalid pull request number or URL'));
}
prModel = await githubRepo.manager.fetchById(githubRepo.repo, parseResult.prNumber);
} else if (selected.prNumber) {
// User selected from the list
prModel = await githubRepo.manager.fetchById(githubRepo.repo, selected.prNumber);
}

const prModel = await githubRepo.manager.fetchById(githubRepo.repo, parseResult.prNumber);
if (prModel) {
return ReviewManager.getReviewManagerForFolderManager(reviewsManager.reviewManagers, githubRepo.manager)?.switch(prModel);
if (prModel) {
return ReviewManager.getReviewManagerForFolderManager(reviewsManager.reviewManagers, githubRepo.manager)?.switch(prModel);
}
} catch (e) {
vscode.window.showErrorMessage(vscode.l10n.t('Failed to fetch pull requests: {0}', formatError(e)));
} finally {
// Clean up event listeners and QuickPick
acceptDisposable?.dispose();
hideDisposable?.dispose();
quickPick.hide();
quickPick.dispose();
}
}));

Expand Down
36 changes: 36 additions & 0 deletions src/github/githubRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
OrganizationTeamsCountResponse,
OrganizationTeamsResponse,
OrgProjectsResponse,
PullRequestNumberData,
PullRequestNumbersResponse,
PullRequestParticipantsResponse,
PullRequestResponse,
PullRequestsResponse,
Expand Down Expand Up @@ -668,6 +670,40 @@ export class GitHubRepository extends Disposable {
return undefined;
}

async getPullRequestNumbers(): Promise<PullRequestNumberData[] | undefined> {
let remote: GitHubRemote | undefined;
try {
Logger.debug(`Fetch pull request numbers - enter`, this.id);
const ensured = await this.ensure();
remote = ensured.remote;
const { query, schema } = ensured;
const { data } = await query<PullRequestNumbersResponse>({
query: schema.PullRequestNumbers,
variables: {
owner: remote.owner,
name: remote.repositoryName,
first: 100,
},
});
Logger.debug(`Fetch pull request numbers - done`, this.id);

if (data?.repository?.pullRequests) {
return data.repository.pullRequests.nodes;
}
} catch (e) {
Logger.error(`Fetching pull request numbers failed: ${e}`, this.id);
if (e.status === 404) {
// not found
vscode.window.showWarningMessage(
`Fetching pull request numbers for remote '${remote?.remoteName}' failed, please check if the repository ${remote?.owner}/${remote?.repositoryName} is valid.`,
);
} else {
throw e;
}
}
return undefined;
}

async getPullRequestForBranch(branch: string, headOwner: string): Promise<PullRequestModel | undefined> {
let remote: GitHubRemote | undefined;
try {
Expand Down
17 changes: 17 additions & 0 deletions src/github/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,23 @@ export interface PullRequestsResponse {
} | null;
}

export interface PullRequestNumbersResponse {
repository: {
pullRequests: {
nodes: PullRequestNumberData[]
}
} | null;
rateLimit: RateLimit;
}

export interface PullRequestNumberData {
number: number;
title: string;
author: {
login: string;
};
}

export interface MaxIssueResponse {
repository: {
issues: {
Expand Down
21 changes: 21 additions & 0 deletions src/github/queriesShared.gql
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,27 @@ query PullRequestTemplates($owner: String!, $name: String!) {
}
}

fragment PullRequestNumberFragment on PullRequest {
number
title
author {
login
}
}

query PullRequestNumbers($owner: String!, $name: String!, $first: Int!) {
repository(owner: $owner, name: $name) {
pullRequests(first: $first, states: OPEN, orderBy: { field: CREATED_AT, direction: DESC }) {
nodes {
...PullRequestNumberFragment
}
}
}
rateLimit {
...RateLimit
}
}

mutation AddComment($input: AddPullRequestReviewCommentInput!) {
addPullRequestReviewComment(input: $input) {
comment {
Expand Down