Skip to content

Commit b3cd23a

Browse files
committed
Searches by a text query using API
(#3543, #3684)
1 parent 9673236 commit b3cd23a

File tree

5 files changed

+63
-35
lines changed

5 files changed

+63
-35
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Adds new ability to search for a GitHub PR in the _Launchpad;_ closes [#3543](https://github.com/gitkraken/vscode-gitlens/issues/3543)
12+
913
## [15.6.2] - 2024-10-17
1014

1115
### Fixed

docs/telemetry-events.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ void
13051305
```typescript
13061306
{
13071307
'timeout': number,
1308-
'operation': 'getPullRequest' | 'getMyPullRequests' | 'getCodeSuggestions' | 'getEnrichedItems' | 'getCodeSuggestionCounts',
1308+
'operation': 'getPullRequest' | 'searchPullRequests' | 'getMyPullRequests' | 'getCodeSuggestions' | 'getEnrichedItems' | 'getCodeSuggestionCounts',
13091309
'duration': number
13101310
}
13111311
```

src/constants.telemetry.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ export type TelemetryEvents = {
296296
timeout: number;
297297
operation:
298298
| 'getPullRequest'
299+
| 'searchPullRequests'
299300
| 'getMyPullRequests'
300301
| 'getCodeSuggestions'
301302
| 'getEnrichedItems'

src/plus/launchpad/launchpad.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,22 @@ export class LaunchpadCommand extends QuickCommand<State> {
518518
};
519519
}
520520

521+
const combineQuickpickItemsWithSearchResults = <T extends { item: { id: string } } | object>(
522+
arr: readonly T[],
523+
items: T[],
524+
) => {
525+
const ids: Set<string> = new Set(
526+
arr.map(i => 'item' in i && i.item?.id).filter(id => typeof id === 'string'),
527+
);
528+
const result = [...arr];
529+
for (const item of items) {
530+
if ('item' in item && item.item?.id && !ids.has(item.item.id)) {
531+
result.push(item);
532+
}
533+
}
534+
return result;
535+
};
536+
521537
const updateItems = async (
522538
quickpick: QuickPick<LaunchpadItemQuickPickItem | DirectiveQuickPickItem | ConnectMoreIntegrationsItem>,
523539
) => {
@@ -536,7 +552,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
536552
}
537553
const { items, placeholder } = getItemsAndPlaceholder(Boolean(search));
538554
quickpick.placeholder = placeholder;
539-
quickpick.items = items;
555+
quickpick.items = search ? combineQuickpickItemsWithSearchResults(quickpick.items, items) : items;
540556
});
541557
} finally {
542558
quickpick.busy = false;
@@ -577,11 +593,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
577593
quickpick.items =
578594
updated && quickpick.items === consideredItems ? [...consideredItems] : consideredItems;
579595

580-
const activeLaunchpadItems = quickpick.activeItems.filter(
581-
(i): i is LaunchpadItemQuickPickItem => 'item' in i,
582-
);
583-
584-
if (!value?.length || activeLaunchpadItems.length) {
596+
if (!value?.length) {
585597
// Nothing to search
586598
this.updateItemsDebouncer.cancel();
587599
return true;
@@ -593,7 +605,9 @@ export class LaunchpadCommand extends QuickCommand<State> {
593605
// Then when we iterate local items we can check them to corresponding identitie according to the item's repo type.
594606
// Same with API: we iterate connected integrations and search in each of them with the corresponding identity.
595607
const prUrlIdentity = getPullRequestIdentityValuesFromSearch(value);
608+
596609
if (prUrlIdentity.prNumber != null) {
610+
// We can identify the PR number, so let's try to find it locally:
597611
const launchpadItems = quickpick.items.filter((i): i is LaunchpadItemQuickPickItem => 'item' in i);
598612
let item = launchpadItems.find(i =>
599613
// perform strict match first
@@ -608,16 +622,15 @@ export class LaunchpadCommand extends QuickCommand<State> {
608622
item.alwaysShow = true;
609623
// Force quickpick to update by changing the items object:
610624
quickpick.items = [...quickpick.items];
611-
// We have found an item that matches to the URL.
612-
// Now it will be displayed as the found item and we exit this function now without sending any requests to API:
613-
this.updateItemsDebouncer.cancel();
614-
return true;
615625
}
616-
// Nothing is found above, so let's perform search in the API:
617-
await updateItems(quickpick);
626+
// We have found an item that matches to the URL.
627+
// Now it will be displayed as the found item and we exit this function now without sending any requests to API:
628+
this.updateItemsDebouncer.cancel();
629+
return true;
618630
}
619631
}
620-
this.updateItemsDebouncer.cancel();
632+
633+
await updateItems(quickpick);
621634
return true;
622635
},
623636
onDidClickButton: async (quickpick, button) => {

src/plus/launchpad/launchpadProvider.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -320,33 +320,42 @@ export class LaunchpadProvider implements Disposable {
320320
return { prs: prs, suggestionCounts: suggestionCounts };
321321
}
322322

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

331-
if (prNumber != null) {
332-
if (ownerAndRepo != null) {
333-
// TODO: This needs to be generalized to work outside of GitHub
334-
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
335-
const [owner, repo] = ownerAndRepo.split('/', 2);
336-
const descriptor: GitHubRepositoryDescriptor = {
337-
key: ownerAndRepo,
338-
owner: owner,
339-
name: repo,
340-
};
341-
const pr = await withDurationAndSlowEventOnTimeout(
342-
integration?.getPullRequest(descriptor, prNumber),
343-
'getPullRequest',
344-
this.container,
345-
);
346-
if (pr?.value != null) {
347-
result = { value: [{ pullRequest: pr.value, reasons: [] }], duration: pr.duration };
348-
return { prs: result, suggestionCounts: undefined };
349-
}
331+
if (prNumber != null && ownerAndRepo != null) {
332+
// TODO: This needs to be generalized to work outside of GitHub
333+
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
334+
const [owner, repo] = ownerAndRepo.split('/', 2);
335+
const descriptor: GitHubRepositoryDescriptor = {
336+
key: ownerAndRepo,
337+
owner: owner,
338+
name: repo,
339+
};
340+
const pr = await withDurationAndSlowEventOnTimeout(
341+
integration?.getPullRequest(descriptor, prNumber),
342+
'getPullRequest',
343+
this.container,
344+
);
345+
if (pr?.value != null) {
346+
result = { value: [{ pullRequest: pr.value, reasons: [] }], duration: pr.duration };
347+
return { prs: result, suggestionCounts: undefined };
348+
}
349+
} else {
350+
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
351+
const prs = await withDurationAndSlowEventOnTimeout(
352+
integration?.searchPullRequests(search, undefined, cancellation),
353+
'searchPullRequests',
354+
this.container,
355+
);
356+
if (prs != null) {
357+
result = { value: prs.value?.map(pr => ({ pullRequest: pr, reasons: [] })), duration: prs.duration };
358+
return { prs: result, suggestionCounts: undefined };
350359
}
351360
}
352361
return { prs: undefined, suggestionCounts: undefined };
@@ -679,7 +688,7 @@ export class LaunchpadProvider implements Disposable {
679688
this.container.git.isDiscoveringRepositories,
680689
this.getEnrichedItems({ force: options?.force, cancellation: cancellation }),
681690
options?.search
682-
? this.getSearchedPullRequests(options.search)
691+
? this.getSearchedPullRequests(options.search, cancellation)
683692
: this.getPullRequestsWithSuggestionCounts({ force: options?.force, cancellation: cancellation }),
684693
]);
685694

@@ -1106,6 +1115,7 @@ function withDurationAndSlowEventOnTimeout<T>(
11061115
promise: Promise<T>,
11071116
name:
11081117
| 'getPullRequest'
1118+
| 'searchPullRequests'
11091119
| 'getMyPullRequests'
11101120
| 'getCodeSuggestionCounts'
11111121
| 'getCodeSuggestions'

0 commit comments

Comments
 (0)