Skip to content

Commit 71b0f67

Browse files
committed
refactor: add search tag for metadata value
1 parent faa2f4f commit 71b0f67

File tree

4 files changed

+58
-29
lines changed

4 files changed

+58
-29
lines changed

src/components/Docs/ContentList.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,13 @@ export function ContentList() {
247247
Matches classes, fields, or enum members that have a metadata key. Example:{" "}
248248
<SearchExample query="metadata:MNetworkEnable" />
249249
</dd>
250+
<dt>
251+
<code>metadatavalue:</code> — find by metadata value
252+
</dt>
253+
<dd>
254+
Matches classes, fields, or enum members by their metadata value. Example:{" "}
255+
<SearchExample query="metadatavalue:water" />
256+
</dd>
250257
</dl>
251258
Filters can be combined: <SearchExample query="module:client metadata:MNetworkEnable" />
252259
</SearchFiltersBlock>

src/components/Docs/SchemaClass.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { getGame } from "../../games";
1111
import {
1212
matchesWords,
1313
matchesMetadataKeys,
14+
matchesMetadataValues,
1415
filterItems,
1516
useParsedSearch,
1617
useFieldParam,
@@ -136,10 +137,10 @@ export const SchemaClassView: React.FC<{
136137
const { root, classesByKey } = useContext(DeclarationsContext);
137138
const navigate = useNavigate();
138139
const parsed = useParsedSearch();
139-
const { nameWords: searchWords, offsets: searchOffsets, metadataKeys: searchMetadata } = parsed;
140+
const { nameWords: searchWords, offsets: searchOffsets, metadataKeys: searchMetadata, metadataValues: searchMetadataValues } = parsed;
140141
const fieldParam = useFieldParam();
141142

142-
const isSearching = searchWords.length > 0 || searchOffsets.size > 0 || searchMetadata.length > 0;
143+
const isSearching = searchWords.length > 0 || searchOffsets.size > 0 || searchMetadata.length > 0 || searchMetadataValues.length > 0;
143144

144145
const inheritedGroups = useMemo(() => {
145146
if (isSearching) return [];
@@ -196,6 +197,7 @@ export const SchemaClassView: React.FC<{
196197
</DeclarationHeader>
197198
{(!collapseNonMatching ||
198199
(searchMetadata.length > 0 && matchesMetadataKeys(declaration.metadata, searchMetadata)) ||
200+
(searchMetadataValues.length > 0 && matchesMetadataValues(declaration.metadata, searchMetadataValues)) ||
199201
(searchWords.length > 0 && matchesMetadataKeys(declaration.metadata, searchWords))) && (
200202
<MetadataTags metadata={declaration.metadata} root={root} navigate={navigate} />
201203
)}

src/components/Docs/SchemaEnum.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ModuleBadge, GitHubFileLink } from "./SchemaClass";
1313
import {
1414
matchesWords,
1515
matchesMetadataKeys,
16+
matchesMetadataValues,
1617
filterItems,
1718
useParsedSearch,
1819
useFieldParam,
@@ -76,10 +77,10 @@ export const SchemaEnumView: React.FC<{
7677
const { root } = useContext(DeclarationsContext);
7778
const navigate = useNavigate();
7879
const parsed = useParsedSearch();
79-
const { nameWords: searchWords, metadataKeys: searchMetadata } = parsed;
80+
const { nameWords: searchWords, metadataKeys: searchMetadata, metadataValues: searchMetadataValues } = parsed;
8081
const fieldParam = useFieldParam();
8182

82-
const isSearching = searchWords.length > 0 || searchMetadata.length > 0;
83+
const isSearching = searchWords.length > 0 || searchMetadata.length > 0 || searchMetadataValues.length > 0;
8384
const nameMatches = searchWords.length > 0 && matchesWords(declaration.name, searchWords);
8485
const collapseNonMatching = isSearching && !nameMatches;
8586

@@ -107,6 +108,7 @@ export const SchemaEnumView: React.FC<{
107108
</DeclarationHeader>
108109
{(!collapseNonMatching ||
109110
(searchMetadata.length > 0 && matchesMetadataKeys(declaration.metadata, searchMetadata)) ||
111+
(searchMetadataValues.length > 0 && matchesMetadataValues(declaration.metadata, searchMetadataValues)) ||
110112
(searchWords.length > 0 && matchesMetadataKeys(declaration.metadata, searchWords))) && (
111113
<MetadataTags metadata={declaration.metadata} root={root} navigate={navigate} />
112114
)}

src/components/Docs/utils/filtering.tsx

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ interface ParsedSearch {
88
moduleWords: string[];
99
offsets: Set<number>;
1010
metadataKeys: string[];
11+
metadataValues: string[];
1112
}
1213

1314
const EMPTY_PARSED: ParsedSearch = {
1415
nameWords: [],
1516
moduleWords: [],
1617
offsets: new Set(),
1718
metadataKeys: [],
19+
metadataValues: [],
1820
};
1921

2022
function isFilterPrefix(word: string): boolean {
21-
return word.startsWith("module:") || word.startsWith("offset:") || word.startsWith("metadata:");
23+
return (
24+
word.startsWith("module:") ||
25+
word.startsWith("offset:") ||
26+
word.startsWith("metadata:") ||
27+
word.startsWith("metadatavalue:")
28+
);
2229
}
2330

2431
function parseSearch(search: string): ParsedSearch {
@@ -30,10 +37,14 @@ function parseSearch(search: string): ParsedSearch {
3037
.map((x) => parseOffset(x.slice(7)))
3138
.filter((x): x is number => x !== null);
3239
const metadataKeys = words
33-
.filter((x) => x.startsWith("metadata:"))
40+
.filter((x) => x.startsWith("metadata:") && !x.startsWith("metadatavalue:"))
3441
.map((x) => x.slice(9))
3542
.filter(Boolean);
36-
return { nameWords, moduleWords, offsets: new Set(offsetValues), metadataKeys };
43+
const metadataValues = words
44+
.filter((x) => x.startsWith("metadatavalue:"))
45+
.map((x) => x.slice(14))
46+
.filter(Boolean);
47+
return { nameWords, moduleWords, offsets: new Set(offsetValues), metadataKeys, metadataValues };
3748
}
3849

3950
export function useParsedSearch(): ParsedSearch {
@@ -87,12 +98,16 @@ export function matchesMetadataKeys(
8798
keys: string[],
8899
): boolean {
89100
if (!metadata || metadata.length === 0 || keys.length === 0) return false;
90-
return keys.every((key) =>
91-
metadata.some(
92-
(m) =>
93-
m.name.toLowerCase().includes(key) ||
94-
(m.value != null && m.value.toLowerCase().includes(key)),
95-
),
101+
return keys.every((key) => metadata.some((m) => m.name.toLowerCase().includes(key)));
102+
}
103+
104+
export function matchesMetadataValues(
105+
metadata: api.SchemaMetadataEntry[] | undefined,
106+
values: string[],
107+
): boolean {
108+
if (!metadata || metadata.length === 0 || values.length === 0) return false;
109+
return values.every((val) =>
110+
metadata.some((m) => m.value != null && m.value.toLowerCase().includes(val)),
96111
);
97112
}
98113

@@ -108,7 +123,7 @@ export function filterItems<T extends HasNameAndMetadata>(
108123
collapseNonMatching: boolean,
109124
extraMatch?: (item: T) => boolean,
110125
): { visible: T[]; highlighted: Set<T>; hiddenCount: number } {
111-
const { nameWords, metadataKeys } = parsed;
126+
const { nameWords, metadataKeys, metadataValues } = parsed;
112127
// Words not already matched by the declaration name must match at the field level
113128
const declLower = declarationName.toLowerCase();
114129
const remainingWords = nameWords.filter((w) => !declLower.includes(w));
@@ -119,12 +134,16 @@ export function filterItems<T extends HasNameAndMetadata>(
119134
matchesWords(item.name, remainingWords) ||
120135
matchesMetadataKeys(item.metadata, remainingWords)) &&
121136
(metadataKeys.length === 0 || matchesMetadataKeys(item.metadata, metadataKeys)) &&
137+
(metadataValues.length === 0 || matchesMetadataValues(item.metadata, metadataValues)) &&
122138
(extraMatch == null || extraMatch(item))
123139
);
124140
}
125141

126142
const hasFieldFilter =
127-
remainingWords.length > 0 || metadataKeys.length > 0 || parsed.offsets.size > 0;
143+
remainingWords.length > 0 ||
144+
metadataKeys.length > 0 ||
145+
metadataValues.length > 0 ||
146+
parsed.offsets.size > 0;
128147

129148
if (!collapseNonMatching) {
130149
return {
@@ -142,7 +161,7 @@ export function filterItems<T extends HasNameAndMetadata>(
142161
}
143162

144163
function doSearch(declarations: api.Declaration[], parsed: ParsedSearch): api.Declaration[] {
145-
const { nameWords, moduleWords, offsets: offsetSet, metadataKeys } = parsed;
164+
const { nameWords, moduleWords, offsets: offsetSet, metadataKeys, metadataValues } = parsed;
146165

147166
function filterModule(declaration: api.Declaration): boolean {
148167
if (moduleWords.length === 0) return true;
@@ -158,12 +177,7 @@ function doSearch(declarations: api.Declaration[], parsed: ParsedSearch): api.De
158177
): boolean {
159178
return (
160179
name.includes(word) ||
161-
(metadata?.some(
162-
(m) =>
163-
m.name.toLowerCase().includes(word) ||
164-
(m.value != null && m.value.toLowerCase().includes(word)),
165-
) ??
166-
false)
180+
(metadata?.some((m) => m.name.toLowerCase().includes(word)) ?? false)
167181
);
168182
}
169183

@@ -180,12 +194,16 @@ function doSearch(declarations: api.Declaration[], parsed: ParsedSearch): api.De
180194
}
181195

182196
function matchesMetadata(declaration: api.Declaration): boolean {
183-
if (matchesMetadataKeys(declaration.metadata, metadataKeys)) return true;
184-
if (declaration.kind === "class")
185-
return declaration.fields.some((f) => matchesMetadataKeys(f.metadata, metadataKeys));
186-
if (declaration.kind === "enum")
187-
return declaration.members.some((m) => matchesMetadataKeys(m.metadata, metadataKeys));
188-
return false;
197+
const allMetadata: (api.SchemaMetadataEntry[] | undefined)[] = [declaration.metadata];
198+
if (declaration.kind === "class") declaration.fields.forEach((f) => allMetadata.push(f.metadata));
199+
else if (declaration.kind === "enum") declaration.members.forEach((m) => allMetadata.push(m.metadata));
200+
201+
const keysMatch =
202+
metadataKeys.length === 0 || allMetadata.some((md) => matchesMetadataKeys(md, metadataKeys));
203+
const valuesMatch =
204+
metadataValues.length === 0 ||
205+
allMetadata.some((md) => matchesMetadataValues(md, metadataValues));
206+
return keysMatch && valuesMatch;
189207
}
190208

191209
function matchesOffset(declaration: api.Declaration): boolean {
@@ -195,7 +213,7 @@ function doSearch(declarations: api.Declaration[], parsed: ParsedSearch): api.De
195213

196214
const hasNameFilter = nameWords.length > 0;
197215
const hasOffsetFilter = offsetSet.size > 0;
198-
const hasMetadataFilter = metadataKeys.length > 0;
216+
const hasMetadataFilter = metadataKeys.length > 0 || metadataValues.length > 0;
199217

200218
return declarations.filter((declaration) => {
201219
if (!filterModule(declaration)) return false;

0 commit comments

Comments
 (0)