Skip to content

Commit c329894

Browse files
committed
Adds interactive search filter pickers
- Adds search completion items that open pickers for authors, references, and files/folders
1 parent d7bd82b commit c329894

File tree

6 files changed

+362
-79
lines changed

6 files changed

+362
-79
lines changed

src/git/search.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,15 @@ export function parseSearchQueryGitHubCommand(
557557

558558
return { args: queryArgs, filters: filters, operations: operations };
559559
}
560+
561+
export function rebuildSearchQueryFromParsed(parsed: ParsedSearchQuery): string {
562+
const parts: string[] = [];
563+
564+
for (const [operator, values] of parsed.operations) {
565+
for (const value of values) {
566+
parts.push(`${operator}${value}`);
567+
}
568+
}
569+
570+
return parts.join(' ');
571+
}

src/webviews/apps/shared/components/autocomplete/autocomplete.ts

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export class GlAutocomplete extends LitElement {
188188
open = false;
189189

190190
@state()
191-
private _selectedIndex = 0;
191+
private _selectedIndex = -1;
192192

193193
/**
194194
* Gets the currently selected index (readonly from outside)
@@ -200,9 +200,11 @@ export class GlAutocomplete extends LitElement {
200200
override updated(changedProperties: Map<string | number | symbol, unknown>) {
201201
super.updated(changedProperties);
202202

203-
// Reset selection when items change
203+
// Clamp selection to valid range when items change
204204
if (changedProperties.has('items')) {
205-
this._selectedIndex = 0;
205+
if (this._selectedIndex >= this.items.length) {
206+
this._selectedIndex = this.items.length > 0 ? this.items.length - 1 : -1;
207+
}
206208
}
207209

208210
if (changedProperties.has('_selectedIndex') && this._selectedIndex >= 0) {
@@ -242,38 +244,33 @@ export class GlAutocomplete extends LitElement {
242244
return Math.floor(availableHeight / itemHeight);
243245
}
244246

245-
/**
246-
* Resets selection to first item
247-
*/
247+
/** Resets selection to no selection */
248248
public resetSelection(): void {
249-
this._selectedIndex = 0;
249+
this._selectedIndex = -1;
250250
}
251251

252-
/**
253-
* Moves selection up by one item
254-
*/
252+
/** Sets selection to a specific index */
253+
public setSelection(index: number): void {
254+
this._selectedIndex = Math.max(-1, Math.min(this.items.length - 1, index));
255+
}
256+
257+
/** Moves selection up by one item */
255258
public selectPrevious(): void {
256-
this._selectedIndex = Math.max(0, this._selectedIndex - 1);
259+
this._selectedIndex = Math.max(-1, this._selectedIndex - 1);
257260
}
258261

259-
/**
260-
* Moves selection down by one item
261-
*/
262+
/** Moves selection down by one item */
262263
public selectNext(): void {
263264
this._selectedIndex = Math.min(this.items.length - 1, this._selectedIndex + 1);
264265
}
265266

266-
/**
267-
* Jumps selection up by a page (visible items)
268-
*/
267+
/** Jumps selection up by a page (visible items) */
269268
public pageUp(): void {
270269
const pageSize = this.getVisibleItemCount();
271270
this._selectedIndex = Math.max(0, this._selectedIndex - pageSize);
272271
}
273272

274-
/**
275-
* Jumps selection down by a page (visible items)
276-
*/
273+
/** Jumps selection down by a page (visible items) */
277274
public pageDown(): void {
278275
const pageSize = this.getVisibleItemCount();
279276
this._selectedIndex = Math.min(this.items.length - 1, this._selectedIndex + pageSize);
@@ -331,7 +328,7 @@ export class GlAutocomplete extends LitElement {
331328

332329
if (scrollable && selectedEl) {
333330
// If the first item is selected, scroll to the very top to ensure description is visible
334-
if (this.selectedIndex === 0) {
331+
if (this.selectedIndex <= 0) {
335332
scrollable.scrollTop = 0;
336333
return;
337334
}

src/webviews/apps/shared/components/search/models.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ export type SearchCompletionItem = CompletionItem<
77
SearchCompletionOperator | SearchCompletionCommand | SearchCompletionValue
88
>;
99

10-
export interface SearchCompletionCommand {
11-
command: 'toggle-natural-language-mode';
12-
}
10+
export type SearchCompletionCommand =
11+
| { command: 'toggle-natural-language-mode' }
12+
| { command: 'pick-author' | 'pick-file'; multi?: boolean }
13+
| { command: 'pick-folder' | 'pick-ref'; multi?: never };
1314

1415
export interface SearchCompletionValue {
1516
/** The operator this value belongs to */
@@ -28,8 +29,10 @@ export const naturalLanguageSearchAutocompleteCommand: CompletionItem<SearchComp
2829
};
2930

3031
export interface SearchCompletionOperatorValue {
31-
/** The value to suggest */
32-
value: string;
32+
/** The value to suggest or command to execute when this value is selected */
33+
value: string | SearchCompletionCommand;
34+
/** Label to display in autocomplete */
35+
label: string;
3336
/** Description of what this value does (shown in autocomplete list) */
3437
description: string;
3538
/** Icon to display in autocomplete */
@@ -47,7 +50,7 @@ export interface SearchCompletionOperator {
4750
icon?: string;
4851
/** Example usage */
4952
example?: TemplateResult;
50-
/** Predefined values to suggest for this operator */
53+
/** Predefined values to suggest for this operator (can include commands that help populate values) */
5154
values?: SearchCompletionOperatorValue[];
5255
}
5356

@@ -70,6 +73,14 @@ export const searchCompletionOperators: SearchCompletionOperator[] = [
7073
aliases: ['@:'],
7174
example: html`Use a name or email, e.g. <code>author:eamodio</code>, <code>@:john</code>, or
7275
<code>@me</code> for your own commits`,
76+
values: [
77+
{
78+
value: { command: 'pick-author', multi: true },
79+
label: 'Choose authors\u2026',
80+
description: 'Select one or more contributors to filter by',
81+
icon: 'person',
82+
},
83+
],
7384
},
7485
{
7586
operator: 'commit:',
@@ -85,6 +96,14 @@ export const searchCompletionOperators: SearchCompletionOperator[] = [
8596
aliases: ['^:'],
8697
example: html`Use a reference to filter, e.g. <code>ref:main</code> or <code>^:v1.0.0</code>, or a range to
8798
compare, e.g. <code>ref:main..feature</code> (commits in feature but not in main)`,
99+
values: [
100+
{
101+
value: { command: 'pick-ref' },
102+
label: 'Choose a branch, tag, or range\u2026',
103+
description: 'Select a branch, tag, or range to filter by',
104+
icon: 'git-branch',
105+
},
106+
],
88107
},
89108
{
90109
operator: 'type:',
@@ -95,11 +114,13 @@ export const searchCompletionOperators: SearchCompletionOperator[] = [
95114
values: [
96115
{
97116
value: 'stash',
117+
label: 'stash',
98118
description: 'Filter commits to only show stashes',
99119
icon: 'archive',
100120
},
101121
{
102122
value: 'tip',
123+
label: 'tip',
103124
description: 'Filter commits to only show commits pointed to by branches or tags',
104125
icon: 'git-branch',
105126
},
@@ -112,6 +133,20 @@ export const searchCompletionOperators: SearchCompletionOperator[] = [
112133
aliases: ['?:'],
113134
example: html`Use a path or filename, e.g. <code>file:package.json</code>, or a glob, e.g.
114135
<code>?:src/**/*.ts</code>`,
136+
values: [
137+
{
138+
value: { command: 'pick-file', multi: true },
139+
label: 'Choose files\u2026',
140+
description: 'Select one or more files to filter by',
141+
icon: 'file',
142+
},
143+
{
144+
value: { command: 'pick-folder' },
145+
label: 'Choose a folder\u2026',
146+
description: 'Select a folder to filter by',
147+
icon: 'folder',
148+
},
149+
],
115150
},
116151
{
117152
operator: 'change:',

0 commit comments

Comments
 (0)