Skip to content

Commit

Permalink
Searches by a text query using API
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeibbb committed Nov 6, 2024
1 parent 9673236 commit b3cd23a
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 35 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p

## [Unreleased]

### Added

- Adds new ability to search for a GitHub PR in the _Launchpad;_ closes [#3543](https://github.com/gitkraken/vscode-gitlens/issues/3543)

## [15.6.2] - 2024-10-17

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion docs/telemetry-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,7 @@ void
```typescript
{
'timeout': number,
'operation': 'getPullRequest' | 'getMyPullRequests' | 'getCodeSuggestions' | 'getEnrichedItems' | 'getCodeSuggestionCounts',
'operation': 'getPullRequest' | 'searchPullRequests' | 'getMyPullRequests' | 'getCodeSuggestions' | 'getEnrichedItems' | 'getCodeSuggestionCounts',
'duration': number
}
```
Expand Down
1 change: 1 addition & 0 deletions src/constants.telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ export type TelemetryEvents = {
timeout: number;
operation:
| 'getPullRequest'
| 'searchPullRequests'
| 'getMyPullRequests'
| 'getCodeSuggestions'
| 'getEnrichedItems'
Expand Down
39 changes: 26 additions & 13 deletions src/plus/launchpad/launchpad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,22 @@ export class LaunchpadCommand extends QuickCommand<State> {
};
}

const combineQuickpickItemsWithSearchResults = <T extends { item: { id: string } } | object>(
arr: readonly T[],
items: T[],
) => {
const ids: Set<string> = new Set(
arr.map(i => 'item' in i && i.item?.id).filter(id => typeof id === 'string'),
);
const result = [...arr];
for (const item of items) {
if ('item' in item && item.item?.id && !ids.has(item.item.id)) {
result.push(item);
}
}
return result;
};

const updateItems = async (
quickpick: QuickPick<LaunchpadItemQuickPickItem | DirectiveQuickPickItem | ConnectMoreIntegrationsItem>,
) => {
Expand All @@ -536,7 +552,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
}
const { items, placeholder } = getItemsAndPlaceholder(Boolean(search));
quickpick.placeholder = placeholder;
quickpick.items = items;
quickpick.items = search ? combineQuickpickItemsWithSearchResults(quickpick.items, items) : items;
});
} finally {
quickpick.busy = false;
Expand Down Expand Up @@ -577,11 +593,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
quickpick.items =
updated && quickpick.items === consideredItems ? [...consideredItems] : consideredItems;

const activeLaunchpadItems = quickpick.activeItems.filter(
(i): i is LaunchpadItemQuickPickItem => 'item' in i,
);

if (!value?.length || activeLaunchpadItems.length) {
if (!value?.length) {
// Nothing to search
this.updateItemsDebouncer.cancel();
return true;
Expand All @@ -593,7 +605,9 @@ export class LaunchpadCommand extends QuickCommand<State> {
// Then when we iterate local items we can check them to corresponding identitie according to the item's repo type.
// Same with API: we iterate connected integrations and search in each of them with the corresponding identity.
const prUrlIdentity = getPullRequestIdentityValuesFromSearch(value);

if (prUrlIdentity.prNumber != null) {
// We can identify the PR number, so let's try to find it locally:
const launchpadItems = quickpick.items.filter((i): i is LaunchpadItemQuickPickItem => 'item' in i);
let item = launchpadItems.find(i =>
// perform strict match first
Expand All @@ -608,16 +622,15 @@ export class LaunchpadCommand extends QuickCommand<State> {
item.alwaysShow = true;
// Force quickpick to update by changing the items object:
quickpick.items = [...quickpick.items];
// We have found an item that matches to the URL.
// Now it will be displayed as the found item and we exit this function now without sending any requests to API:
this.updateItemsDebouncer.cancel();
return true;
}
// Nothing is found above, so let's perform search in the API:
await updateItems(quickpick);
// We have found an item that matches to the URL.
// Now it will be displayed as the found item and we exit this function now without sending any requests to API:
this.updateItemsDebouncer.cancel();
return true;
}
}
this.updateItemsDebouncer.cancel();

await updateItems(quickpick);
return true;
},
onDidClickButton: async (quickpick, button) => {
Expand Down
52 changes: 31 additions & 21 deletions src/plus/launchpad/launchpadProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,33 +320,42 @@ export class LaunchpadProvider implements Disposable {
return { prs: prs, suggestionCounts: suggestionCounts };
}

private async getSearchedPullRequests(search: string) {
private async getSearchedPullRequests(search: string, cancellation?: CancellationToken) {
// TODO: This needs to be generalized to work outside of GitHub,
// The current idea is that we should iterate the connected integrations and apply their parsing.
// Probably we even want to build a map like this: { integrationId: identity }
// Then we iterate connected integrations and search in each of them with the corresponding identity.
const { ownerAndRepo, prNumber } = getPullRequestIdentityValuesFromSearch(search);
let result: TimedResult<SearchedPullRequest[] | undefined> | undefined;

if (prNumber != null) {
if (ownerAndRepo != null) {
// TODO: This needs to be generalized to work outside of GitHub
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
const [owner, repo] = ownerAndRepo.split('/', 2);
const descriptor: GitHubRepositoryDescriptor = {
key: ownerAndRepo,
owner: owner,
name: repo,
};
const pr = await withDurationAndSlowEventOnTimeout(
integration?.getPullRequest(descriptor, prNumber),
'getPullRequest',
this.container,
);
if (pr?.value != null) {
result = { value: [{ pullRequest: pr.value, reasons: [] }], duration: pr.duration };
return { prs: result, suggestionCounts: undefined };
}
if (prNumber != null && ownerAndRepo != null) {
// TODO: This needs to be generalized to work outside of GitHub
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
const [owner, repo] = ownerAndRepo.split('/', 2);
const descriptor: GitHubRepositoryDescriptor = {
key: ownerAndRepo,
owner: owner,
name: repo,
};
const pr = await withDurationAndSlowEventOnTimeout(
integration?.getPullRequest(descriptor, prNumber),
'getPullRequest',
this.container,
);
if (pr?.value != null) {
result = { value: [{ pullRequest: pr.value, reasons: [] }], duration: pr.duration };
return { prs: result, suggestionCounts: undefined };
}
} else {
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
const prs = await withDurationAndSlowEventOnTimeout(
integration?.searchPullRequests(search, undefined, cancellation),
'searchPullRequests',
this.container,
);
if (prs != null) {
result = { value: prs.value?.map(pr => ({ pullRequest: pr, reasons: [] })), duration: prs.duration };
return { prs: result, suggestionCounts: undefined };
}
}
return { prs: undefined, suggestionCounts: undefined };
Expand Down Expand Up @@ -679,7 +688,7 @@ export class LaunchpadProvider implements Disposable {
this.container.git.isDiscoveringRepositories,
this.getEnrichedItems({ force: options?.force, cancellation: cancellation }),
options?.search
? this.getSearchedPullRequests(options.search)
? this.getSearchedPullRequests(options.search, cancellation)
: this.getPullRequestsWithSuggestionCounts({ force: options?.force, cancellation: cancellation }),
]);

Expand Down Expand Up @@ -1106,6 +1115,7 @@ function withDurationAndSlowEventOnTimeout<T>(
promise: Promise<T>,
name:
| 'getPullRequest'
| 'searchPullRequests'
| 'getMyPullRequests'
| 'getCodeSuggestionCounts'
| 'getCodeSuggestions'
Expand Down

0 comments on commit b3cd23a

Please sign in to comment.