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
20 changes: 15 additions & 5 deletions src/github/copilotPrWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import { debounce } from '../common/async';
import { COPILOT_ACCOUNTS } from '../common/comment';
import { COPILOT_LOGINS, copilotEventToStatus, CopilotPRStatus } from '../common/copilot';
import { Disposable } from '../common/lifecycle';
import Logger from '../common/logger';
import { DEV_MODE, PR_SETTINGS_NAMESPACE, QUERIES } from '../common/settingKeys';
import { formatError } from '../common/utils';
import { PrsTreeModel } from '../view/prsTreeModel';

export function isCopilotQuery(query: string): boolean {
Expand Down Expand Up @@ -190,6 +192,7 @@ export class CopilotStateModel extends Disposable {
}

export class CopilotPRWatcher extends Disposable {
private static readonly ID = 'CopilotPRWatcher';
private readonly _model: CopilotStateModel;

constructor(private readonly _reposManager: RepositoriesManager, private readonly _prsTreeModel: PrsTreeModel) {
Expand All @@ -206,9 +209,10 @@ export class CopilotPRWatcher extends Disposable {
}

private _initialize() {
this._prsTreeModel.refreshCopilotStateChanges(true);
this._pollForChanges();
const updateFullState = debounce(() => this._prsTreeModel.refreshCopilotStateChanges(true), 50);
const updateFullState = debounce(() => this._prsTreeModel.refreshCopilotStateChanges(true).catch(error => {
Logger.error(`Refreshing Copilot pull request state failed: ${formatError(error)}`, CopilotPRWatcher.ID);
}), 50);
this._register(this._reposManager.onDidChangeAnyPullRequests(e => {
if (e.some(pr => COPILOT_ACCOUNTS[pr.model.author.login])) {
if (!this._model.isInitialized) {
Expand Down Expand Up @@ -256,7 +260,7 @@ export class CopilotPRWatcher extends Disposable {
private async _pollForChanges(): Promise<void> {
// Skip polling if dev mode is enabled
const devMode = vscode.workspace.getConfiguration(PR_SETTINGS_NAMESPACE).get<boolean>(DEV_MODE, false);
if (devMode) {
if (devMode || this.isDisposed) {
return;
}

Expand All @@ -265,9 +269,15 @@ export class CopilotPRWatcher extends Disposable {
this._pollTimeout = undefined;
}
this._lastPollTime = Date.now();
const shouldContinue = await this._prsTreeModel.refreshCopilotStateChanges(true);
try {
if (!await this._prsTreeModel.refreshCopilotStateChanges(true)) {
return;
}
} catch (error) {
Logger.error(`Refreshing Copilot pull request state failed: ${formatError(error)}`, CopilotPRWatcher.ID);
}

if (shouldContinue) {
if (!this.isDisposed && !this._pollTimeout) {
this._pollTimeout = setTimeout(() => {
this._pollForChanges();
}, this._pollInterval);
Expand Down
35 changes: 21 additions & 14 deletions src/github/folderRepositoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ export interface ItemsResponseResult<T> {
hasMorePages: boolean;
hasUnsearchedRepositories: boolean;
totalCount?: number;
// Pages reached across repositories, including pages with no matching items.
paginationProgress?: number;
}

export class NoGitHubReposError extends Error {
Expand Down Expand Up @@ -1259,7 +1261,7 @@ export class FolderRepositoryManager extends Disposable {
this.telemetry.sendTelemetryEvent('branch.delete');
}

// Keep track of how many pages we've fetched for each query, so when we reload we pull the same ones.
// Track reached pages, including failed requests, so reloading retries all of them.
private totalFetchedPages = new Map<string, number>();

/**
Expand All @@ -1268,9 +1270,9 @@ export class FolderRepositoryManager extends Disposable {
* 2) Fetch Next: fetch the next page from this remote, or if it has no more pages, the first page from the next remote that does have pages
* 3) Restore: fetch all the pages you previously have fetched
*
* When `options.fetchNextPage === false`, we are in case 2.
* When `options.fetchNextPage === true`, we are in case 2.
* Otherwise:
* If `this.totalFetchQueries[queryId] === 0`, we are in case 1.
* If `this.totalFetchedPages.get(queryId)` is zero or unset, we are in case 1.
* Otherwise, we're in case 3.
*/
private async fetchPagedData<T>(
Expand All @@ -1286,7 +1288,8 @@ export class FolderRepositoryManager extends Disposable {
items: [],
hasMorePages: false,
hasUnsearchedRepositories: false,
totalCount: 0
totalCount: 0,
paginationProgress: 0
};
}

Expand Down Expand Up @@ -1329,6 +1332,12 @@ export class FolderRepositoryManager extends Disposable {
// If we are in case 1 or 3, don't filter out repos that are out of pages, as we will be querying from the start.
return info && (options.fetchNextPage === false || info.hasMorePages !== false);
});
const hasMorePages = () => githubRepositories.some(repo =>
this._repositoryPageInformation.get(repo.remote.url.toString() + queryId)?.hasMorePages === true
);
const paginationProgress = () => githubRepositoriesWithGitRemotes.reduce((total, repo) =>
total + (this._repositoryPageInformation.get(repo.remote.url.toString() + queryId)?.pullRequestPage ?? 0), 0
);

for (let i = 0; i < githubRepositories.length; i++) {
const githubRepository = githubRepositories[i];
Expand Down Expand Up @@ -1362,16 +1371,17 @@ export class FolderRepositoryManager extends Disposable {
};

if (options.fetchNextPage) {
// Case 2. Fetch a single new page, and increment the global number of pages fetched for this query.
// Case 2. Advance both counters before fetching so failures don't shorten the restore limit.
pageInformation.pullRequestPage++;
addPage(await fetchPage(pageInformation.pullRequestPage));
setTotalFetchedPages(getTotalFetchedPages() + 1);
addPage(await fetchPage(pageInformation.pullRequestPage));
} else {
// Case 1&3. Fetch all the pages we have fetched in the past, or in case 1, just a single page.

if (pageInformation.pullRequestPage === 0) {
// Case 1. Pretend we have previously fetched the first page, then hand off to the case 3 machinery to "fetch all pages we have fetched in the past"
pageInformation.pullRequestPage = 1;
setTotalFetchedPages(getTotalFetchedPages() + 1);
}

const pages = await Promise.all(
Expand All @@ -1393,25 +1403,22 @@ export class FolderRepositoryManager extends Disposable {
const shouldBreakEarly = hasReceivedData && (isFetchingNextPage || hasReachedPreviousFetchLimit) && !hasUserConfiguredRemotes;

if (shouldBreakEarly) {
if (getTotalFetchedPages() === 0) {
// We're in case 1, manually set number of pages we looked through until we found first results.
setTotalFetchedPages(pagesFetched);
}

return {
items: itemData.items,
hasMorePages: pageInformation.hasMorePages,
hasMorePages: hasMorePages(),
hasUnsearchedRepositories: i < githubRepositories.length - 1,
totalCount: itemData.totalCount,
paginationProgress: paginationProgress()
};
}
}

return {
items: itemData.items,
hasMorePages: itemData.hasMorePages,
hasMorePages: hasMorePages(),
hasUnsearchedRepositories: false,
totalCount: itemData.totalCount
totalCount: itemData.totalCount,
paginationProgress: paginationProgress()
};
}

Expand Down
Loading
Loading