Skip to content

Commit f6e8b13

Browse files
authored
Introduce filter * and not (#1652)
1 parent d3918c0 commit f6e8b13

File tree

6 files changed

+136
-11
lines changed

6 files changed

+136
-11
lines changed

base/src/main/resources/web/lib/components/extensions-picker/extension-tags.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { TagEntry } from './extensions-picker';
3-
import { Dropdown, Overlay } from 'react-bootstrap';
3+
import { Dropdown } from 'react-bootstrap';
44
import { HoverControlledDropdown } from '../../core/components';
55

66
export function ExtensionTags(props: { name?: string; tagsDef: TagEntry[]; hover: boolean }) {
@@ -38,7 +38,6 @@ export function ExtensionTags(props: { name?: string; tagsDef: TagEntry[]; hover
3838
<HoverControlledDropdown
3939
className="extension-tag-dropdown"
4040
placement="right"
41-
overlay={Overlay}
4241
delay={{ show: 200, hide: 0 }}
4342
>
4443
<Dropdown.Toggle as="div"

base/src/main/resources/web/lib/components/extensions-picker/extensions-origin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ export function PlatformIcon(props: SVGProps<SVGSVGElement>){
2727
}
2828

2929
export function ExtensionsOrigin(props: { platform: boolean }) {
30-
const Overlay = (
30+
const tooltip = (
3131
<Tooltip id="extension-origin-platform-tooltip" style={{ zIndex: 200 }}>
3232
The quark icon indicates the extension is part of the selected Quarkus Platform. Extensions in a platform are tested and verified together and thus safer to use and easier to upgrade.
3333
</Tooltip>
3434
);
3535
return (
3636
props.platform ? <OverlayTrigger
3737
placement="bottom"
38-
overlay={Overlay}
38+
overlay={tooltip}
3939
delay={{ show: 200, hide: 0 }}
4040
>
4141
<span className="extension-origin-platform">

base/src/main/resources/web/lib/components/extensions-picker/extensions-utils.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const FIELD_KEYS = FIELD_IDENTIFIERS.map(s => s.keys).reduce((acc, value) => acc
3535

3636
const getInPattern = keys => `(?<expr>([a-zA-Z0-9-._]+\\s+)*[a-zA-Z0-9-._]+)\\sin\\s(?<fields>((${keys.join('|')}),?)+)`;
3737
const getInRegexp = keys => new RegExp(getInPattern(keys), 'gi');
38-
const getEqualsPattern = keys => `(?<field>${keys.join('|')}):(?<expr>([a-zA-Z0-9-._,]+|("([a-zA-Z0-9-._,:]+\\s*)+")))`;
38+
const getEqualsPattern = keys => `(-|!)?(?<field>${keys.join('|')}):(?<expr>\\*|(([a-zA-Z0-9-._,]+|"([a-zA-Z0-9-._,:]+\\s*)+"),?)+)`;
3939
const getEqualsRegexp = keys => new RegExp(getEqualsPattern(keys), 'gi');
4040
const ORIGIN_PATTERN = '\\s*origin:(?<origin>platform|other)\\s*'
4141
const ORIGIN_REGEX = new RegExp(ORIGIN_PATTERN, 'gi');
@@ -136,6 +136,9 @@ function equalsFilter(e: ExtensionValues, expr: string[], field: string) {
136136
const val = e.values.get(field);
137137
if (val) {
138138
for (const e of expr) {
139+
if (e === '*' && val) {
140+
return true;
141+
}
139142
if (typeof val === 'string') {
140143
if (val === e) {
141144
return true;
@@ -153,6 +156,30 @@ function defaultFiltering(filtered: ExtensionValues[], formattedSearch: string)
153156
}
154157

155158

159+
function splitOnCommaOutsideQuotes(input: string): string[] {
160+
const parts: string[] = [];
161+
let current = '';
162+
let insideQuotes = false;
163+
164+
for (const char of input) {
165+
if (char === '"') {
166+
insideQuotes = !insideQuotes; // toggle state
167+
// don’t add quote char to current
168+
} else if (char === ',' && !insideQuotes) {
169+
parts.push(current.trim());
170+
current = '';
171+
} else {
172+
current += char;
173+
}
174+
}
175+
176+
if (current.length) {
177+
parts.push(current.trim());
178+
}
179+
180+
return parts;
181+
}
182+
156183
export function search(search: string, processedExtensions: ProcessedExtensions): Extension[] {
157184
let formattedSearch = search.trim().toLowerCase();
158185
if (!formattedSearch) {
@@ -177,10 +204,13 @@ export function search(search: string, processedExtensions: ProcessedExtensions)
177204
if (!e.groups?.expr || !e.groups?.field) {
178205
continue;
179206
}
180-
181-
const expr = e.groups.expr.replace(/"/g, '').split(',').map(s => s.toLowerCase().trim());
207+
const not = e[1] === '-' || e[1] === '!' ;
208+
const expr = splitOnCommaOutsideQuotes(e.groups.expr).map(s => s.toLowerCase().trim());
182209
const field = e.groups.field.trim().toLowerCase();
183-
filtered = filtered.filter(e => equalsFilter(e, expr, field));
210+
filtered = filtered.filter(item => {
211+
const match = equalsFilter(item, expr, field);
212+
return not ? !match : match;
213+
});
184214
}
185215

186216
formattedSearch = formattedSearch.replace(equalsRegex, ';').replace(ORIGIN_REGEX, ';').trim();

base/src/main/resources/web/lib/components/info-picker/no-code-select.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ export const NoCodeSelect = (props: InputProps<boolean>) => {
99
const adaptedOnChange = (e: any) => {
1010
onChangeWithDirty(!e.currentTarget.checked);
1111
};
12-
const Overlay = (
12+
const tooltip = (
1313
<Tooltip id="no-code-select" style={{ zIndex: 200 }}>
1414
Decide whether to include the starter code provided by extensions or get an empty Quarkus project.
1515
</Tooltip>
1616
);
1717
return (
1818
<OverlayTrigger
1919
placement="right"
20-
overlay={Overlay}
20+
overlay={tooltip}
2121
delay={{ show: 200, hide: 0 }}
2222
>
2323
<div className="form-group code-switch">

base/src/main/resources/web/lib/core/components/copy-to-clipboard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function CopyToClipboard(props: CopyToClipboardProps) {
6363
}
6464

6565
return (
66-
<OverlayTrigger trigger="hover" placement={props.tooltipPlacement} overlay={tooltip} delay={{ show: 0, hide: 0 }}>
66+
<OverlayTrigger placement={props.tooltipPlacement} overlay={tooltip} delay={{ show: 0, hide: 0 }}>
6767
<div
6868
onClick={copyToClipboard}
6969
className={classNames('copy-to-clipboard', props.className)}

extension-search-doc.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Extension Search Field
2+
3+
The search field supports both simple keyword searches and advanced filtering.
4+
5+
## 1. Simple Search
6+
Type any word to search in:
7+
- **Name**
8+
- **Short name**
9+
- **Keywords**
10+
- **Category**
11+
- **Artifact ID**
12+
13+
**Example:**
14+
```
15+
hibernate
16+
```
17+
Matches any extension whose name, short name, keywords, category, or artifact ID contains `hibernate`.
18+
19+
## 2. Field Search (`field:expression`)
20+
Search in a specific field using the `field:value` format. If a field has aliases, you can use any of them.
21+
22+
### Available fields and aliases
23+
| Field | Aliases | Example |
24+
|--------------|----------------------|--------------------------------|
25+
| `name` || `name:hibernate` |
26+
| `description`| `desc` | `desc:cache` |
27+
| `group-id` | `groupid`, `group` | `group:io.quarkus` |
28+
| `artifact-id`| `artifactid`, `artifact` | `artifact:hibernate-orm` |
29+
| `short-name` | `shortname` | `shortname:jaxrs` |
30+
| `keywords` | `keyword` | `keyword:database` |
31+
| `tags` | `tag` | `tag:stable` |
32+
| `category` | `cat` | `category:cloud` |
33+
34+
Multiple values can be separated by commas:
35+
```
36+
keyword:database,sql
37+
```
38+
Quotes can be used for multi-word expressions:
39+
```
40+
desc:"reactive sql"
41+
```
42+
43+
## 3. "In" Search (`expression in field1,field2`)
44+
Search for words in specific fields.
45+
46+
**Example:**
47+
```
48+
sql in name,description
49+
```
50+
Finds extensions where `sql` appears in **name** or **description**.
51+
52+
Multiple words must all appear in the specified fields:
53+
```
54+
reactive sql in description
55+
```
56+
57+
## 4. Origin Filter
58+
Filter by extension origin:
59+
- `origin:platform` → Only platform extensions
60+
- `origin:other` → Only non-platform extensions
61+
62+
**Example:**
63+
```
64+
hibernate origin:platform
65+
```
66+
67+
## 5. Tags and Categories
68+
Tags and categories can be searched like normal fields.
69+
70+
**Example:**
71+
```
72+
category:messaging
73+
tag:stable
74+
```
75+
76+
## 6. Combining Filters
77+
You can combine different filter types in one query.
78+
79+
**Example:**
80+
```
81+
reactive in name,description category:messaging origin:platform
82+
```
83+
- Looks for "reactive" in name or description
84+
- Filters to the `messaging` category
85+
- Shows only platform extensions
86+
87+
## 7. Short Name Match Priority
88+
If your query exactly matches a short name, that extension appears at the top of results.
89+
90+
**Example:**
91+
```
92+
jaxrs
93+
```
94+
95+
## 8. Empty Search
96+
If the field is empty, all extensions are shown.

0 commit comments

Comments
 (0)