Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@
"default": "ask",
"description": "%githubPullRequests.createOnPublishBranch.description%"
},
"githubPullRequests.createInBrowser": {
"type": "boolean",
"default": false,
"description": "When true, the 'Create Pull Request' command opens GitHub in the browser instead of showing the create view in VS Code."
},
"githubPullRequests.commentExpandState": {
"type": "string",
"enum": [
Expand Down
1 change: 1 addition & 0 deletions src/@types/vscode.proposed.chatParticipantAdditions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ declare module 'vscode' {
isComplete?: boolean;
toolSpecificData?: ChatTerminalToolInvocationData;
fromSubAgent?: boolean;
presentation?: 'hidden' | 'hiddenAfterComplete' | undefined;

constructor(toolName: string, toolCallId: string, isError?: boolean);
}
Expand Down
1 change: 1 addition & 0 deletions src/common/settingKeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type PullPRBranchVariants = 'never' | 'pull' | 'pullAndMergeBase' | 'pull
export const UPSTREAM_REMOTE = 'upstreamRemote';
export const DEFAULT_CREATE_OPTION = 'defaultCreateOption';
export const CREATE_BASE_BRANCH = 'createDefaultBaseBranch';
export const CREATE_IN_BROWSER = 'createInBrowser';

export const ISSUES_SETTINGS_NAMESPACE = 'githubIssues';
export const ASSIGN_WHEN_WORKING = 'assignWhenWorking';
Expand Down
27 changes: 26 additions & 1 deletion src/github/createPRViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,28 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
return this._replyMessage(message, undefined);
}

private async openCreateInBrowser(): Promise<void> {
// Get the base repository info
const baseRepo = this.getBaseGitHubRepo();
if (!baseRepo) {
vscode.window.showErrorMessage(vscode.l10n.t('Unable to find repository to create pull request in.'));
return;
}

// Get the compare branch name - this is the branch we want to create a PR from
const compareBranch = this._defaultCompareBranch;
if (!compareBranch) {
vscode.window.showErrorMessage(vscode.l10n.t('Unable to determine branch to create pull request from.'));
return;
}

// Construct the GitHub URL for creating a PR
// Format: https://github.com/{owner}/{repo}/pull/new/{branch}
const url = `${baseRepo.remote.normalizedHost}/${baseRepo.remote.owner}/${baseRepo.remote.repositoryName}/pull/new/${encodeURIComponent(compareBranch)}`;

await vscode.env.openExternal(vscode.Uri.parse(url));
}

protected override async _onDidReceiveMessage(message: IRequestMessage<any>) {
const result = await super._onDidReceiveMessage(message);
if (result !== this.MESSAGE_UNHANDLED) {
Expand Down Expand Up @@ -539,6 +561,9 @@ export abstract class BaseCreatePullRequestViewProvider<T extends BasePullReques
case 'pr.removeLabel':
return this.removeLabel(message);

case 'pr.openCreateInBrowser':
return this.openCreateInBrowser();

default:
return this.MESSAGE_UNHANDLED;
}
Expand Down Expand Up @@ -1342,4 +1367,4 @@ export class CreatePullRequestViewProvider extends BaseCreatePullRequestViewProv
vscode.window.showErrorMessage('Unsupported webview message');
}
}
}
}
52 changes: 52 additions & 0 deletions src/view/reviewManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Logger from '../common/logger';
import { parseRepositoryRemotes, Remote } from '../common/remote';
import {
COMMENTS,
CREATE_IN_BROWSER,
FOCUSED_MODE,
IGNORE_PR_BRANCHES,
NEVER_IGNORE_DEFAULT_BRANCH,
Expand Down Expand Up @@ -1170,6 +1171,15 @@ export class ReviewManager extends Disposable {
}

public async createPullRequest(compareBranch?: string): Promise<void> {
// Check if user wants to create PR in browser
const createInBrowser = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<boolean>(CREATE_IN_BROWSER, false);

if (createInBrowser) {
// Open browser to create PR instead of showing the create view
await this.openCreatePullRequestInBrowser(compareBranch);
return;
}

const postCreate = async (createdPR: PullRequestModel | undefined) => {
if (!createdPR) {
return;
Expand Down Expand Up @@ -1208,6 +1218,48 @@ export class ReviewManager extends Disposable {
return this._createPullRequestHelper.create(this._telemetry, this._context.extensionUri, this._folderRepoManager, compareBranch, postCreate);
}

private async openCreatePullRequestInBrowser(compareBranch?: string): Promise<void> {
try {
// Get the current branch
const branch = compareBranch
? await this._folderRepoManager.repository.getBranch(compareBranch)
: this._folderRepoManager.repository.state.HEAD;

if (!branch?.name) {
vscode.window.showErrorMessage(vscode.l10n.t('Unable to determine the current branch.'));
return;
}

// Get pull request defaults which includes the base branch
const pullRequestDefaults = await this._folderRepoManager.getPullRequestDefaults(branch);

// Get the origin to determine the repository
const compareOrigin = await this._folderRepoManager.getOrigin(branch);

// Find the GitHub repository for the base
// Note: pullRequestDefaults.owner is the target owner (may differ from current fork)
// compareOrigin.remote.repositoryName is the repository we're working in
const baseRepo = this._folderRepoManager.gitHubRepositories.find(
repo => repo.remote.owner === pullRequestDefaults.owner &&
repo.remote.repositoryName === compareOrigin.remote.repositoryName
);

if (!baseRepo) {
vscode.window.showErrorMessage(vscode.l10n.t('Unable to find repository to create pull request in.'));
return;
}

// Construct the GitHub URL
// Format: https://github.com/{owner}/{repo}/compare/{base}...{head}
const url = `${baseRepo.remote.normalizedHost}/${pullRequestDefaults.owner}/${compareOrigin.remote.repositoryName}/compare/${pullRequestDefaults.base}...${encodeURIComponent(branch.name)}`;

await vscode.env.openExternal(vscode.Uri.parse(url));
} catch (error) {
Logger.error(`Failed to open create pull request in browser: ${error}`, 'ReviewManager');
vscode.window.showErrorMessage(vscode.l10n.t('Failed to open create pull request in browser: {0}', formatError(error)));
}
}

public async openDescription(): Promise<void> {
const pullRequest = this._folderRepoManager.activePullRequest;
if (!pullRequest) {
Expand Down
4 changes: 4 additions & 0 deletions webviews/common/createContextNew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ export class CreatePRContextNew {
return this.postMessage({ command: 'pr.cancelCreate', args });
};

public openCreateInBrowser = (): Promise<void> => {
return this.postMessage({ command: 'pr.openCreateInBrowser' });
};

public updateState = (params: Partial<CreateParamsNew>, reset: boolean = false): void => {
this.createParams = reset ? { ...defaultCreateParams, ...params } : { ...this.createParams, ...params };
vscode.setState(this.createParams);
Expand Down
4 changes: 4 additions & 0 deletions webviews/createPullRequestViewNew/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ export function main() {
Cancel
</button>

<button disabled={isBusy || !ctx.initialized} className='secondary' onClick={() => ctx.openCreateInBrowser()} title='Create pull request on GitHub.com'>
Create in Browser
</button>

<ContextDropdown optionsContext={() => makeCreateMenuContext(params)}
defaultAction={onCreateButton}
defaultOptionLabel={() => createMethodLabel(ctx.createParams.isDraft, ctx.createParams.autoMerge, ctx.createParams.autoMergeMethod, ctx.createParams.baseHasMergeQueue).label}
Expand Down