Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class SessionsChatAccessibilityHelp implements IAccessibleViewImplementat
content.push(localize('sessionsChat.feedbackAttachment', "When a feedback comments attachment appears above the input, focus it and press Enter or Space. A single comment opens directly. Multiple comments open a tree grouped by file; use the arrow keys to navigate, Enter to reveal a comment, and Escape to close the tree."));
content.push(localize('sessionsChat.inputBackground', "Press Alt+Enter to start the session in the background without navigating into it. The started session appears in the Chat Sessions view."));
content.push(localize('sessionsChat.workspace', "Shift+Tab to navigate to the workspace picker and choose a workspace for your session."));
content.push(localize('sessionsChat.pullRequestSession', "In a repository section where New Session is a split button, focus New Session and press Right Arrow to reach its dropdown, then activate Create Session from Pull Request to open a searchable pull request picker. Pull requests are grouped by review and assignment status. Use the arrow keys to navigate, Enter to create the session, and Escape to close the picker."));
content.push(localize('sessionsChat.pullRequestSession', "In a repository section where New Session is a split button, focus New Session and press Right Arrow to reach its dropdown, then activate New Session from Pull Request to open a searchable pull request picker. Pull requests are grouped by review and assignment status. Use the arrow keys to navigate, Enter to create the session, and Escape to close the picker."));
content.push(localize('sessionsChat.githubReferences', "Pull request and issue pills in the session header open their GitHub item in the GitHub Pull Requests extension when it is available. Pills that represent several items open a keyboard-accessible picker."));
content.push(localize('sessionsChat.failingChecksPullRequest', "When the active session has failing checks, use Reveal in the banner above the input to open its pull request, or use Fix Checks to ask the agent to address the failures."));
content.push(localize('sessionsChat.pickFolderQuickPick', "To choose a folder from a searchable list instead, use the New Session in Folder command{0}.", '<keybinding:workbench.action.sessions.newSession.pickFolderQuickPick>'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { IQuickInputService } from '../../../../platform/quickinput/common/quick
import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
import { toErrorMessage } from '../../../../base/common/errorMessage.js';
import { ChatContextKeys } from '../../../../workbench/contrib/chat/common/actions/chatContextKeys.js';
import { IGitService } from '../../../../workbench/contrib/git/common/gitService.js';
import { ISessionsManagementService } from '../../../services/sessions/common/sessionsManagement.js';
import { ISessionsPartService } from '../../../services/sessions/browser/sessionsPartService.js';
import { ISessionsService } from '../../../services/sessions/browser/sessionsService.js';
Expand All @@ -28,16 +27,16 @@ import { Menus } from '../../../browser/menus.js';
import { ISessionSection, SessionSectionHasGitHubRepositoryContext, SessionSectionHasNonCloudRepositoryContext, SessionSectionTypeContext } from '../../sessions/browser/views/sessionsList.js';
import { IGitHubService } from './githubService.js';
import { IGitHubPullRequestSummary } from '../common/types.js';
import { createPullRequestBootstrapPrompt, createPullRequestContextAttachment, createPullRequestQuickPickItems, createPullRequestSessionMetadata, getExistingPullRequests, getGitHubRepositoryFromRemotes, hasExistingPullRequest, IPullRequestQuickPickItem, mergePullRequestSummaries, pullRequestMatchesQuery, resolvePullRequestSessionRepository } from './pullRequestPicker.js';
import { createPullRequestBootstrapPrompt, createPullRequestContextAttachment, createPullRequestQuickPickItems, createPullRequestSessionMetadata, getExistingPullRequests, hasExistingPullRequest, IPullRequestQuickPickItem, mergePullRequestSummaries, pullRequestMatchesQuery, resolvePullRequestSessionRepository } from './pullRequestPicker.js';
import { createAndOpenPullRequestSession } from './pullRequestSessionCreation.js';

export const CREATE_SESSION_FROM_PULL_REQUEST_COMMAND_ID = 'workbench.agentSessions.createSessionFromPullRequest';
export const NEW_SESSION_FROM_PULL_REQUEST_COMMAND_ID = 'workbench.agentSessions.newSessionFromPullRequest';
Comment thread
lszomoru marked this conversation as resolved.

registerAction2(class CreateSessionFromPullRequestAction extends Action2 {
registerAction2(class NewSessionFromPullRequestAction extends Action2 {
constructor() {
super({
id: CREATE_SESSION_FROM_PULL_REQUEST_COMMAND_ID,
title: localize2('createSessionFromPullRequest', "Create Session from Pull Request"),
id: NEW_SESSION_FROM_PULL_REQUEST_COMMAND_ID,
title: localize2('newSessionFromPullRequest', "New Session from Pull Request"),
Comment thread
lszomoru marked this conversation as resolved.
icon: Codicon.gitPullRequestCreate,
precondition: ChatContextKeys.enabled,
menu: {
Expand All @@ -61,7 +60,6 @@ registerAction2(class CreateSessionFromPullRequestAction extends Action2 {

const sessionsManagementService = accessor.get(ISessionsManagementService);
const notificationService = accessor.get(INotificationService);
const gitService = accessor.get(IGitService);
const gitHubService = accessor.get(IGitHubService);
const sessionsService = accessor.get(ISessionsService);
const sessionsPartService = accessor.get(ISessionsPartService);
Expand All @@ -71,8 +69,8 @@ registerAction2(class CreateSessionFromPullRequestAction extends Action2 {
const store = new DisposableStore();
const pickerCts = store.add(new CancellationTokenSource());
let sessionCreated = false;
picker.title = localize('createSessionFromPullRequest.title', "Create Session from Pull Request");
picker.placeholder = localize('createSessionFromPullRequest.resolvingRepository', "Resolving GitHub repository...");
picker.title = localize('newSessionFromPullRequest.title', "New Session from Pull Request");
picker.placeholder = localize('newSessionFromPullRequest.resolvingRepository', "Resolving GitHub repository...");
picker.matchOnDescription = true;
picker.matchOnDetail = true;
picker.sortByLabel = false;
Expand All @@ -89,26 +87,7 @@ registerAction2(class CreateSessionFromPullRequestAction extends Action2 {

let repository;
try {
repository = await resolvePullRequestSessionRepository(
context.sessions,
async folderUri => {
const gitRepository = await gitService.openRepository(folderUri);
if (!gitRepository) {
return undefined;
}
const current = getGitHubRepositoryFromRemotes(gitRepository.state.get().remotes);
if (current) {
return current;
}
const state = await waitForState(
gitRepository.state,
state => state.remotes.length > 0,
undefined,
pickerCts.token,
);
return getGitHubRepositoryFromRemotes(state.remotes);
},
);
repository = await resolvePullRequestSessionRepository(context.sessions);
Comment thread
lszomoru marked this conversation as resolved.
} catch (error) {
picker.hide();
if (!isCancellationError(error)) {
Expand Down
23 changes: 1 addition & 22 deletions src/vs/sessions/contrib/github/browser/pullRequestPicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,8 @@ export interface IPullRequestSessionRepository {
readonly repo: string;
}

export interface IRepositoryRemote {
readonly name: string;
readonly fetchUrl?: string;
}

export async function resolvePullRequestSessionRepository(
sectionSessions: readonly ISession[],
resolveGitHubRepository: (folderUri: URI) => Promise<{ readonly owner: string; readonly repo: string } | undefined>,
): Promise<IPullRequestSessionRepository | undefined> {
let folderUri: URI | undefined;
for (const session of sectionSessions) {
Expand All @@ -55,25 +49,10 @@ export async function resolvePullRequestSessionRepository(
if (!folderUri) {
return undefined;
}
const identity = getFirstGitHubRepository(sectionSessions) ?? await resolveGitHubRepository(folderUri);
const identity = getFirstGitHubRepository(sectionSessions);
Comment thread
lszomoru marked this conversation as resolved.
return identity ? { folderUri, owner: identity.owner, repo: identity.repo } : undefined;
}

export function getGitHubRepositoryFromRemotes(remotes: readonly IRepositoryRemote[]): { readonly owner: string; readonly repo: string } | undefined {
const orderedRemotes = [...remotes].sort((a, b) => Number(b.name === 'origin') - Number(a.name === 'origin'));
for (const remote of orderedRemotes) {
const fetchUrl = remote.fetchUrl?.trim().replace(/\/$/, '').replace(/\.git$/, '');
if (!fetchUrl) {
continue;
}
const match = /^(?:(?:https?|ssh):\/\/(?:git@)?github\.com\/|git@github\.com:)(?<owner>[^/\s]+)\/(?<repo>[^/\s]+)$/i.exec(fetchUrl);
if (match?.groups) {
return { owner: match.groups.owner, repo: match.groups.repo };
}
}
return undefined;
}

export function getExistingPullRequests(sessions: readonly ISession[], owner: string, repo: string, repositorySessions: readonly ISession[] = []): IExistingPullRequests {
const numbers = new Set<number>();
const headRefs = new Set<string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { mock } from '../../../../../base/test/common/mock.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { readSessionGitHubState } from '../../../../../platform/agentHost/common/state/sessionState.js';
import { ISession, ISessionWorkspace } from '../../../../services/sessions/common/session.js';
import { createPullRequestBootstrapPrompt, createPullRequestContextAttachment, createPullRequestQuickPickItems, createPullRequestSessionMetadata, getExistingPullRequests, getGitHubRepositoryFromRemotes, getPullRequestNumberFromCheckoutRef, IPullRequestQuickPickItem, mergePullRequestSummaries, pullRequestMatchesQuery, resolvePullRequestSessionRepository } from '../../browser/pullRequestPicker.js';
import { createPullRequestBootstrapPrompt, createPullRequestContextAttachment, createPullRequestQuickPickItems, createPullRequestSessionMetadata, getExistingPullRequests, getPullRequestNumberFromCheckoutRef, IPullRequestQuickPickItem, mergePullRequestSummaries, pullRequestMatchesQuery, resolvePullRequestSessionRepository } from '../../browser/pullRequestPicker.js';
import { IGitHubPullRequestSummary } from '../../common/types.js';
import { createAndOpenPullRequestSession } from '../../browser/pullRequestSessionCreation.js';

Expand Down Expand Up @@ -259,7 +259,7 @@ suite('Create Session from Pull Request', () => {
});
});

test('resolves non-cloud repositories from session metadata or Git remotes', async () => {
test('resolves non-cloud repositories from session metadata', async () => {
const cloudRoot = URI.parse('github-remote-file://github/alexr00/playground/copilot%252Finspect-pull-request-748');
const localRoot = URI.file('/repos/alexr00/playground');
const remoteRoot = URI.parse('vscode-remote://ssh-remote+host/repos/alexr00/playground');
Expand All @@ -268,17 +268,13 @@ suite('Create Session from Pull Request', () => {
const remoteSession = sessionWithRepository(remoteRoot, 'alexr00', 'playground');

assert.deepStrictEqual({
cloud: await resolvePullRequestSessionRepository([cloudSession], async () => undefined),
local: await resolvePullRequestSessionRepository([localSession], async () => ({ owner: 'alexr00', repo: 'playground' })),
mixed: await resolvePullRequestSessionRepository([cloudSession, localSession], async () => undefined),
remote: await resolvePullRequestSessionRepository([remoteSession], async () => undefined),
cloud: await resolvePullRequestSessionRepository([cloudSession]),
local: await resolvePullRequestSessionRepository([localSession]),
mixed: await resolvePullRequestSessionRepository([cloudSession, localSession]),
remote: await resolvePullRequestSessionRepository([remoteSession]),
}, {
cloud: undefined,
local: {
folderUri: localRoot,
owner: 'alexr00',
repo: 'playground',
},
local: undefined,
mixed: {
folderUri: localRoot,
owner: 'alexr00',
Expand All @@ -291,25 +287,6 @@ suite('Create Session from Pull Request', () => {
},
});
});

test('parses GitHub repository identity from origin before other remotes', () => {
assert.deepStrictEqual({
https: getGitHubRepositoryFromRemotes([
{ name: 'upstream', fetchUrl: 'git@github.com:microsoft/vscode.git' },
{ name: 'origin', fetchUrl: 'https://github.com/alexr00/vscode.git' },
]),
ssh: getGitHubRepositoryFromRemotes([
{ name: 'origin', fetchUrl: 'ssh://git@github.com/alexr00/playground' },
]),
nonGitHub: getGitHubRepositoryFromRemotes([
{ name: 'origin', fetchUrl: 'https://example.com/alexr00/playground.git' },
]),
}, {
https: { owner: 'alexr00', repo: 'vscode' },
ssh: { owner: 'alexr00', repo: 'playground' },
nonGitHub: undefined,
});
});
});

function pullRequest(number: number, overrides: Partial<IGitHubPullRequestSummary> = {}): IGitHubPullRequestSummary {
Expand Down
Loading